From 83bef42194d5e34885e0fe9063824769461b5548 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Wed, 15 Oct 2025 09:22:17 +0900 Subject: [PATCH 01/53] sprint mission2 push --- .gitignore | 1 + .prettierrc | 6 + ArticleService.js | 108 +++++++++++++++++ ProductService.js | 126 ++++++++++++++++++++ README.md | 67 +++++++++++ main.js | 110 +++++++++++++++++ package-lock.json | 295 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 17 +++ 8 files changed, 730 insertions(+) create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 ArticleService.js create mode 100644 ProductService.js create mode 100644 README.md create mode 100644 main.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..017c1beb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "printWidth": 120 +} diff --git a/ArticleService.js b/ArticleService.js new file mode 100644 index 00000000..1b6b82e6 --- /dev/null +++ b/ArticleService.js @@ -0,0 +1,108 @@ +// 초기 await 버전 +// export async function getArticleList(page = '1', pageSize = '10', keyword = '') { +// try { +// const data = await fetch( +// `https://panda-market-api-crud.vercel.app/articles?page=${page}&pageSize=${pageSize}&orderBy=recent&keyword=${keyword}` +// ); +// const body = await data.json(); +// console.log('=====getArticleListData====='); +// if (!data.ok) { +// console.error(`전송 상태 에러: ${data.status}`); +// throw new Error(`Error`); +// } +// console.log(body); +// console.log('=====getArticleListData End====='); +// return body; +// } catch (error) { +// console.error(error); +// throw new Error('에러 발생'); +// } +// } + +export function getArticleList(page = '1', pageSize = '10', keyword = '') { + console.log('=====getArticleListData====='); + return fetch( + `https://panda-market-api-crud.vercel.app/articles?page=${page}&pageSize=${pageSize}&orderBy=recent&keyword=${keyword}` + ) + .then((res) => { + if (!res.ok) { + console.error(`전송 상태 에러: ${res.status}`); + throw new Error(`Error`); + } + return res.json(); + }) + .then((body) => { + console.log(body); + console.log('=====getArticleListDataEND====='); + return body; + }) + .catch((error) => { + console.error('에러코드:' + error.status); + throw new Error('에러 발생'); + }); +} + +export async function getArticle(articleId) { + return fetch(`https://panda-market-api-crud.vercel.app/articles/${articleId}`) + .then((res) => { + if (!res.ok) { + console.error(`전송 상태 에러: ${res.status}`); + throw new Error(`전송 상태 에러`); + } + return res.json(); + }) + .then((body) => { + console.log(body); + console.log('=====getArticleData End====='); + return body; + }) + .catch((error) => { + console.error(error); + throw error; + }); + // 초기 await 버전 + // try { + // const data = await fetch(`https://panda-market-api-crud.vercel.app/articles/${articleId}`); + // console.log('=====getArticleData====='); + // const body = await data.json(); + // if (!data.ok) { + // console.error(`전송 상태: ${data.status}`); + // throw new Error(`전송 상태 에러`); + // } + // console.log(body); + // console.log('=====getArticleData End====='); + // return data; + // } catch (error) { + // console.error(error); + // throw error; + // } +} +export async function createArticle(articleData) { + try { + const _Data = { + title: articleData.title, + content: articleData.content, + image: articleData.image, + }; + + const res = await fetch('https://panda-market-api-crud.vercel.app/articles/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(_Data), + }); + if (!res.ok) { + console.error(`전송 상태 에러: ${res.status}`); + throw new Error('전송 상태 에러'); + } + const data = await res.json(); + console.log('=====PostArticleData====='); + console.log(data); + console.log('=====PostArticleData End====='); + return data; + } catch (error) { + console.error(error); + throw new Error('에러 발생'); + } +} diff --git a/ProductService.js b/ProductService.js new file mode 100644 index 00000000..da9c9fb4 --- /dev/null +++ b/ProductService.js @@ -0,0 +1,126 @@ +import axios from 'axios'; + +const API_URL = axios.create({ + baseURL: 'https://panda-market-api-crud.vercel.app/products', + timeout: 10000, +}); + +export async function getProductList(page = 1, pageSize = 10, keyword = '') { + try { + console.log('====getProductList===='); + const params = { + page: page, + pageSize: pageSize, + orderBy: 'recent', + keyword: keyword, + }; + const res = await API_URL.get('/', { params }); + console.log(res.data); + console.log('====getProductList End===='); + return res.data; + } catch (error) { + console.error(error); + throw error; + } +} + +export async function PostProduct(productData) { + try { + const Data = { + name: productData.name, + description: productData.description, + price: productData.price, + tags: productData.tags, + images: productData.images, + }; + + const res = await API_URL.post('/', Data); + console.log('====PostProduct===='); + console.log(res.data); + console.log('====PostProduct End===='); + return res.data; + } catch (error) { + if (error.response) { + console.error('Error Response Data:', error.response.data); + console.error('Error Response Status:', error.response.status); + throw new Error('생성 오류 발생'); + } else if (error.request) { + throw new Error('리퀘스트는 전송되었으나 응답이 오지 않는 오류 발생'); + } else { + console.error('Error Message:', error.message); + throw new Error('그 외 오류'); + } + } +} + +export async function getProduct(id) { + try { + const res = await API_URL.get(`/${id}`); + console.log('====getProduct===='); + console.log(res.data); + console.log('====getProduct End===='); + return res.data; + } catch (error) { + if (error.response) { + console.error('Error Response Data:', error.response.data); + console.error('Error Response Status:', error.response.status); + throw new Error('생성 오류 발생'); + } else if (error.request) { + throw new Error('리퀘스트는 전송되었으나 응답이 오지 않는 오류 발생'); + } else { + console.error('Error Message:', error.message); + throw new Error('그 외 오류'); + } + } +} + +export async function patchProduct(id, updateData = {}) { + const newData = { + name: updateData.name, + description: updateData.description, + price: updateData.price, + tags: updateData.tags, + images: updateData.images, + }; + console.log('====patchProduct===='); + try { + const res = await API_URL.patch(`/${id}`, newData); + console.log(res.data); + console.log('====patchProduct end===='); + return res.data; + } catch (error) { + if (error.response) { + console.error('Error Response Data:', error.response.data); + console.error('Error Response Status:', error.response.status); + throw new Error('생성 오류 발생'); + } else if (error.request) { + console.log(error); + console.log('========================'); + console.log(error.response.status); + throw new Error('리퀘스트는 전송되었으나 응답이 오지 않는 오류 발생'); + } else { + console.error('Error Message:', error.message); + throw new Error('그 외 오류'); + } + } +} + +export async function deleteProduct(id) { + try { + const res = await API_URL.delete(`/${id}`); + console.log('====DeleteProduct===='); + console.log('====DeleteProduct end===='); + return res.data; + } catch (error) { + if (error.response) { + console.error('Error Response Data:', error.response.data); + console.error('Error Response Status:', error.response.status); + throw new Error('생성 오류 발생'); + } else if (error.request) { + throw new Error('리퀘스트는 전송되었으나 응답이 오지 않는 오류 발생'); + } else { + console.error('Error Message:', error.message); + throw new Error('그 외 오류'); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..104a6851 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +## 요구사항 + +### 기본 + +#### 클래스 구현하기 + +- [x] class 키워드를 이용해서 Product 클래스와 ElectronicProduct 클래스를 만들어 주세요. +- [x] Product 클래스는 name(상품명) description(상품 설명), price(판매 가격), tags(해시태그 배열), images(이미지 배열), favoriteCount(찜하기 수)프로퍼티를 가집니다. +- [x] Product 클래스는 favorite 메소드를 가집니다. favorite 메소드가 호출될 경우 찜하기 수가 1 증가합니다. +- [x] ElectronicProduct 클래스는 Product를 상속하며, 추가로 manufacturer(제조사) 프로퍼티를 가집니다. +- [x] class 키워드를 이용해서 Article 클래스를 만들어 주세요. +- [x] Article 클래스는 title(제목), content(내용), writer(작성자), likeCount(좋아요 수) 프로퍼티를 가집니다. +- [x] Article 클래스는 like 메소드를 가집니다. like 메소드가 호출될 경우 좋아요 수가 1 증가합니다. +- [x] 각 클래스 마다 constructor를 작성해 주세요. +- [ ] 추상화/캡슐화/상속/다형성을 고려하여 코드를 작성해 주세요. + +--- + +#### Article 요청 함수 구현하기 + +- [x] https://panda-market-api-crud.vercel.app/docs 의 Article API를 이용하여 아래 함수들을 구현해 주세요. +- [x] getArticleList() : GET 메소드를 사용해 주세요. +- [x] page, pageSize, keyword 쿼리 파라미터를 이용해 주세요. +- [x] getArticle() : GET 메소드를 사용해 주세요. +- [x] createArticle() : POST 메소드를 사용해 주세요. +- [x] request body에 title, content, image 를 포함해 주세요. +- [x] patchArticle() : PATCH 메소드를 사용해 주세요. +- [x] deleteArticle() : DELETE 메소드를 사용해 주세요. +- [x] fetch 혹은 axios를 이용해 주세요. +- [x] 응답의 상태 코드가 2XX가 아닐 경우, 에러 메시지를 콘솔에 출력해 주세요. +- [x] .then() 메소드를 이용하여 비동기 처리를 해주세요. +- [x] .catch() 를 이용하여 오류 처리를 해주세요. + +#### 심화 + +- [x] Article 클래스에 createdAt(생성일자) 프로퍼티를 만들어 주세요. +- [x] 새로운 객체가 생성되어 constructor가 호출될 시 createdAt에 현재 시간을 저장합니다. + +--- + +#### Product 요청 함수 구현하기 + +- [x] https://panda-market-api-crud.vercel.app/docs 의 Product API를 이용하여 아래 함수들을 구현해 주세요. +- [x] getProductList() : GET 메소드를 사용해 주세요. +- [x] page, pageSize, keyword 쿼리 파라미터를 이용해 주세요. +- [x] getProduct() : GET 메소드를 사용해 주세요. +- [x] createProduct() : POST 메소드를 사용해 주세요. +- [x] request body에 name, description, price, tags, images 를 포함해 주세요. +- [x] patchProduct() : PATCH 메소드를 사용해 주세요. +- [x] deleteProduct() : DELETE 메소드를 사용해 주세요. +- [x] async/await 을 이용하여 비동기 처리를 해주세요. +- [x] try/catch 를 이용하여 오류 처리를 해주세요. +- [x] getProductList()를 통해서 받아온 상품 리스트를 각각 인스턴스로 만들어 products 배열에 저장해 주세요. +- [x] 해시태그에 "전자제품"이 포함되어 있는 상품들은 Product 클래스 대신 ElectronicProduct 클래스를 사용해 인스턴스를 생성해 주세요. +- [x] 나머지 상품들은 모두 Product 클래스를 사용해 인스턴스를 생성해 주세요. +- [x] 구현한 함수들을 아래와 같이 파일을 분리해 주세요. +- [x] export를 활용해 주세요. +- [x] ProductService.js 파일 Product API 관련 함수들을 작성해 주세요. +- [x] ArticleService.js 파일에 Article API 관련 함수들을 작성해 주세요. +- [x] 이외의 코드들은 모두 main.js 파일에 작성해 주세요. +- [x] import를 활용해 주세요. +- [x] 각 함수를 실행하는 코드를 작성하고, 제대로 동작하는지 확인해 주세요. + +## 멘토에게 + +- +- diff --git a/main.js b/main.js new file mode 100644 index 00000000..5555d959 --- /dev/null +++ b/main.js @@ -0,0 +1,110 @@ +import { getArticleList, getArticle, createArticle } from './ArticleService.js'; +import { getProduct, getProductList, PostProduct, patchProduct, deleteProduct } from './ProductService.js'; + +class Product { + constructor(name, description, prcie, tags, images) { + this.name = name; + this.description = description; + this.price = prcie; + this.tags = tags; + this.images = images; + this.favoriteCount = 0; + this.createdAt = new Date(); + } + favorite() { + this.favoriteCount += 1; + } +} + +class ElectronicProduct extends Product { + constructor(name, decription, prcie, tags, images, manufacturerr) { + super(name, decription, prcie, tags, images); + this.manufacturerr = manufacturerr; + } +} + +class Article { + constructor(title, content, writer) { + this.title = title; + this.content = content; + this.writer = writer; + this.likeCount = 0; + this.createdAt = new Date(); + } + like() { + this.likeCount += 1; + } +} + +const newArticleData = new Article('BH new Article', 'This is content', 'BlueHamster'); +const Products = []; +async function DoItArticleThings() { + const PostCompleteData = await createArticle(newArticleData); + const articleList = await getArticleList(1, 10, ''); + const article = await getArticle(PostCompleteData.id); +} + +async function DoItProductThings() { + const newProduct = await CreateProduct( + 'BH new Product', + 'This is Product description', + 10000, + ['전자제품', '컴퓨터'], + ['https://sampleimage', 'https://thisisimage2'], + 'Samsung' + ); + const PostCompleteProductData = await PostProduct(newProduct); + const product = await getProduct(PostCompleteProductData.id); + + if (!product) { + console.log('상품이 없습니다.'); + return; + } + + product.description = '가격 2배 이벤트.'; + product.price = 20000; + const updatedProduct = await patchProduct(product.id, product); + const isDeleted = await deleteProduct(product.id); + console.log('삭제 완료:', isDeleted); + + const productList = await getProductList(1, 10, ''); + console.log(productList); + + // productList가 배열일 경우 length 사용 + for (let i = 0; i < productList.list.length; i++) { + const prod = productList.list[i]; + const newP = await CreateProduct( + prod.name, + prod.description, + prod.price, + prod.tags, + prod.images, + prod.manufacturerr ? prod.manufacturerr : '' + ); + // 필요한 값만 복사 + newP.createdAt = prod.createdAt; + Products.push(newP); + } + console.log(Products); +} + +async function CreateProduct(name, decription, prcie, tags, images, manufacturerr = '') { + let newProduct; + if (tags.includes === '전자제품') { + newProduct = new ElectronicProduct(name, decription, prcie, tags, images, manufacturerr); + } else { + newProduct = new Product(name, decription, prcie, tags, images); + } + return newProduct; +} + +async function DoTest() { + console.log('Article 관련 함수 시작'); + await DoItArticleThings(); + console.log('Article 관련 함수 종료'); + console.log('Product 관련 함수 시작'); + await DoItProductThings(); + console.log('Product 관련 함수 종료'); +} + +DoTest(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..b6c0a8e4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,295 @@ +{ + "name": "sprint1", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sprint1", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.12.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..16cd1809 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "sprint1", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "start": "node main.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "axios": "^1.12.2" + } +} From 7ceb00c1f0bd6647d67d035e6bc83fabc6e906a6 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 16:48:18 +0900 Subject: [PATCH 02/53] =?UTF-8?q?[FIX]=20product=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=8C=8C=EC=9D=BC=20=EB=B6=84=EB=A6=AC,=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ElectronicProduct.js | 14 +++++++++++ ProductService.js | 20 ++++++++-------- README.md | 2 +- main.js | 38 +++++++----------------------- product.js | 56 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 ElectronicProduct.js create mode 100644 product.js diff --git a/ElectronicProduct.js b/ElectronicProduct.js new file mode 100644 index 00000000..bf1e149b --- /dev/null +++ b/ElectronicProduct.js @@ -0,0 +1,14 @@ +import { Product } from './product.js'; + +export class ElectronicProduct extends Product { + constructor(name, decription, prcie, tags, images, manufacturerr) { + super(name, decription, prcie, tags, images); + this._manufacturerr = manufacturerr; + } + GetManufacturerr() { + return this._manufacturerr; + } + SetManufacturerr(manufacturerr) { + this._manufacturerr = manufacturerr; + } +} diff --git a/ProductService.js b/ProductService.js index da9c9fb4..3851e7f7 100644 --- a/ProductService.js +++ b/ProductService.js @@ -27,11 +27,11 @@ export async function getProductList(page = 1, pageSize = 10, keyword = '') { export async function PostProduct(productData) { try { const Data = { - name: productData.name, - description: productData.description, - price: productData.price, - tags: productData.tags, - images: productData.images, + name: productData._name, + description: productData._description, + price: productData._price, + tags: productData._tags, + images: productData._images, }; const res = await API_URL.post('/', Data); @@ -76,11 +76,11 @@ export async function getProduct(id) { export async function patchProduct(id, updateData = {}) { const newData = { - name: updateData.name, - description: updateData.description, - price: updateData.price, - tags: updateData.tags, - images: updateData.images, + name: updateData._name, + description: updateData._description, + price: updateData._price, + tags: updateData._tags, + images: updateData._images, }; console.log('====patchProduct===='); try { diff --git a/README.md b/README.md index 104a6851..dca41c45 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [x] Article 클래스는 title(제목), content(내용), writer(작성자), likeCount(좋아요 수) 프로퍼티를 가집니다. - [x] Article 클래스는 like 메소드를 가집니다. like 메소드가 호출될 경우 좋아요 수가 1 증가합니다. - [x] 각 클래스 마다 constructor를 작성해 주세요. -- [ ] 추상화/캡슐화/상속/다형성을 고려하여 코드를 작성해 주세요. +- [x] 추상화/캡슐화/상속/다형성을 고려하여 코드를 작성해 주세요. --- diff --git a/main.js b/main.js index 5555d959..39f8aff1 100644 --- a/main.js +++ b/main.js @@ -1,27 +1,7 @@ import { getArticleList, getArticle, createArticle } from './ArticleService.js'; import { getProduct, getProductList, PostProduct, patchProduct, deleteProduct } from './ProductService.js'; - -class Product { - constructor(name, description, prcie, tags, images) { - this.name = name; - this.description = description; - this.price = prcie; - this.tags = tags; - this.images = images; - this.favoriteCount = 0; - this.createdAt = new Date(); - } - favorite() { - this.favoriteCount += 1; - } -} - -class ElectronicProduct extends Product { - constructor(name, decription, prcie, tags, images, manufacturerr) { - super(name, decription, prcie, tags, images); - this.manufacturerr = manufacturerr; - } -} +import { Product } from './product.js'; +import { ElectronicProduct } from './ElectronicProduct.js'; class Article { constructor(title, content, writer) { @@ -61,8 +41,8 @@ async function DoItProductThings() { return; } - product.description = '가격 2배 이벤트.'; - product.price = 20000; + product._description = '가격 2배 이벤트.'; + product.SetPrice = 20000; const updatedProduct = await patchProduct(product.id, product); const isDeleted = await deleteProduct(product.id); console.log('삭제 완료:', isDeleted); @@ -82,7 +62,7 @@ async function DoItProductThings() { prod.manufacturerr ? prod.manufacturerr : '' ); // 필요한 값만 복사 - newP.createdAt = prod.createdAt; + newP.SetCreatedAt(prod._createdAt); Products.push(newP); } console.log(Products); @@ -90,7 +70,7 @@ async function DoItProductThings() { async function CreateProduct(name, decription, prcie, tags, images, manufacturerr = '') { let newProduct; - if (tags.includes === '전자제품') { + if (tags.includes('전자제품')) { newProduct = new ElectronicProduct(name, decription, prcie, tags, images, manufacturerr); } else { newProduct = new Product(name, decription, prcie, tags, images); @@ -99,9 +79,9 @@ async function CreateProduct(name, decription, prcie, tags, images, manufacturer } async function DoTest() { - console.log('Article 관련 함수 시작'); - await DoItArticleThings(); - console.log('Article 관련 함수 종료'); + // console.log('Article 관련 함수 시작'); + // await DoItArticleThings(); + // console.log('Article 관련 함수 종료'); console.log('Product 관련 함수 시작'); await DoItProductThings(); console.log('Product 관련 함수 종료'); diff --git a/product.js b/product.js new file mode 100644 index 00000000..9a85a985 --- /dev/null +++ b/product.js @@ -0,0 +1,56 @@ +export class Product { + constructor(name, description, prcie, tags, images) { + this._name = name; + this._description = description; + this._price = prcie; + this._tags = tags; + this._images = images; + this._favoriteCount = 0; + this._createdAt = new Date(); + } + favorite() { + this._favoriteCount += 1; + } + GetName() { + return this._name; + } + SetName(name) { + this._name = name; + } + GetDescription() { + return this._description; + } + SetDescription(description) { + this._description = description; + } + GetPrice() { + return this._price; + } + SetPrice(price) { + this._price = price; + } + GetTags() { + return this._tags; + } + SetTags(tags) { + this._tags = tags; + } + GetImages() { + return this._images; + } + SetImages(images) { + this._images = images; + } + GetFavoriteCount() { + return this._favoriteCount; + } + SetfavoriteCount(count) { + this._favoriteCount = count; + } + GetCreatedAt() { + return this._createdAt; + } + SetCreatedAt(date) { + this._createdAt = date; + } +} From d97ea80a12d62313b9979d051ef0328d18569202 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 16:50:28 +0900 Subject: [PATCH 03/53] =?UTF-8?q?[FIX]=20Article=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=ED=8C=8C=EC=9D=BC=20=EB=B6=84=EB=A6=AC,=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=ED=99=94=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Article.js | 27 +++++++++++++++++++++++++++ main.js | 20 ++++---------------- 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 Article.js diff --git a/Article.js b/Article.js new file mode 100644 index 00000000..ac1f263e --- /dev/null +++ b/Article.js @@ -0,0 +1,27 @@ +export class Article { + constructor(title, content, writer) { + this.title = title; + this.content = content; + this.writer = writer; + this.likeCount = 0; + this.createdAt = new Date(); + } + like() { + this.likeCount += 1; + } + getTitle() { + return this.title; + } + getContent() { + return this.content; + } + getWriter() { + return this.writer; + } + getLikeCount() { + return this.likeCount; + } + getCreatedAt() { + return this.createdAt; + } +} diff --git a/main.js b/main.js index 39f8aff1..57383aad 100644 --- a/main.js +++ b/main.js @@ -2,19 +2,7 @@ import { getArticleList, getArticle, createArticle } from './ArticleService.js'; import { getProduct, getProductList, PostProduct, patchProduct, deleteProduct } from './ProductService.js'; import { Product } from './product.js'; import { ElectronicProduct } from './ElectronicProduct.js'; - -class Article { - constructor(title, content, writer) { - this.title = title; - this.content = content; - this.writer = writer; - this.likeCount = 0; - this.createdAt = new Date(); - } - like() { - this.likeCount += 1; - } -} +import { Article } from './Article.js'; const newArticleData = new Article('BH new Article', 'This is content', 'BlueHamster'); const Products = []; @@ -79,9 +67,9 @@ async function CreateProduct(name, decription, prcie, tags, images, manufacturer } async function DoTest() { - // console.log('Article 관련 함수 시작'); - // await DoItArticleThings(); - // console.log('Article 관련 함수 종료'); + console.log('Article 관련 함수 시작'); + await DoItArticleThings(); + console.log('Article 관련 함수 종료'); console.log('Product 관련 함수 시작'); await DoItProductThings(); console.log('Product 관련 함수 종료'); From 95fbb384bb126f43944a218c7f58731480df5eff Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 17:11:28 +0900 Subject: [PATCH 04/53] =?UTF-8?q?[DEL]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ArticleService.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/ArticleService.js b/ArticleService.js index 1b6b82e6..dca6061e 100644 --- a/ArticleService.js +++ b/ArticleService.js @@ -1,24 +1,3 @@ -// 초기 await 버전 -// export async function getArticleList(page = '1', pageSize = '10', keyword = '') { -// try { -// const data = await fetch( -// `https://panda-market-api-crud.vercel.app/articles?page=${page}&pageSize=${pageSize}&orderBy=recent&keyword=${keyword}` -// ); -// const body = await data.json(); -// console.log('=====getArticleListData====='); -// if (!data.ok) { -// console.error(`전송 상태 에러: ${data.status}`); -// throw new Error(`Error`); -// } -// console.log(body); -// console.log('=====getArticleListData End====='); -// return body; -// } catch (error) { -// console.error(error); -// throw new Error('에러 발생'); -// } -// } - export function getArticleList(page = '1', pageSize = '10', keyword = '') { console.log('=====getArticleListData====='); return fetch( From 671b3dee054c60627b41de27ed71f724c251e810 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 17:15:03 +0900 Subject: [PATCH 05/53] =?UTF-8?q?Revert=20"[FIX]=20Article=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=ED=8C=8C=EC=9D=BC=20=EB=B6=84=EB=A6=AC,?= =?UTF-8?q?=20=EC=BA=A1=EC=8A=90=ED=99=94=20=EC=A7=84=ED=96=89"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d97ea80a12d62313b9979d051ef0328d18569202. --- Article.js | 27 --------------------------- main.js | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 31 deletions(-) delete mode 100644 Article.js diff --git a/Article.js b/Article.js deleted file mode 100644 index ac1f263e..00000000 --- a/Article.js +++ /dev/null @@ -1,27 +0,0 @@ -export class Article { - constructor(title, content, writer) { - this.title = title; - this.content = content; - this.writer = writer; - this.likeCount = 0; - this.createdAt = new Date(); - } - like() { - this.likeCount += 1; - } - getTitle() { - return this.title; - } - getContent() { - return this.content; - } - getWriter() { - return this.writer; - } - getLikeCount() { - return this.likeCount; - } - getCreatedAt() { - return this.createdAt; - } -} diff --git a/main.js b/main.js index 57383aad..39f8aff1 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,19 @@ import { getArticleList, getArticle, createArticle } from './ArticleService.js'; import { getProduct, getProductList, PostProduct, patchProduct, deleteProduct } from './ProductService.js'; import { Product } from './product.js'; import { ElectronicProduct } from './ElectronicProduct.js'; -import { Article } from './Article.js'; + +class Article { + constructor(title, content, writer) { + this.title = title; + this.content = content; + this.writer = writer; + this.likeCount = 0; + this.createdAt = new Date(); + } + like() { + this.likeCount += 1; + } +} const newArticleData = new Article('BH new Article', 'This is content', 'BlueHamster'); const Products = []; @@ -67,9 +79,9 @@ async function CreateProduct(name, decription, prcie, tags, images, manufacturer } async function DoTest() { - console.log('Article 관련 함수 시작'); - await DoItArticleThings(); - console.log('Article 관련 함수 종료'); + // console.log('Article 관련 함수 시작'); + // await DoItArticleThings(); + // console.log('Article 관련 함수 종료'); console.log('Product 관련 함수 시작'); await DoItProductThings(); console.log('Product 관련 함수 종료'); From 0dae9bf17f674eec36bb74a9704a7fc1dcff3004 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 17:20:02 +0900 Subject: [PATCH 06/53] =?UTF-8?q?[DEL]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Article.js | 27 +++++++++++++++++++++++++++ ArticleService.js | 16 ---------------- main.js | 6 +++--- 3 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 Article.js diff --git a/Article.js b/Article.js new file mode 100644 index 00000000..ac1f263e --- /dev/null +++ b/Article.js @@ -0,0 +1,27 @@ +export class Article { + constructor(title, content, writer) { + this.title = title; + this.content = content; + this.writer = writer; + this.likeCount = 0; + this.createdAt = new Date(); + } + like() { + this.likeCount += 1; + } + getTitle() { + return this.title; + } + getContent() { + return this.content; + } + getWriter() { + return this.writer; + } + getLikeCount() { + return this.likeCount; + } + getCreatedAt() { + return this.createdAt; + } +} diff --git a/ArticleService.js b/ArticleService.js index dca6061e..a7f17630 100644 --- a/ArticleService.js +++ b/ArticleService.js @@ -39,22 +39,6 @@ export async function getArticle(articleId) { console.error(error); throw error; }); - // 초기 await 버전 - // try { - // const data = await fetch(`https://panda-market-api-crud.vercel.app/articles/${articleId}`); - // console.log('=====getArticleData====='); - // const body = await data.json(); - // if (!data.ok) { - // console.error(`전송 상태: ${data.status}`); - // throw new Error(`전송 상태 에러`); - // } - // console.log(body); - // console.log('=====getArticleData End====='); - // return data; - // } catch (error) { - // console.error(error); - // throw error; - // } } export async function createArticle(articleData) { try { diff --git a/main.js b/main.js index 39f8aff1..cb79f6c4 100644 --- a/main.js +++ b/main.js @@ -79,9 +79,9 @@ async function CreateProduct(name, decription, prcie, tags, images, manufacturer } async function DoTest() { - // console.log('Article 관련 함수 시작'); - // await DoItArticleThings(); - // console.log('Article 관련 함수 종료'); + console.log('Article 관련 함수 시작'); + await DoItArticleThings(); + console.log('Article 관련 함수 종료'); console.log('Product 관련 함수 시작'); await DoItProductThings(); console.log('Product 관련 함수 종료'); From bfa7d1c899eec33f4c84f530d7f9745c973b121d Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 17 Oct 2025 18:58:56 +0900 Subject: [PATCH 07/53] =?UTF-8?q?[FIX]Article=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/main.js b/main.js index cb79f6c4..96ed6b8d 100644 --- a/main.js +++ b/main.js @@ -3,19 +3,6 @@ import { getProduct, getProductList, PostProduct, patchProduct, deleteProduct } import { Product } from './product.js'; import { ElectronicProduct } from './ElectronicProduct.js'; -class Article { - constructor(title, content, writer) { - this.title = title; - this.content = content; - this.writer = writer; - this.likeCount = 0; - this.createdAt = new Date(); - } - like() { - this.likeCount += 1; - } -} - const newArticleData = new Article('BH new Article', 'This is content', 'BlueHamster'); const Products = []; async function DoItArticleThings() { @@ -46,7 +33,6 @@ async function DoItProductThings() { const updatedProduct = await patchProduct(product.id, product); const isDeleted = await deleteProduct(product.id); console.log('삭제 완료:', isDeleted); - const productList = await getProductList(1, 10, ''); console.log(productList); From be279da94178f84cdc4fb2625603a3cf1bbd8dd4 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 6 Nov 2025 11:11:37 +0900 Subject: [PATCH 08/53] =?UTF-8?q?[ADD]=20sprint3=20=EB=82=B4=EC=9A=A9=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 --- .gitignore | 5 +- package-lock.json | 1667 ++++++++++++++++- package.json | 27 +- .prettierrc => sprint-mission-2/.prettierrc | 0 Article.js => sprint-mission-2/Article.js | 0 .../ArticleService.js | 0 .../ElectronicProduct.js | 0 .../ProductService.js | 0 README.md => sprint-mission-2/README.md | 0 main.js => sprint-mission-2/main.js | 0 product.js => sprint-mission-2/product.js | 0 src/Routers/articleRouter.js | 38 + src/Routers/commentRouter.js | 36 + src/Routers/productRouter.js | 38 + src/Routers/uploadRouter.js | 16 + src/apis/articleapi.js | 93 + src/apis/commentapi.js | 120 ++ src/apis/productapi.js | 91 + src/apis/uploadapi.js | 8 + src/libs/constants.js | 4 + src/libs/structs.js | 37 + src/main.js | 22 + upload/dfaa169cd68c4c0f453dabf12f0063a6 | Bin 0 -> 494180 bytes 23 files changed, 2117 insertions(+), 85 deletions(-) rename .prettierrc => sprint-mission-2/.prettierrc (100%) rename Article.js => sprint-mission-2/Article.js (100%) rename ArticleService.js => sprint-mission-2/ArticleService.js (100%) rename ElectronicProduct.js => sprint-mission-2/ElectronicProduct.js (100%) rename ProductService.js => sprint-mission-2/ProductService.js (100%) rename README.md => sprint-mission-2/README.md (100%) rename main.js => sprint-mission-2/main.js (100%) rename product.js => sprint-mission-2/product.js (100%) create mode 100644 src/Routers/articleRouter.js create mode 100644 src/Routers/commentRouter.js create mode 100644 src/Routers/productRouter.js create mode 100644 src/Routers/uploadRouter.js create mode 100644 src/apis/articleapi.js create mode 100644 src/apis/commentapi.js create mode 100644 src/apis/productapi.js create mode 100644 src/apis/uploadapi.js create mode 100644 src/libs/constants.js create mode 100644 src/libs/structs.js create mode 100644 src/main.js create mode 100644 upload/dfaa169cd68c4c0f453dabf12f0063a6 diff --git a/.gitignore b/.gitignore index b512c09d..ee9ab89c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -node_modules \ No newline at end of file +node_modules/ +prisma/ +.env +__http__/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b6c0a8e4..71e05921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,32 +1,302 @@ { - "name": "sprint1", + "name": "0-sprint-mission-3", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "sprint1", + "name": "0-sprint-mission-3", "version": "1.0.0", "license": "ISC", "dependencies": { - "axios": "^1.12.2" + "@prisma/client": "^6.18.0", + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "is-email": "^1.0.2", + "is-uuid": "^1.0.2", + "multer": "^2.0.2", + "prisma": "^6.18.0", + "superstruct": "^2.0.2" + }, + "devDependencies": { + "nodemon": "^3.1.10" + } + }, + "node_modules/@prisma/client": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", + "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", + "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", + "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", + "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/fetch-engine": "6.18.0", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", + "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", + "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", + "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, - "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/call-bind-apply-helpers": { @@ -42,25 +312,202 @@ "node": ">= 0.4" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/delayed-stream": { + "node_modules/content-disposition": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/dunder-proto": { @@ -77,6 +524,40 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -107,55 +588,152 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", "funding": [ { "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" } ], "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, "engines": { - "node": ">=4.0" + "node": ">=8.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 6" + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/function-bind": { @@ -204,22 +782,40 @@ "node": ">= 0.4" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "giget": "dist/cli.mjs" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -228,14 +824,21 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, "engines": { "node": ">= 0.4" }, @@ -255,6 +858,138 @@ "node": ">= 0.4" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-email": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-email/-/is-email-1.0.2.tgz", + "integrity": "sha512-UojUgD2EhDTBQ2SGKwrK9edce5phRzgLsP+V5+Uu2Swi+uvjVXgH3zduM3HhT9iaC/9Kq19/TYUbP0jPoi6ioA==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-uuid": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-uuid/-/is-uuid-1.0.2.tgz", + "integrity": "sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -264,7 +999,116 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", @@ -273,7 +1117,7 @@ "node": ">= 0.6" } }, - "node_modules/mime-types": { + "node_modules/multer/node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", @@ -285,11 +1129,682 @@ "node": ">= 0.6" } }, - "node_modules/proxy-from-env": { + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/prisma": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", + "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.18.0", + "@prisma/engines": "6.18.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } } } } diff --git a/package.json b/package.json index 16cd1809..08482e5d 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,28 @@ { - "name": "sprint1", + "name": "0-sprint-mission-3", "version": "1.0.0", "description": "", + "license": "ISC", + "author": "", + "type": "module", "main": "main.js", "scripts": { - "start": "node main.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "nodemon ./src/main.js", + "start": "node ./src/main.js" }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "module", "dependencies": { - "axios": "^1.12.2" + "@prisma/client": "^6.18.0", + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "is-email": "^1.0.2", + "is-uuid": "^1.0.2", + "multer": "^2.0.2", + "prisma": "^6.18.0", + "superstruct": "^2.0.2" + }, + "devDependencies": { + "nodemon": "^3.1.10" } } diff --git a/.prettierrc b/sprint-mission-2/.prettierrc similarity index 100% rename from .prettierrc rename to sprint-mission-2/.prettierrc diff --git a/Article.js b/sprint-mission-2/Article.js similarity index 100% rename from Article.js rename to sprint-mission-2/Article.js diff --git a/ArticleService.js b/sprint-mission-2/ArticleService.js similarity index 100% rename from ArticleService.js rename to sprint-mission-2/ArticleService.js diff --git a/ElectronicProduct.js b/sprint-mission-2/ElectronicProduct.js similarity index 100% rename from ElectronicProduct.js rename to sprint-mission-2/ElectronicProduct.js diff --git a/ProductService.js b/sprint-mission-2/ProductService.js similarity index 100% rename from ProductService.js rename to sprint-mission-2/ProductService.js diff --git a/README.md b/sprint-mission-2/README.md similarity index 100% rename from README.md rename to sprint-mission-2/README.md diff --git a/main.js b/sprint-mission-2/main.js similarity index 100% rename from main.js rename to sprint-mission-2/main.js diff --git a/product.js b/sprint-mission-2/product.js similarity index 100% rename from product.js rename to sprint-mission-2/product.js diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js new file mode 100644 index 00000000..b2ff514e --- /dev/null +++ b/src/Routers/articleRouter.js @@ -0,0 +1,38 @@ +import express from 'express'; +import { Prisma } from '@prisma/client'; +import { + GetArticle, + PostArticle, + GetArticleById, + PatchArticleById, + DeleteArticleById +} from '../apis/articleapi.js'; + +const articleRouter = express.Router(); + + + +articleRouter.route('/') + .get(GetArticle) + .post(PostArticle); + +articleRouter.route('/:id') + .get(GetArticleById) + .patch(PatchArticleById) + .delete(DeleteArticleById); + + +articleRouter.use((err, req, res, next) => { + console.error(err.name); + if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { + res.sendStatus(404); + } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { + res.status(400).send({ message: err.message }); + } else if (err.name == "StructError") { + res.status(400).send({ message: err.message }); + } else { + res.status(500).send({ message: err.message }); + } +}); + +export default articleRouter; \ No newline at end of file diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js new file mode 100644 index 00000000..52140988 --- /dev/null +++ b/src/Routers/commentRouter.js @@ -0,0 +1,36 @@ +import express from 'express'; +import { Prisma } from '@prisma/client'; +import { + GetComment, + PostComment, + GetCommentById, + PatchCommentById, + DeleteCommentById +} from '../apis/commentapi.js'; + +const commentRouter = express.Router(); + +commentRouter.route('/') + .get(GetComment) + .post(PostComment); + +commentRouter.route('/:id') + .get(GetCommentById) + .patch(PatchCommentById) + .delete(DeleteCommentById); + + +commentRouter.use((err, req, res, next) => { + console.error(err.name); + if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { + res.sendStatus(404); + } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { + res.status(400).send({ message: err.message }); + } else if (err.name == "StructError") { + res.status(400).send({ message: err.message }); + } else { + res.status(500).send({ message: err.message }); + } +}); + +export default commentRouter; \ No newline at end of file diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js new file mode 100644 index 00000000..58625e52 --- /dev/null +++ b/src/Routers/productRouter.js @@ -0,0 +1,38 @@ +import express from 'express'; +import { Prisma } from '@prisma/client'; +import { + GetProduct, + PostProduct, + GetProductById, + PatchProductById, + DeleteProductById +} from '../apis/productapi.js'; + +const productRouter = express.Router(); + + + +productRouter.route('/') + .get(GetProduct) + .post(PostProduct); + +productRouter.route('/:id') + .get(GetProductById) + .patch(PatchProductById) + .delete(DeleteProductById); + + +productRouter.use((err, req, res, next) => { + console.error(err.name); + if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { + res.sendStatus(404); + } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { + res.status(400).send({ message: err.message }); + } else if (err.name == "StructError") { + res.status(400).send({ message: err.message }); + } else { + res.status(500).send({ message: err.message }); + } +}); + +export default productRouter; \ No newline at end of file diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.js new file mode 100644 index 00000000..f8634cf8 --- /dev/null +++ b/src/Routers/uploadRouter.js @@ -0,0 +1,16 @@ +import express from 'express'; +import multer from 'multer'; +import { + UploadSingleImage +} from '../apis/uploadapi.js'; + +const uploadRouter = express.Router(); + +const upload = multer({ dest: 'upload/' }); + +uploadRouter.post('/', upload.single('attachment'), + UploadSingleImage); + +uploadRouter.use('/', express.static('upload')); + +export default uploadRouter; diff --git a/src/apis/articleapi.js b/src/apis/articleapi.js new file mode 100644 index 00000000..d552593f --- /dev/null +++ b/src/apis/articleapi.js @@ -0,0 +1,93 @@ +import { PrismaClient } from '@prisma/client'; +import { assert } from 'superstruct'; +import { CreateArticle, PatchArticle } from '../libs/structs.js'; + + +const prisma = new PrismaClient(); + +export async function GetArticle(req, res) { + const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + + const findOptions = { + where: { + title: { + contains: title, + }, + content: { + contains: content, + }, + }, + orderBy, + skip: parsedOffset, + }; + + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + + const article = await prisma.article.findMany(findOptions); + res.send(article); +} + + +export async function GetArticleById(req, res) { + const { id } = req.params; + const article = await prisma.article.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(article); +} + +export async function PostArticle(req, res) { + assert(req.body, CreateArticle); + const { ...userFields } = req.body; + const article = await prisma.article.create({ + data: { + ...userFields + }, + }); + res.status(201).send(article); +} + +export async function PatchArticleById(req, res) { + const { id } = req.params; + assert(req.body, PatchArticle); + const { ...userFields } = req.body; + const article = await prisma.article.update({ + where: { + id + }, + data: { + ...userFields + }, + }); + res.send(article); +} + +export async function DeleteArticleById(req, res) { + const { id } = req.params; + const article = await prisma.article.delete({ + where: { + id + }, + }); + res.send(article); +} + + diff --git a/src/apis/commentapi.js b/src/apis/commentapi.js new file mode 100644 index 00000000..20923e16 --- /dev/null +++ b/src/apis/commentapi.js @@ -0,0 +1,120 @@ +import { PrismaClient } from '@prisma/client'; +import { assert } from 'superstruct'; +import { CreateComment, PatchComment } from '../libs/structs.js'; + + +const prisma = new PrismaClient(); + +export async function GetComment(req, res) { + const { productId, articleId } = req.query; + const { take = '10', cursor } = req.query; + const parsedTake = parseInt(take, 10); + + if (isNaN(parsedTake) || parsedTake <= 0) { + return res.status(400).send({ error: 'Invalid "take" parameter.' }); + } + + const whereClause = {}; + + if (productId) { + whereClause.productId = productId; // 상품 ID로 필터링 + } else if (articleId) { + whereClause.articleId = articleId; // 게시글 ID로 필터링 + } + + const findOptions = { + take: parsedTake, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, + }; + } + + const comments = await prisma.comment.findMany(findOptions); // + let nextCursor = null; + if (comments.length === parsedTake) { + nextCursor = comments[comments.length - 1].id; + } + res.status(200).json({ + comments, + nextCursor, + }); +} + + +export async function GetCommentById(req, res) { + const { id } = req.params; + const Comment = await prisma.comment.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(Comment); +} + + +export async function PostComment(req, res) { + assert(req.body, CreateComment); + const { content, productId, articleId } = req.body; + console.log("====================="); + console.log(content); + console.log(productId); + console.log(articleId); + console.log("====================="); + if ((productId && articleId) || (!productId && !articleId)) { + return res.status(400).json({ + error: 'Comment must belong to EITHER a Product OR an Article.', + }); + } + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + + const comment = await prisma.comment.create({ + data: { + content: content, + productId: productId, + articleId: articleId, + }, + }); + res.status(201).send(comment); +} + +export async function PatchCommentById(req, res) { + const { id } = req.params; + assert(req.body, PatchComment); + const { content } = req.body; + + if (content !== undefined && content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + + const comment = await prisma.comment.update({ + where: { + id + }, + data: { + content: content, + }, + }); + res.send(comment); +} + +export async function DeleteCommentById(req, res) { + const { id } = req.params; + const Comment = await prisma.comment.delete({ + where: { + id + }, + }); + res.send(Comment); +} + + diff --git a/src/apis/productapi.js b/src/apis/productapi.js new file mode 100644 index 00000000..4a98cbdf --- /dev/null +++ b/src/apis/productapi.js @@ -0,0 +1,91 @@ +import { PrismaClient } from '@prisma/client'; +import { assert } from 'superstruct'; +import { CreateProduct, PatchProduct } from '../libs/structs.js'; + + +const prisma = new PrismaClient(); + +export async function GetProduct(req, res) { + const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + + const findOptions = { + where: { + name: { + contains: name, + }, + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; + + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + + const product = await prisma.product.findMany(findOptions); + res.send(product); +} + + +export async function GetProductById(req, res) { + const { id } = req.params; + const product = await prisma.product.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(product); +} + +export async function PostProduct(req, res) { + assert(req.body, CreateProduct); + const { ...userFields } = req.body; + const product = await prisma.product.create({ + data: { + ...userFields + }, + }); + res.status(201).send(product); +} + +export async function PatchProductById(req, res) { + const { id } = req.params; + assert(req.body, PatchProduct); + const { ...userFields } = req.body; + const Product = await prisma.product.update({ + where: { + id + }, + data: { + ...userFields + }, + }); + res.send(Product); +} + +export async function DeleteProductById(req, res) { + const { id } = req.params; + const Product = await prisma.product.delete({ + where: { + id + }, + }); + res.send(Product); +} \ No newline at end of file diff --git a/src/apis/uploadapi.js b/src/apis/uploadapi.js new file mode 100644 index 00000000..d753a46f --- /dev/null +++ b/src/apis/uploadapi.js @@ -0,0 +1,8 @@ + + +export function UploadSingleImage(req, res) { + console.log(req.file); + const { filename } = req.file; + const path = `files/${filename}`; + res.json({ path }); +} \ No newline at end of file diff --git a/src/libs/constants.js b/src/libs/constants.js new file mode 100644 index 00000000..b70b83f9 --- /dev/null +++ b/src/libs/constants.js @@ -0,0 +1,4 @@ +import * as dotenv from 'dotenv'; +dotenv.config(); + +export const PORT = process.env.PORT; \ No newline at end of file diff --git a/src/libs/structs.js b/src/libs/structs.js new file mode 100644 index 00000000..ddd86136 --- /dev/null +++ b/src/libs/structs.js @@ -0,0 +1,37 @@ +import * as s from "superstruct"; +import isUuid from "is-uuid"; + + +export const CreateProduct = s.object({ + name: s.size(s.string(), 1, 100), + description: s.string(), + price: s.min(s.number(), 0), + tags: s.array(s.string()), +}); + + +export const CreateArticle = s.object({ + title: s.size(s.string(), 1, 100), + content: s.string(), +}); + +export const CreateComment = s.refine(s.object({ + content: s.string(), + productId: s.optional(s.define('Uuid', (value) => isUuid.v4(value))), + articleId: s.optional(s.define('Uuid', (value) => isUuid.v4(value))), +}), + 'EitherProductIdOrArticleId',//검증 규칙 이름 + (value) => { + const { productId, articleId } = value; + // 실패 케이스: (둘 다 있거나) OR (둘 다 없거나) + if ((productId && articleId) || (!productId && !articleId)) { + return 'Comment must have *either* a productId *or* an articleId, but not both.'; + } + + // 성공 케이스: 둘 중 하나만 존재함 + return true; + }); + +export const PatchProduct = s.partial(CreateProduct); +export const PatchArticle = s.partial(CreateArticle); +export const PatchComment = s.object({ content: s.optional(s.string()), }); \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..b6c5d4a0 --- /dev/null +++ b/src/main.js @@ -0,0 +1,22 @@ +import express from 'express'; +import { PORT } from './libs/constants.js'; +import productRouter from './Routers/productRouter.js'; +import articleRouter from './Routers/articleRouter.js'; +import commentRouter from './Routers/commentRouter.js'; +import uploadRouter from './Routers/uploadRouter.js'; +import cors from 'cors'; + + +const app = express(); +app.use(cors()); +app.use(express.json()); + + +app.use('/products', productRouter); +app.use('/articles', articleRouter); +app.use('/comments', commentRouter); +app.use('/files', uploadRouter); + +app.listen(PORT, () => { + console.log(`Server is running`); +}); \ No newline at end of file diff --git a/upload/dfaa169cd68c4c0f453dabf12f0063a6 b/upload/dfaa169cd68c4c0f453dabf12f0063a6 new file mode 100644 index 0000000000000000000000000000000000000000..1b92836b5f874c23eacc5a8d1993c6078a31859b GIT binary patch literal 494180 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

-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

fAU5ydFo}n;|+g+4xSOP-Dpk$pSkHcAN=nh;$U8 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

^A#_p?V|_Yp+3*VMg4N1CS5yan84WHG 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 X!<}9;-!T4E00000NkvXXu0mjffbR1^ literal 0 HcmV?d00001 From 347bb38214f88acec7f579218664c358f3f1ca81 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 25 Nov 2025 11:01:54 +0900 Subject: [PATCH 09/53] =?UTF-8?q?=EC=8A=A4=ED=94=84=EB=A6=B0=ED=8A=B84=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..ba541f49 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +## 기본 요구 사항 + +### 공통 +- [x] PostgreSQL를 이용해 주세요. +- [x] 데이터 모델 간의 관계를 고려하여 onDelete를 설정해 주세요. +- [x] 데이터베이스 시딩 코드를 작성해 주세요. +- [x] 각 API에 적절한 에러 처리를 해 주세요. +- [x] 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요. + +### 중고마켓 +- [x] Product 스키마를 작성해 주세요. +- [x] id, name, description, price, tags, createdAt, updatedAt필드를 가집니다. +- [x] 필요한 필드가 있다면 자유롭게 추가해 주세요. +- [x] 상품 등록 API를 만들어 주세요. +- [x] name, description, price, tags를 입력하여 상품을 등록합니다. +- [x] 상품 상세 조회 API를 만들어 주세요. +- [x] id, name, description, price, tags, createdAt를 조회합니다. +- [x] 상품 수정 API를 만들어 주세요. +- [x] PATCH 메서드를 사용해 주세요. +- [x] 상품 삭제 API를 만들어 주세요. +- [x] 상품 목록 조회 API를 만들어 주세요. +- [x] id, name, price, createdAt를 조회합니다. +- [x] offset 방식의 페이지네이션 기능을 포함해 주세요. +- [x] 최신순(recent)으로 정렬할 수 있습니다. +- [x] name, description에 포함된 단어로 검색할 수 있습니다. +- [x] 각 API에 적절한 에러 처리를 해 주세요. +- [x] 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요. + +### 자유게시판 +- [x] Article 스키마를 작성해 주세요. +- [x] id, title, content, createdAt, updatedAt 필드를 가집니다. +- [x] 게시글 등록 API를 만들어 주세요. +- [x] title, content를 입력해 게시글을 등록합니다. +- [x] 게시글 상세 조회 API를 만들어 주세요. +- [x] id, title, content, createdAt를 조회합니다. +- [x] 게시글 수정 API를 만들어 주세요. +- [x] 게시글 삭제 API를 만들어 주세요. +- [x] 게시글 목록 조회 API를 만들어 주세요. +- [x] id, title, content, createdAt를 조회합니다. +- [x] offset 방식의 페이지네이션 기능을 포함해 주세요. +- [x] 최신순(recent)으로 정렬할 수 있습니다. +- [x] title, content에 포함된 단어로 검색할 수 있습니다. + +### 댓글 +- [x] 댓글 등록 API를 만들어 주세요. +- [x] content를 입력하여 댓글을 등록합니다. +- [x] 중고마켓, 자유게시판 댓글 등록 API를 따로 만들어 주세요. +- [x] 댓글 수정 API를 만들어 주세요. +- [x] PATCH 메서드를 사용해 주세요. +- [x] 댓글 삭제 API를 만들어 주세요. +- [x] 댓글 목록 조회 API를 만들어 주세요. +- [x] id, content, createdAt 를 조회합니다. +- [x] cursor 방식의 페이지네이션 기능을 포함해 주세요. +- [x] 중고마켓, 자유게시판 댓글 목록 조회 API를 따로 만들어 주세요. + +### 유효성 검증 +- [x] 상품 등록 시 필요한 필드(이름, 설명, 가격 등)의 유효성을 검증하는 미들웨어를 구현합니다. +- [x] 게시물 등록 시 필요한 필드(제목, 내용 등)의 유효성 검증하는 미들웨어를 구현합니다. + +### 이미지 업로드 +- [x] multer 미들웨어를 사용하여 이미지 업로드 API를 구현해주세요. +- [x] 업로드된 이미지는 서버에 저장하고, 해당 이미지의 경로를 response 객체에 포함해 반환합니다. + +### 에러 처리 +- [x] 모든 예외 상황을 처리할 수 있는 에러 핸들러 미들웨어를 구현합니다. +- [x] 서버 오류(500), 사용자 입력 오류(400 시리즈), 리소스 찾을 수 없음(404) 등 상황에 맞는 상태값을 반환합니다. + +### 라우트 중복 제거 +- [x] 중복되는 라우트 경로(예: /users에 대한 get 및 post 요청)를 app.route()로 통합해 중복을 제거합니다. +- [x] express.Router()를 활용하여 중고마켓/자유게시판 관련 라우트를 별도의 모듈로 구분합니다. + +### 배포 +- [x] .env 파일에 환경 변수를 설정해 주세요. +- [x] CORS를 설정해 주세요. +- [x] [render.com](https://render.com/)으로 배포해 주세요. + + +## 멘토님께 +1. 댓글 구현 요구 사항 중 cursor 방식의 페이지네이션 기능 관련하여 검색 후 구현하였으나 요구 사항에 맞는 구현이 된건지 확인이 필요합니다. + From 8f13cd515154793b08e349b4ed0a1de31b47962b Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 25 Nov 2025 16:11:03 +0900 Subject: [PATCH 10/53] =?UTF-8?q?[FIX]=20=EC=97=90=EB=9F=AC=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC,=20prismaclient=20=EC=9D=B8=EC=8A=A4?= =?UTF-8?q?=ED=84=B4=EC=8A=A4=20=ED=8C=A8=ED=84=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 + src/Routers/articleRouter.js | 27 ++--- src/Routers/commentRouter.js | 27 ++--- src/Routers/productRouter.js | 28 ++---- src/Routers/uploadRouter.js | 5 +- src/Routers/userRouter.js | 9 ++ src/apis/commentapi.js | 120 ----------------------- src/apis/productapi.js | 91 ----------------- src/apis/uploadapi.js | 8 -- src/{apis => controller}/articleapi.js | 13 ++- src/controller/commentapi.js | 130 +++++++++++++++++++++++++ src/controller/productapi.js | 103 ++++++++++++++++++++ src/controller/uploadapi.js | 10 ++ src/controller/userApi.js | 7 ++ src/libs/Handler/errorHandler.js | 78 +++++++++++++++ src/libs/catchAsync.js | 3 + src/libs/constants.js | 4 +- src/main.js | 4 + src/services/userService.js | 0 19 files changed, 380 insertions(+), 290 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/Routers/userRouter.js delete mode 100644 src/apis/commentapi.js delete mode 100644 src/apis/productapi.js delete mode 100644 src/apis/uploadapi.js rename src/{apis => controller}/articleapi.js (84%) create mode 100644 src/controller/commentapi.js create mode 100644 src/controller/productapi.js create mode 100644 src/controller/uploadapi.js create mode 100644 src/controller/userApi.js create mode 100644 src/libs/Handler/errorHandler.js create mode 100644 src/libs/catchAsync.js create mode 100644 src/services/userService.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..97f0e81d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "prisma.pinToPrisma6": true +} diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index b2ff514e..b8da6541 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,38 +1,25 @@ import express from 'express'; -import { Prisma } from '@prisma/client'; +import catchAsync from './../libs/catchAsync.js'; import { GetArticle, PostArticle, GetArticleById, PatchArticleById, DeleteArticleById -} from '../apis/articleapi.js'; +} from '../controller/articleapi.js'; const articleRouter = express.Router(); articleRouter.route('/') - .get(GetArticle) - .post(PostArticle); + .get(catchAsync(GetArticle)) + .post(catchAsync(PostArticle)); articleRouter.route('/:id') - .get(GetArticleById) - .patch(PatchArticleById) - .delete(DeleteArticleById); + .get(catchAsync(GetArticleById)) + .patch(catchAsync(PatchArticleById)) + .delete(catchAsync(DeleteArticleById)); -articleRouter.use((err, req, res, next) => { - console.error(err.name); - if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { - res.sendStatus(404); - } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { - res.status(400).send({ message: err.message }); - } else if (err.name == "StructError") { - res.status(400).send({ message: err.message }); - } else { - res.status(500).send({ message: err.message }); - } -}); - export default articleRouter; \ No newline at end of file diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js index 52140988..1d4753bd 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.js @@ -1,36 +1,23 @@ import express from 'express'; -import { Prisma } from '@prisma/client'; +import catchAsync from './../libs/catchAsync.js'; import { GetComment, PostComment, GetCommentById, PatchCommentById, DeleteCommentById -} from '../apis/commentapi.js'; +} from '../controller/commentapi.js'; const commentRouter = express.Router(); commentRouter.route('/') - .get(GetComment) - .post(PostComment); + .get(catchAsync(GetComment)) + .post(catchAsync(PostComment)); commentRouter.route('/:id') - .get(GetCommentById) - .patch(PatchCommentById) - .delete(DeleteCommentById); + .get(catchAsync(GetCommentById)) + .patch(catchAsync(PatchCommentById)) + .delete(catchAsync(DeleteCommentById)); -commentRouter.use((err, req, res, next) => { - console.error(err.name); - if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { - res.sendStatus(404); - } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { - res.status(400).send({ message: err.message }); - } else if (err.name == "StructError") { - res.status(400).send({ message: err.message }); - } else { - res.status(500).send({ message: err.message }); - } -}); - export default commentRouter; \ No newline at end of file diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index 58625e52..14e5d7ee 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -1,38 +1,24 @@ import express from 'express'; -import { Prisma } from '@prisma/client'; +import catchAsync from './../libs/catchAsync.js'; import { GetProduct, PostProduct, GetProductById, PatchProductById, DeleteProductById -} from '../apis/productapi.js'; +} from '../controller/productapi.js'; const productRouter = express.Router(); productRouter.route('/') - .get(GetProduct) - .post(PostProduct); + .get(catchAsync(GetProduct)) + .post(catchAsync(PostProduct)); productRouter.route('/:id') - .get(GetProductById) - .patch(PatchProductById) - .delete(DeleteProductById); - - -productRouter.use((err, req, res, next) => { - console.error(err.name); - if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2025") { - res.sendStatus(404); - } else if (err instanceof Prisma.PrismaClientKnownRequestError && err.code == "P2002") { - res.status(400).send({ message: err.message }); - } else if (err.name == "StructError") { - res.status(400).send({ message: err.message }); - } else { - res.status(500).send({ message: err.message }); - } -}); + .get(catchAsync(GetProductById)) + .patch(catchAsync(PatchProductById)) + .delete(catchAsync(DeleteProductById)); export default productRouter; \ No newline at end of file diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.js index f8634cf8..4b77a288 100644 --- a/src/Routers/uploadRouter.js +++ b/src/Routers/uploadRouter.js @@ -1,15 +1,16 @@ import express from 'express'; +import catchAsync from './../libs/catchAsync.js'; import multer from 'multer'; import { UploadSingleImage -} from '../apis/uploadapi.js'; +} from '../controller/uploadapi.js'; const uploadRouter = express.Router(); const upload = multer({ dest: 'upload/' }); uploadRouter.post('/', upload.single('attachment'), - UploadSingleImage); + catchAsync(UploadSingleImage)); uploadRouter.use('/', express.static('upload')); diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js new file mode 100644 index 00000000..4daf7129 --- /dev/null +++ b/src/Routers/userRouter.js @@ -0,0 +1,9 @@ +import express from 'express'; +import catchAsync from './../libs/catchAsync.js'; +import { register } from '../controller/userApi'; + +const userRouter = express.Router(); + + + +export default userRouter; \ No newline at end of file diff --git a/src/apis/commentapi.js b/src/apis/commentapi.js deleted file mode 100644 index 20923e16..00000000 --- a/src/apis/commentapi.js +++ /dev/null @@ -1,120 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { assert } from 'superstruct'; -import { CreateComment, PatchComment } from '../libs/structs.js'; - - -const prisma = new PrismaClient(); - -export async function GetComment(req, res) { - const { productId, articleId } = req.query; - const { take = '10', cursor } = req.query; - const parsedTake = parseInt(take, 10); - - if (isNaN(parsedTake) || parsedTake <= 0) { - return res.status(400).send({ error: 'Invalid "take" parameter.' }); - } - - const whereClause = {}; - - if (productId) { - whereClause.productId = productId; // 상품 ID로 필터링 - } else if (articleId) { - whereClause.articleId = articleId; // 게시글 ID로 필터링 - } - - const findOptions = { - take: parsedTake, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, - }; - - if (cursor) { - findOptions.skip = 1; - findOptions.cursor = { - id: cursor, - }; - } - - const comments = await prisma.comment.findMany(findOptions); // - let nextCursor = null; - if (comments.length === parsedTake) { - nextCursor = comments[comments.length - 1].id; - } - res.status(200).json({ - comments, - nextCursor, - }); -} - - -export async function GetCommentById(req, res) { - const { id } = req.params; - const Comment = await prisma.comment.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(Comment); -} - - -export async function PostComment(req, res) { - assert(req.body, CreateComment); - const { content, productId, articleId } = req.body; - console.log("====================="); - console.log(content); - console.log(productId); - console.log(articleId); - console.log("====================="); - if ((productId && articleId) || (!productId && !articleId)) { - return res.status(400).json({ - error: 'Comment must belong to EITHER a Product OR an Article.', - }); - } - if (!content || content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - - const comment = await prisma.comment.create({ - data: { - content: content, - productId: productId, - articleId: articleId, - }, - }); - res.status(201).send(comment); -} - -export async function PatchCommentById(req, res) { - const { id } = req.params; - assert(req.body, PatchComment); - const { content } = req.body; - - if (content !== undefined && content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - - const comment = await prisma.comment.update({ - where: { - id - }, - data: { - content: content, - }, - }); - res.send(comment); -} - -export async function DeleteCommentById(req, res) { - const { id } = req.params; - const Comment = await prisma.comment.delete({ - where: { - id - }, - }); - res.send(Comment); -} - - diff --git a/src/apis/productapi.js b/src/apis/productapi.js deleted file mode 100644 index 4a98cbdf..00000000 --- a/src/apis/productapi.js +++ /dev/null @@ -1,91 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { assert } from 'superstruct'; -import { CreateProduct, PatchProduct } from '../libs/structs.js'; - - -const prisma = new PrismaClient(); - -export async function GetProduct(req, res) { - const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - - const findOptions = { - where: { - name: { - contains: name, - }, - description: { - contains: description, - }, - }, - orderBy, - skip: parsedOffset, - }; - - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } - - const product = await prisma.product.findMany(findOptions); - res.send(product); -} - - -export async function GetProductById(req, res) { - const { id } = req.params; - const product = await prisma.product.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(product); -} - -export async function PostProduct(req, res) { - assert(req.body, CreateProduct); - const { ...userFields } = req.body; - const product = await prisma.product.create({ - data: { - ...userFields - }, - }); - res.status(201).send(product); -} - -export async function PatchProductById(req, res) { - const { id } = req.params; - assert(req.body, PatchProduct); - const { ...userFields } = req.body; - const Product = await prisma.product.update({ - where: { - id - }, - data: { - ...userFields - }, - }); - res.send(Product); -} - -export async function DeleteProductById(req, res) { - const { id } = req.params; - const Product = await prisma.product.delete({ - where: { - id - }, - }); - res.send(Product); -} \ No newline at end of file diff --git a/src/apis/uploadapi.js b/src/apis/uploadapi.js deleted file mode 100644 index d753a46f..00000000 --- a/src/apis/uploadapi.js +++ /dev/null @@ -1,8 +0,0 @@ - - -export function UploadSingleImage(req, res) { - console.log(req.file); - const { filename } = req.file; - const path = `files/${filename}`; - res.json({ path }); -} \ No newline at end of file diff --git a/src/apis/articleapi.js b/src/controller/articleapi.js similarity index 84% rename from src/apis/articleapi.js rename to src/controller/articleapi.js index d552593f..594f3ddf 100644 --- a/src/apis/articleapi.js +++ b/src/controller/articleapi.js @@ -1,9 +1,8 @@ -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; import { CreateArticle, PatchArticle } from '../libs/structs.js'; -const prisma = new PrismaClient(); export async function GetArticle(req, res) { const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; @@ -39,14 +38,14 @@ export async function GetArticle(req, res) { findOptions.take = parsedLimit; } - const article = await prisma.article.findMany(findOptions); + const article = await PrismaClient.article.findMany(findOptions); res.send(article); } export async function GetArticleById(req, res) { const { id } = req.params; - const article = await prisma.article.findUniqueOrThrow({ + const article = await PrismaClient.article.findUniqueOrThrow({ where: { id }, @@ -57,7 +56,7 @@ export async function GetArticleById(req, res) { export async function PostArticle(req, res) { assert(req.body, CreateArticle); const { ...userFields } = req.body; - const article = await prisma.article.create({ + const article = await PrismaClient.article.create({ data: { ...userFields }, @@ -69,7 +68,7 @@ export async function PatchArticleById(req, res) { const { id } = req.params; assert(req.body, PatchArticle); const { ...userFields } = req.body; - const article = await prisma.article.update({ + const article = await PrismaClient.article.update({ where: { id }, @@ -82,7 +81,7 @@ export async function PatchArticleById(req, res) { export async function DeleteArticleById(req, res) { const { id } = req.params; - const article = await prisma.article.delete({ + const article = await PrismaClient.article.delete({ where: { id }, diff --git a/src/controller/commentapi.js b/src/controller/commentapi.js new file mode 100644 index 00000000..11f2a329 --- /dev/null +++ b/src/controller/commentapi.js @@ -0,0 +1,130 @@ +import { PrismaClient } from '../libs/constants.js'; +import { assert } from 'superstruct'; +import { CreateComment, PatchComment } from '../libs/structs.js'; + + + +export async function GetComment(req, res, next) { + try { + const { productId, articleId } = req.query; + const { take = '10', cursor } = req.query; + const parsedTake = parseInt(take, 10); + + if (isNaN(parsedTake) || parsedTake <= 0) { + return res.status(400).send({ error: 'Invalid "take" parameter.' }); + } + + const whereClause = {}; + + if (productId) { + whereClause.productId = productId; // 상품 ID로 필터링 + } else if (articleId) { + whereClause.articleId = articleId; // 게시글 ID로 필터링 + } + + const findOptions = { + take: parsedTake, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, + }; + } + + const comments = await PrismaClient.comment.findMany(findOptions); // + let nextCursor = null; + if (comments.length === parsedTake) { + nextCursor = comments[comments.length - 1].id; + } + res.status(200).json({ + comments, + nextCursor, + }); + } + catch { next(); } + +} + + +export async function GetCommentById(req, res, next) { + try { + const { id } = req.params; + const Comment = await PrismaClient.comment.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(Comment); + } + catch { next(); } +} + + +export async function PostComment(req, res, next) { + try { + assert(req.body, CreateComment); + const { content, productId, articleId } = req.body; + if ((productId && articleId) || (!productId && !articleId)) { + return res.status(400).json({ + error: 'Comment must belong to EITHER a Product OR an Article.', + }); + } + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + + const comment = await PrismaClient.comment.create({ + data: { + content: content, + productId: productId, + articleId: articleId, + }, + }); + res.status(201).send(comment); + } + catch { next(); } +} + +export async function PatchCommentById(req, res, next) { + try { + const { id } = req.params; + assert(req.body, PatchComment); + const { content } = req.body; + + if (content !== undefined && content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + + const comment = await PrismaClient.comment.update({ + where: { + id + }, + data: { + content: content, + }, + }); + res.send(comment); + } + catch { next(); } +} + +export async function DeleteCommentById(req, res, next) { + try { + const { id } = req.params; + const Comment = await PrismaClient.comment.delete({ + where: { + id + }, + }); + res.send(Comment); + } + catch { next(); } +} + + diff --git a/src/controller/productapi.js b/src/controller/productapi.js new file mode 100644 index 00000000..bb227cb4 --- /dev/null +++ b/src/controller/productapi.js @@ -0,0 +1,103 @@ +import { PrismaClient } from '../libs/constants.js'; +import { assert } from 'superstruct'; +import { CreateProduct, PatchProduct } from '../libs/structs.js'; + +export async function GetProduct(req, res, next) { + try { + const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + + const findOptions = { + where: { + name: { + contains: name, + }, + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; + + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + + const product = await PrismaClient.product.findMany(findOptions); + res.send(product); + } + catch { next(); } +} + + +export async function GetProductById(req, res, next) { + try { + const { id } = req.params; + const product = await PrismaClient.product.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(product); + } + catch { next(); } +} + +export async function PostProduct(req, res, next) { + try { + assert(req.body, CreateProduct); + const { ...userFields } = req.body; + const product = await PrismaClient.product.create({ + data: { + ...userFields + }, + }); + res.status(201).send(product); + } + catch { next(); } +} + +export async function PatchProductById(req, res, next) { + try { + const { id } = req.params; + assert(req.body, PatchProduct); + const { ...userFields } = req.body; + const Product = await PrismaClient.product.update({ + where: { + id + }, + data: { + ...userFields + }, + }); + res.send(Product); + } + catch { next(); } +} + +export async function DeleteProductById(req, res, next) { + try { + const { id } = req.params; + const Product = await PrismaClient.product.delete({ + where: { + id + }, + }); + res.send(Product); + } + catch { next(); } +} \ No newline at end of file diff --git a/src/controller/uploadapi.js b/src/controller/uploadapi.js new file mode 100644 index 00000000..4df9c0a5 --- /dev/null +++ b/src/controller/uploadapi.js @@ -0,0 +1,10 @@ + + +export function UploadSingleImage(req, res, next) { + try { + const { filename } = req.file; + const path = `files/${filename}`; + res.json({ path }); + } + catch { next(); } +} \ No newline at end of file diff --git a/src/controller/userApi.js b/src/controller/userApi.js new file mode 100644 index 00000000..37eaa8bf --- /dev/null +++ b/src/controller/userApi.js @@ -0,0 +1,7 @@ + + + +export function register(req, res, next) { + +} + diff --git a/src/libs/Handler/errorHandler.js b/src/libs/Handler/errorHandler.js new file mode 100644 index 00000000..61852a7d --- /dev/null +++ b/src/libs/Handler/errorHandler.js @@ -0,0 +1,78 @@ +import multer from 'multer'; + +const errorHandler = (err, req, res, next) => { + console.error(`Error: ${err}`); + if (err.name === 'PrismaCluentInitializationError') + return res.status(500).json({ + success: false, + message: '데이터베이스 연결에 실패했씁니다. DATABASE_URL을 확인해주세요.' + }); + + if (err.code === 'P2002') + return res.status(409).json({ + success: false, + message: '중복된 데이터가 존재합니다.' + }); + if (err.code === 'P2025') { + return res.status(404).json({ + success: false, + message: '요청한 데이터를 찾을 수 없습니다.' + }); + } + if (err instanceof multer.MulterError) { + if (err.code === 'LIMIT_FILE_SIZE') { + return res.status(400).json({ + success: false, + message: '파일 크기가 너무 큽니다.', + }); + } + if (err.code === 'LIMIT_FILE_COUNT') { + return res.status(400).json({ + success: false, + message: '파일 개수가 너무 많습니다.', + }); + } + } + const statusCode = err.statusCode || 500; + const message = err.message || '서버 오류가 발생했습니다.'; + const path = err.path || null; + const response = { + success: false, + message, + path + }; + return res.status(statusCode).json(response); +}; + +export default errorHandler; + +/** + * 모든 커스텀 에러의 기본이 되는 클래스 + * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. + */ +export class CustomError extends Error { + constructor(message, statusCode = 500) { + super(message); + this.statusCode = statusCode; + // Error stack 추적을 위해 클래스 이름 설정 + this.name = this.constructor.name; + // 생성자 함수를 호출 스택에서 제외 + Error.captureStackTrace(this, this.constructor); + } +} + +export const globalErrorHandler = (err, req, res, next) => { + // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. + const statusCode = err.statusCode || 500; + const message = err.message || 'Internal Server Error'; + + // 에러를 콘솔에 기록 + console.error(`[${err.name} - ${statusCode}] ${err.message}`, err.stack); + + res.status(statusCode).json({ + status: 'error', + message: message, + // 개발 환경에서만 스택 트레이스를 포함할 수 있습니다. + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, + }); +}; \ No newline at end of file diff --git a/src/libs/catchAsync.js b/src/libs/catchAsync.js new file mode 100644 index 00000000..d169b373 --- /dev/null +++ b/src/libs/catchAsync.js @@ -0,0 +1,3 @@ +export const catchAsync = (fn) => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); +}; \ No newline at end of file diff --git a/src/libs/constants.js b/src/libs/constants.js index b70b83f9..67df1998 100644 --- a/src/libs/constants.js +++ b/src/libs/constants.js @@ -1,4 +1,6 @@ import * as dotenv from 'dotenv'; +import { PrismaClient } from '@prisma/client'; dotenv.config(); -export const PORT = process.env.PORT; \ No newline at end of file +export const PORT = process.env.PORT || 3000; +export const prismaClient = new PrismaClient; \ No newline at end of file diff --git a/src/main.js b/src/main.js index b6c5d4a0..2ab6f3e5 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,7 @@ import productRouter from './Routers/productRouter.js'; import articleRouter from './Routers/articleRouter.js'; import commentRouter from './Routers/commentRouter.js'; import uploadRouter from './Routers/uploadRouter.js'; +import errorHandler from './libs/Handler/errorHandler.js'; import cors from 'cors'; @@ -17,6 +18,9 @@ app.use('/articles', articleRouter); app.use('/comments', commentRouter); app.use('/files', uploadRouter); + +app.use(errorHandler); + app.listen(PORT, () => { console.log(`Server is running`); }); \ No newline at end of file diff --git a/src/services/userService.js b/src/services/userService.js new file mode 100644 index 00000000..e69de29b From f1aca6fa5efa1a698e4660963cd38d249956ce44 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 25 Nov 2025 16:12:07 +0900 Subject: [PATCH 11/53] =?UTF-8?q?[FIX]=20api=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20controller=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/{articleapi.js => articleController.js} | 0 src/controller/{commentapi.js => commentController.js} | 0 src/controller/{productapi.js => productaController.js} | 0 src/controller/{uploadapi.js => uploadController.js} | 0 src/controller/{userApi.js => userController.js} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/controller/{articleapi.js => articleController.js} (100%) rename src/controller/{commentapi.js => commentController.js} (100%) rename src/controller/{productapi.js => productaController.js} (100%) rename src/controller/{uploadapi.js => uploadController.js} (100%) rename src/controller/{userApi.js => userController.js} (100%) diff --git a/src/controller/articleapi.js b/src/controller/articleController.js similarity index 100% rename from src/controller/articleapi.js rename to src/controller/articleController.js diff --git a/src/controller/commentapi.js b/src/controller/commentController.js similarity index 100% rename from src/controller/commentapi.js rename to src/controller/commentController.js diff --git a/src/controller/productapi.js b/src/controller/productaController.js similarity index 100% rename from src/controller/productapi.js rename to src/controller/productaController.js diff --git a/src/controller/uploadapi.js b/src/controller/uploadController.js similarity index 100% rename from src/controller/uploadapi.js rename to src/controller/uploadController.js diff --git a/src/controller/userApi.js b/src/controller/userController.js similarity index 100% rename from src/controller/userApi.js rename to src/controller/userController.js From d7d632e9abce25dd5359fffb5f2324c9777beea1 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 25 Nov 2025 16:44:56 +0900 Subject: [PATCH 12/53] =?UTF-8?q?[FIX]express=EC=9D=98=20=EC=8B=B1?= =?UTF-8?q?=EA=B8=80=ED=86=A4=ED=8C=A8=ED=84=B4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Routers/articleRouter.js | 6 +++--- src/Routers/commentRouter.js | 6 +++--- src/Routers/productRouter.js | 6 +++--- src/Routers/routerManager.js | 14 ++++++++++++++ src/Routers/uploadRouter.js | 8 ++++---- src/Routers/userRouter.js | 6 +++--- src/controller/articleController.js | 12 ++++++------ src/controller/commentController.js | 12 ++++++------ src/controller/productaController.js | 12 ++++++------ src/controller/userController.js | 3 --- src/libs/catchAsync.js | 6 ++++-- src/libs/constants.js | 4 +++- src/main.js | 19 ++++++------------- 13 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 src/Routers/routerManager.js diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index b8da6541..c331ee96 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,4 +1,4 @@ -import express from 'express'; +import { EXPRESS } from './libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetArticle, @@ -6,9 +6,9 @@ import { GetArticleById, PatchArticleById, DeleteArticleById -} from '../controller/articleapi.js'; +} from '../controller/articleController.js'; -const articleRouter = express.Router(); +const articleRouter = EXPRESS.Router(); diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js index 1d4753bd..87d3aeb7 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.js @@ -1,4 +1,4 @@ -import express from 'express'; +import { EXPRESS } from './libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetComment, @@ -6,9 +6,9 @@ import { GetCommentById, PatchCommentById, DeleteCommentById -} from '../controller/commentapi.js'; +} from '../controller/commentController.js'; -const commentRouter = express.Router(); +const commentRouter = EXPRESS.Router(); commentRouter.route('/') .get(catchAsync(GetComment)) diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index 14e5d7ee..6e2639dc 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -1,4 +1,4 @@ -import express from 'express'; +import { EXPRESS } from './libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetProduct, @@ -6,9 +6,9 @@ import { GetProductById, PatchProductById, DeleteProductById -} from '../controller/productapi.js'; +} from '../controller/productaController.js'; -const productRouter = express.Router(); +const productRouter = EXPRESS.Router(); diff --git a/src/Routers/routerManager.js b/src/Routers/routerManager.js new file mode 100644 index 00000000..b89515a2 --- /dev/null +++ b/src/Routers/routerManager.js @@ -0,0 +1,14 @@ +import { EXPRESS } from './libs/constants.js'; +import productRouter from './productRouter.js'; +import articleRouter from './articleRouter.js'; +import commentRouter from './commentRouter.js'; +import uploadRouter from './uploadRouter.js'; + +export const RouterManager = EXPRESS.Router(); + + + +RouterManager.use('/products', productRouter); +RouterManager.use('/articles', articleRouter); +RouterManager.use('/comments', commentRouter); +RouterManager.use('/files', uploadRouter); \ No newline at end of file diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.js index 4b77a288..ac244693 100644 --- a/src/Routers/uploadRouter.js +++ b/src/Routers/uploadRouter.js @@ -1,17 +1,17 @@ -import express from 'express'; +import { EXPRESS } from './libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import multer from 'multer'; import { UploadSingleImage -} from '../controller/uploadapi.js'; +} from '../controller/uploadController.js'; -const uploadRouter = express.Router(); +const uploadRouter = EXPRESS.Router(); const upload = multer({ dest: 'upload/' }); uploadRouter.post('/', upload.single('attachment'), catchAsync(UploadSingleImage)); -uploadRouter.use('/', express.static('upload')); +uploadRouter.use('/', EXPRESS.static('upload')); export default uploadRouter; diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 4daf7129..762e5753 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -1,8 +1,8 @@ -import express from 'express'; +import { EXPRESS } from './libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; -import { register } from '../controller/userApi'; +import { register } from '../controller/userController.js'; -const userRouter = express.Router(); +const userRouter = EXPRESS.Router(); diff --git a/src/controller/articleController.js b/src/controller/articleController.js index 594f3ddf..b454ce54 100644 --- a/src/controller/articleController.js +++ b/src/controller/articleController.js @@ -1,4 +1,4 @@ -import { PrismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; import { CreateArticle, PatchArticle } from '../libs/structs.js'; @@ -38,14 +38,14 @@ export async function GetArticle(req, res) { findOptions.take = parsedLimit; } - const article = await PrismaClient.article.findMany(findOptions); + const article = await prismaClient.article.findMany(findOptions); res.send(article); } export async function GetArticleById(req, res) { const { id } = req.params; - const article = await PrismaClient.article.findUniqueOrThrow({ + const article = await prismaClient.article.findUniqueOrThrow({ where: { id }, @@ -56,7 +56,7 @@ export async function GetArticleById(req, res) { export async function PostArticle(req, res) { assert(req.body, CreateArticle); const { ...userFields } = req.body; - const article = await PrismaClient.article.create({ + const article = await prismaClient.article.create({ data: { ...userFields }, @@ -68,7 +68,7 @@ export async function PatchArticleById(req, res) { const { id } = req.params; assert(req.body, PatchArticle); const { ...userFields } = req.body; - const article = await PrismaClient.article.update({ + const article = await prismaClient.article.update({ where: { id }, @@ -81,7 +81,7 @@ export async function PatchArticleById(req, res) { export async function DeleteArticleById(req, res) { const { id } = req.params; - const article = await PrismaClient.article.delete({ + const article = await prismaClient.article.delete({ where: { id }, diff --git a/src/controller/commentController.js b/src/controller/commentController.js index 11f2a329..174cd15b 100644 --- a/src/controller/commentController.js +++ b/src/controller/commentController.js @@ -1,4 +1,4 @@ -import { PrismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; import { CreateComment, PatchComment } from '../libs/structs.js'; @@ -37,7 +37,7 @@ export async function GetComment(req, res, next) { }; } - const comments = await PrismaClient.comment.findMany(findOptions); // + const comments = await prismaClient.comment.findMany(findOptions); // let nextCursor = null; if (comments.length === parsedTake) { nextCursor = comments[comments.length - 1].id; @@ -55,7 +55,7 @@ export async function GetComment(req, res, next) { export async function GetCommentById(req, res, next) { try { const { id } = req.params; - const Comment = await PrismaClient.comment.findUniqueOrThrow({ + const Comment = await prismaClient.comment.findUniqueOrThrow({ where: { id }, @@ -79,7 +79,7 @@ export async function PostComment(req, res, next) { return res.status(400).json({ error: 'Content cannot be empty.' }); } - const comment = await PrismaClient.comment.create({ + const comment = await prismaClient.comment.create({ data: { content: content, productId: productId, @@ -101,7 +101,7 @@ export async function PatchCommentById(req, res, next) { return res.status(400).json({ error: 'Content cannot be empty.' }); } - const comment = await PrismaClient.comment.update({ + const comment = await prismaClient.comment.update({ where: { id }, @@ -117,7 +117,7 @@ export async function PatchCommentById(req, res, next) { export async function DeleteCommentById(req, res, next) { try { const { id } = req.params; - const Comment = await PrismaClient.comment.delete({ + const Comment = await prismaClient.comment.delete({ where: { id }, diff --git a/src/controller/productaController.js b/src/controller/productaController.js index bb227cb4..624f585f 100644 --- a/src/controller/productaController.js +++ b/src/controller/productaController.js @@ -1,4 +1,4 @@ -import { PrismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; import { CreateProduct, PatchProduct } from '../libs/structs.js'; @@ -37,7 +37,7 @@ export async function GetProduct(req, res, next) { findOptions.take = parsedLimit; } - const product = await PrismaClient.product.findMany(findOptions); + const product = await prismaClient.product.findMany(findOptions); res.send(product); } catch { next(); } @@ -47,7 +47,7 @@ export async function GetProduct(req, res, next) { export async function GetProductById(req, res, next) { try { const { id } = req.params; - const product = await PrismaClient.product.findUniqueOrThrow({ + const product = await prismaClient.product.findUniqueOrThrow({ where: { id }, @@ -61,7 +61,7 @@ export async function PostProduct(req, res, next) { try { assert(req.body, CreateProduct); const { ...userFields } = req.body; - const product = await PrismaClient.product.create({ + const product = await prismaClient.product.create({ data: { ...userFields }, @@ -76,7 +76,7 @@ export async function PatchProductById(req, res, next) { const { id } = req.params; assert(req.body, PatchProduct); const { ...userFields } = req.body; - const Product = await PrismaClient.product.update({ + const Product = await prismaClient.product.update({ where: { id }, @@ -92,7 +92,7 @@ export async function PatchProductById(req, res, next) { export async function DeleteProductById(req, res, next) { try { const { id } = req.params; - const Product = await PrismaClient.product.delete({ + const Product = await prismaClient.product.delete({ where: { id }, diff --git a/src/controller/userController.js b/src/controller/userController.js index 37eaa8bf..d764a4ff 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -1,6 +1,3 @@ - - - export function register(req, res, next) { } diff --git a/src/libs/catchAsync.js b/src/libs/catchAsync.js index d169b373..06a87194 100644 --- a/src/libs/catchAsync.js +++ b/src/libs/catchAsync.js @@ -1,3 +1,5 @@ -export const catchAsync = (fn) => (req, res, next) => { +const catchAsync = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); -}; \ No newline at end of file +}; + +export default catchAsync; \ No newline at end of file diff --git a/src/libs/constants.js b/src/libs/constants.js index 67df1998..17e5cf9b 100644 --- a/src/libs/constants.js +++ b/src/libs/constants.js @@ -1,6 +1,8 @@ import * as dotenv from 'dotenv'; import { PrismaClient } from '@prisma/client'; +import express from 'express'; dotenv.config(); +export const EXPRESS = express; export const PORT = process.env.PORT || 3000; -export const prismaClient = new PrismaClient; \ No newline at end of file +export const prismaClient = new PrismaClient; diff --git a/src/main.js b/src/main.js index 2ab6f3e5..02690a3c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,22 +1,15 @@ -import express from 'express'; -import { PORT } from './libs/constants.js'; -import productRouter from './Routers/productRouter.js'; -import articleRouter from './Routers/articleRouter.js'; -import commentRouter from './Routers/commentRouter.js'; -import uploadRouter from './Routers/uploadRouter.js'; -import errorHandler from './libs/Handler/errorHandler.js'; +import { PORT, EXPRESS } from './libs/constants.js'; import cors from 'cors'; +import { RouterManager } from './Routers/routerManager.js'; +import errorHandler from './libs/Handler/errorHandler.js'; -const app = express(); +const app = EXPRESS(); app.use(cors()); -app.use(express.json()); +app.use(EXPRESS.json()); +app.use('/', RouterManager); -app.use('/products', productRouter); -app.use('/articles', articleRouter); -app.use('/comments', commentRouter); -app.use('/files', uploadRouter); app.use(errorHandler); From de07cd9c1ca07b2d89d2dcbc1c5b8ca1cc6274f8 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Wed, 26 Nov 2025 14:24:27 +0900 Subject: [PATCH 13/53] =?UTF-8?q?[FEAT]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md => sprint3-README.md | 12 +++++-- src/Routers/articleRouter.js | 2 +- src/Routers/commentRouter.js | 2 +- src/Routers/productRouter.js | 2 +- src/Routers/routerManager.js | 7 ++-- src/Routers/uploadRouter.js | 2 +- src/Routers/userRouter.js | 6 ++-- src/controller/userController.js | 17 +++++++++- src/libs/Handler/errorHandler.js | 3 +- src/libs/corsSetUp.js | 28 ++++++++++++++++ src/main.js | 19 +++++++++-- src/repositories/userRepository.js | 52 ++++++++++++++++++++++++++++++ src/services/userService.js | 38 ++++++++++++++++++++++ 13 files changed, 176 insertions(+), 14 deletions(-) rename README.md => sprint3-README.md (97%) create mode 100644 src/libs/corsSetUp.js create mode 100644 src/repositories/userRepository.js diff --git a/README.md b/sprint3-README.md similarity index 97% rename from README.md rename to sprint3-README.md index ba541f49..42102caa 100644 --- a/README.md +++ b/sprint3-README.md @@ -1,6 +1,7 @@ ## 기본 요구 사항 ### 공통 + - [x] PostgreSQL를 이용해 주세요. - [x] 데이터 모델 간의 관계를 고려하여 onDelete를 설정해 주세요. - [x] 데이터베이스 시딩 코드를 작성해 주세요. @@ -8,6 +9,7 @@ - [x] 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요. ### 중고마켓 + - [x] Product 스키마를 작성해 주세요. - [x] id, name, description, price, tags, createdAt, updatedAt필드를 가집니다. - [x] 필요한 필드가 있다면 자유롭게 추가해 주세요. @@ -27,6 +29,7 @@ - [x] 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요. ### 자유게시판 + - [x] Article 스키마를 작성해 주세요. - [x] id, title, content, createdAt, updatedAt 필드를 가집니다. - [x] 게시글 등록 API를 만들어 주세요. @@ -42,6 +45,7 @@ - [x] title, content에 포함된 단어로 검색할 수 있습니다. ### 댓글 + - [x] 댓글 등록 API를 만들어 주세요. - [x] content를 입력하여 댓글을 등록합니다. - [x] 중고마켓, 자유게시판 댓글 등록 API를 따로 만들어 주세요. @@ -54,27 +58,31 @@ - [x] 중고마켓, 자유게시판 댓글 목록 조회 API를 따로 만들어 주세요. ### 유효성 검증 + - [x] 상품 등록 시 필요한 필드(이름, 설명, 가격 등)의 유효성을 검증하는 미들웨어를 구현합니다. - [x] 게시물 등록 시 필요한 필드(제목, 내용 등)의 유효성 검증하는 미들웨어를 구현합니다. ### 이미지 업로드 + - [x] multer 미들웨어를 사용하여 이미지 업로드 API를 구현해주세요. - [x] 업로드된 이미지는 서버에 저장하고, 해당 이미지의 경로를 response 객체에 포함해 반환합니다. ### 에러 처리 + - [x] 모든 예외 상황을 처리할 수 있는 에러 핸들러 미들웨어를 구현합니다. - [x] 서버 오류(500), 사용자 입력 오류(400 시리즈), 리소스 찾을 수 없음(404) 등 상황에 맞는 상태값을 반환합니다. ### 라우트 중복 제거 + - [x] 중복되는 라우트 경로(예: /users에 대한 get 및 post 요청)를 app.route()로 통합해 중복을 제거합니다. - [x] express.Router()를 활용하여 중고마켓/자유게시판 관련 라우트를 별도의 모듈로 구분합니다. ### 배포 + - [x] .env 파일에 환경 변수를 설정해 주세요. - [x] CORS를 설정해 주세요. - [x] [render.com](https://render.com/)으로 배포해 주세요. - ## 멘토님께 -1. 댓글 구현 요구 사항 중 cursor 방식의 페이지네이션 기능 관련하여 검색 후 구현하였으나 요구 사항에 맞는 구현이 된건지 확인이 필요합니다. +1. 댓글 구현 요구 사항 중 cursor 방식의 페이지네이션 기능 관련하여 검색 후 구현하였으나 요구 사항에 맞는 구현이 된건지 확인이 필요합니다. diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index c331ee96..599452ad 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,4 +1,4 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetArticle, diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js index 87d3aeb7..cec00c75 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.js @@ -1,4 +1,4 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetComment, diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index 6e2639dc..69851afd 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -1,4 +1,4 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import { GetProduct, diff --git a/src/Routers/routerManager.js b/src/Routers/routerManager.js index b89515a2..3b1c053d 100644 --- a/src/Routers/routerManager.js +++ b/src/Routers/routerManager.js @@ -1,8 +1,10 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import productRouter from './productRouter.js'; import articleRouter from './articleRouter.js'; import commentRouter from './commentRouter.js'; import uploadRouter from './uploadRouter.js'; +import userRouter from './userRouter.js'; + export const RouterManager = EXPRESS.Router(); @@ -11,4 +13,5 @@ export const RouterManager = EXPRESS.Router(); RouterManager.use('/products', productRouter); RouterManager.use('/articles', articleRouter); RouterManager.use('/comments', commentRouter); -RouterManager.use('/files', uploadRouter); \ No newline at end of file +RouterManager.use('/files', uploadRouter); +RouterManager.use('/user', userRouter); diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.js index ac244693..c731f858 100644 --- a/src/Routers/uploadRouter.js +++ b/src/Routers/uploadRouter.js @@ -1,4 +1,4 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; import multer from 'multer'; import { diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 762e5753..2dec1f30 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -1,9 +1,11 @@ -import { EXPRESS } from './libs/constants.js'; +import { EXPRESS } from './../libs/constants.js'; import catchAsync from './../libs/catchAsync.js'; -import { register } from '../controller/userController.js'; +import { login, register } from '../controller/userController.js'; const userRouter = EXPRESS.Router(); +userRouter.post('/', catchAsync(register)); +userRouter.post('/login', catchAsync(login)); export default userRouter; \ No newline at end of file diff --git a/src/controller/userController.js b/src/controller/userController.js index d764a4ff..814e37a2 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -1,4 +1,19 @@ -export function register(req, res, next) { +import { userService } from '../services/userService.js'; + +export async function register(req, res, next) { + try { + const user = await userService.createUser(req.body); + return res.status(201).json(user); + } + catch (err) { next(err); } } +export async function login(req, res, next) { + try { + const { email, password } = req.body; + const user = await userService.getUser(email, password); + return res.status(200).json(user); + } + catch (err) { next(err); } +} diff --git a/src/libs/Handler/errorHandler.js b/src/libs/Handler/errorHandler.js index 61852a7d..81a0b492 100644 --- a/src/libs/Handler/errorHandler.js +++ b/src/libs/Handler/errorHandler.js @@ -51,11 +51,12 @@ export default errorHandler; * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. */ export class CustomError extends Error { - constructor(message, statusCode = 500) { + constructor(statusCode = 500, message = '', data = {}) { super(message); this.statusCode = statusCode; // Error stack 추적을 위해 클래스 이름 설정 this.name = this.constructor.name; + this.data = data; // 생성자 함수를 호출 스택에서 제외 Error.captureStackTrace(this, this.constructor); } diff --git a/src/libs/corsSetUp.js b/src/libs/corsSetUp.js new file mode 100644 index 00000000..ebc4611c --- /dev/null +++ b/src/libs/corsSetUp.js @@ -0,0 +1,28 @@ +import 'dotenv/config'; +export const getCorsOrigin = () => { + const corsOrigin = process.env.CORS_ORIGIN; + if (!corsOrigin || corsOrigin === '*') return '*'; + + if (corsOrigin.includes(',')) + return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); + + // 단일 origin (trailing slash 제거) + return corsOrigin.trim().replace(/\/$/, ''); +}; + +export const corsOriginChecker = (origin, callback) => { + const allowedOrigins = getCorsOrigin(); + if (allowedOrigins === '*') return callback(null, true); + + const origins = Array.isArray(allowedOrigins) ? allowedOrigins : [allowedOrigins]; + if (!origins) return callback(null, false); + + const normalizedOrigin = origin.replace(/\/$/, ''); + const isAllowed = origins.some((allowed) => { + const normalizedAllowed = allowed.replace(/\/$/, ''); + return normalizedOrigin === normalizedAllowed; + + }); + callback(null, isAllowed); + +}; \ No newline at end of file diff --git a/src/main.js b/src/main.js index 02690a3c..507288e4 100644 --- a/src/main.js +++ b/src/main.js @@ -1,12 +1,27 @@ import { PORT, EXPRESS } from './libs/constants.js'; import cors from 'cors'; import { RouterManager } from './Routers/routerManager.js'; +import { getCorsOrigin, corsOriginChecker } from './libs/corsSetUp.js'; import errorHandler from './libs/Handler/errorHandler.js'; - const app = EXPRESS(); -app.use(cors()); + + +app.use(cors({ + origin: getCorsOrigin(), + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], + allowedHeaders: ['Content-Type', 'Authorization'] +})); + + + app.use(EXPRESS.json()); +app.use(EXPRESS.urlencoded({ extended: true })); + +app.use('/upload', EXPRESS.static('upload')); + + app.use('/', RouterManager); diff --git a/src/repositories/userRepository.js b/src/repositories/userRepository.js new file mode 100644 index 00000000..0761eb28 --- /dev/null +++ b/src/repositories/userRepository.js @@ -0,0 +1,52 @@ +import { prismaClient } from '../libs/constants.js'; + +async function findById(id) { + return prismaClient.user.findUnique({ + where: { + id, + }, + }); +} + +async function findByEmail(email) { + return await prismaClient.user.findUnique({ + where: { + email, + }, + }); +} + +async function save(user) { + return prismaClient.user.create({ + data: { + email: user.email, + nickname: user.nickname, + password: user.password, + }, + }); +} + +async function update(id, data) { + return prismaClient.user.update({ + where: { + id, + }, + data: data, + }); +} + +async function createOrUpdate(provider, providerId, email, name) { + return prismaClient.user.upsert({ + where: { provider, providerId }, + update: { email, name }, + create: { provider, providerId, email, name }, + }); +} + +export default { + findById, + findByEmail, + save, + update, + createOrUpdate, +}; diff --git a/src/services/userService.js b/src/services/userService.js index e69de29b..14167d00 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -0,0 +1,38 @@ +import { CustomError } from '../libs/Handler/errorHandler.js'; +import userRepository from '../repositories/userRepository.js'; + + +class UserService { + async createUser(user) { + const existedUser = await userRepository.findByEmail(user.email); + if (existedUser) { + throw new CustomError(422, 'User already exists', { email: user.email }); + } + const createdUser = await userRepository.save({ ...user }); + return this.filterSensitivceUserData(createdUser); + } + + filterSensitivceUserData(user) { + const { password, ...rest } = user; + return rest; + } + + async getUser(email, password) { + const user = await userRepository.findByEmail(email); + if (!user) throw new CustomError(401, 'Unauthorized'); + + this.verifyPassword(password, user.password); + return this.filterSensitivceUserData(user); + } + + verifyPassword(inputPassword, password) { + const isMatch = inputPassword === password; + if (!isMatch) { + const error = new Error('Unauthorized'); + error.code = 401; + throw error; + } + } +} + +export const userService = new UserService(); \ No newline at end of file From c6e41ad552f83d700c056110c62e003a3d39c010 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Wed, 26 Nov 2025 14:42:11 +0900 Subject: [PATCH 14/53] =?UTF-8?q?[FEAT]=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=ED=95=B4=EC=8B=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 35 +++++++++++++++++++++++++++++++++++ package.json | 1 + src/services/userService.js | 21 ++++++++++++--------- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71e05921..fd9c38b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@prisma/client": "^6.18.0", + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", @@ -148,6 +149,20 @@ "dev": true, "license": "MIT" }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1151,12 +1166,32 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/node-fetch-native": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "license": "MIT" }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/nodemon": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", diff --git a/package.json b/package.json index 08482e5d..f747496a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@prisma/client": "^6.18.0", + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", diff --git a/src/services/userService.js b/src/services/userService.js index 14167d00..80465d06 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,5 +1,10 @@ import { CustomError } from '../libs/Handler/errorHandler.js'; import userRepository from '../repositories/userRepository.js'; +import bcrypt from 'bcrypt'; + +async function hashingPassword(password) { // 함수 추가 + return bcrypt.hash(password, 12); +} class UserService { @@ -8,7 +13,8 @@ class UserService { if (existedUser) { throw new CustomError(422, 'User already exists', { email: user.email }); } - const createdUser = await userRepository.save({ ...user }); + const hashedPassword = await hashingPassword(user.password); + const createdUser = await userRepository.save({ ...user, password: hashedPassword }); return this.filterSensitivceUserData(createdUser); } @@ -21,17 +27,14 @@ class UserService { const user = await userRepository.findByEmail(email); if (!user) throw new CustomError(401, 'Unauthorized'); - this.verifyPassword(password, user.password); + await this.verifyPassword(password, user.password); return this.filterSensitivceUserData(user); } - verifyPassword(inputPassword, password) { - const isMatch = inputPassword === password; - if (!isMatch) { - const error = new Error('Unauthorized'); - error.code = 401; - throw error; - } + async verifyPassword(inputPassword, savedPassword) { + const isValid = await bcrypt.compare(inputPassword, savedPassword); + if (!isValid) throw new CustomError(401, 'Unauthorized'); + } } From a8a7d74ce1d700aaf5fee83cc7774136d9fc43bb Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 27 Nov 2025 14:11:59 +0900 Subject: [PATCH 15/53] =?UTF-8?q?[feat]=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=ED=95=B4=EC=8B=B1,=20jwt=EA=B8=B0=EB=B0=98=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20accesstoken?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 +++++++ package-lock.json | 102 +++++++++++++++++- package.json | 1 + src/Routers/productRouter.js | 2 +- ...uctaController.js => productController.js} | 0 src/controller/userController.js | 3 +- src/services/userService.js | 6 ++ 7 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 README.md rename src/controller/{productaController.js => productController.js} (100%) diff --git a/README.md b/README.md new file mode 100644 index 00000000..7706d5bb --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +## 미션 목표 + +- [ ] 토큰 기반 유저 인증/인가 구현하기 +- [ ] (심화) Refresh Token 구현하기 +- [ ] (심화) Prisma로 관계형 활용하기 + +## 기본 요구사항 + +- [ ] 스프린트 미션 3의 구현이 완료된 상태에서 진행을 권장합니다. +- [ ] 가능하다면 Prisma 스키마를 수정할 때 Prisma 마이그레이션을 함께 진행해 보고, 잘되지 않는다면 마이그레이션 파일과 데이터베이스를 초기화하고 진행해도 좋습니다. + +### 인증 + +- [ ] User 스키마를 작성해 주세요. +- [ ] id, email, nickname, image, password, createdAt, updatedAt 필드를 가집니다. +- [ ] 회원가입 API를 만들어 주세요. +- [ ] email, nickname, password 를 입력하여 회원가입을 진행합니다. +- [ ] password는 해싱해 저장합니다. +- [ ] 토큰 기반 인증: 로그인에 성공하면 Access Token을 발급하는 기능을 구현합니다. ###상품 기능 인가 +- [ ] 로그인한 유저만 상품을 등록할 수 있습니다. +- [ ] 상품을 등록한 유저만 해당 상품의 정보를 수정하거나 삭제할 수 있습니다. ###게시글 기능 인가 +- [ ] 로그인한 유저만 게시글을 등록할 수 있습니다. +- [ ] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. ###댓글 기능 인가 +- [ ] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. +- [ ] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. +- [ ] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. ###유저 정보 +- [ ] 유저가 자신의 정보를 조회하는 기능을 구현합니다. +- [ ] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. +- [ ] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. +- [ ] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. +- [ ] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. ##심화 요구사항 ###인증 +- [ ] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. ###좋아요 기능 +- [ ] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [ ] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [ ] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. +- [ ] 유저가 '좋아요'를 표시한 상품의 목록을 조회하는 기능을 구현합니다. diff --git a/package-lock.json b/package-lock.json index fd9c38b8..eba6f50b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "express": "^5.1.0", "is-email": "^1.0.2", "is-uuid": "^1.0.2", + "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", "prisma": "^6.18.0", "superstruct": "^2.0.2" @@ -220,6 +221,12 @@ "node": ">=8" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -539,6 +546,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1005,6 +1021,91 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1552,7 +1653,6 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/package.json b/package.json index f747496a..9a23aedf 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "express": "^5.1.0", "is-email": "^1.0.2", "is-uuid": "^1.0.2", + "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", "prisma": "^6.18.0", "superstruct": "^2.0.2" diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index 69851afd..cc3423b2 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -6,7 +6,7 @@ import { GetProductById, PatchProductById, DeleteProductById -} from '../controller/productaController.js'; +} from '../controller/productController.js'; const productRouter = EXPRESS.Router(); diff --git a/src/controller/productaController.js b/src/controller/productController.js similarity index 100% rename from src/controller/productaController.js rename to src/controller/productController.js diff --git a/src/controller/userController.js b/src/controller/userController.js index 814e37a2..93b9ad38 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -13,7 +13,8 @@ export async function login(req, res, next) { try { const { email, password } = req.body; const user = await userService.getUser(email, password); - return res.status(200).json(user); + const accessToken = userService.createToken(user); + return res.status(200).json({ accessToken }); } catch (err) { next(err); } } diff --git a/src/services/userService.js b/src/services/userService.js index 80465d06..abf7c9aa 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,6 +1,7 @@ import { CustomError } from '../libs/Handler/errorHandler.js'; import userRepository from '../repositories/userRepository.js'; import bcrypt from 'bcrypt'; +import jwt from 'jsonwebtoken'; async function hashingPassword(password) { // 함수 추가 return bcrypt.hash(password, 12); @@ -36,6 +37,11 @@ class UserService { if (!isValid) throw new CustomError(401, 'Unauthorized'); } + createToken(user) { + const payload = { userId: user.id }; + const options = { expiresIn: '1h' }; + return jwt.sign(payload, process.env.JWT_SECRET, options); + } } export const userService = new UserService(); \ No newline at end of file From 8ebb91a409afbb9e6a06c60d5f20c8877ad73c29 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 27 Nov 2025 16:16:52 +0900 Subject: [PATCH 16/53] =?UTF-8?q?[FEAT]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EB=A7=8C=20=EC=83=81=ED=92=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D,=20=EC=88=98=EC=A0=95,=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=93=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 52 +++++++ package.json | 1 + src/Routers/articleRouter.js | 2 +- src/Routers/commentRouter.js | 2 +- src/Routers/productRouter.js | 9 +- src/Routers/uploadRouter.js | 2 +- src/Routers/userRouter.js | 2 +- src/controller/commentController.js | 193 ++++++++++++-------------- src/controller/productController.js | 144 ++++++++----------- src/controller/uploadController.js | 11 +- src/controller/userController.js | 22 ++- src/libs/Handler/errorHandler.js | 3 +- src/libs/catchAsync.js | 5 +- src/libs/structs.js | 5 +- src/middlewares/auth.js | 31 +++++ src/repositories/productRepository.js | 43 ++++++ 16 files changed, 303 insertions(+), 224 deletions(-) create mode 100644 src/middlewares/auth.js create mode 100644 src/repositories/productRepository.js diff --git a/package-lock.json b/package-lock.json index eba6f50b..98a37f71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", + "express-jwt": "^8.5.1", "is-email": "^1.0.2", "is-uuid": "^1.0.2", "jsonwebtoken": "^9.0.2", @@ -110,6 +111,31 @@ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -676,6 +702,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-jwt": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz", + "integrity": "sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==", + "license": "MIT" + }, "node_modules/exsolve": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", @@ -1902,6 +1948,12 @@ "dev": true, "license": "MIT" }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 9a23aedf..70a2a6da 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", + "express-jwt": "^8.5.1", "is-email": "^1.0.2", "is-uuid": "^1.0.2", "jsonwebtoken": "^9.0.2", diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index 599452ad..61e9af39 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,5 +1,5 @@ import { EXPRESS } from './../libs/constants.js'; -import catchAsync from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import { GetArticle, PostArticle, diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js index cec00c75..c9bc7a38 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.js @@ -1,5 +1,5 @@ import { EXPRESS } from './../libs/constants.js'; -import catchAsync from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import { GetComment, PostComment, diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index cc3423b2..22419b80 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -1,5 +1,6 @@ import { EXPRESS } from './../libs/constants.js'; -import catchAsync from './../libs/catchAsync.js'; +import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; +import auth from './../middlewares/auth.js'; import { GetProduct, PostProduct, @@ -14,11 +15,11 @@ const productRouter = EXPRESS.Router(); productRouter.route('/') .get(catchAsync(GetProduct)) - .post(catchAsync(PostProduct)); + .post(auth.verifyAccessToken, catchAsync(PostProduct)); productRouter.route('/:id') .get(catchAsync(GetProductById)) - .patch(catchAsync(PatchProductById)) - .delete(catchAsync(DeleteProductById)); + .patch(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, PatchProductById)) + .delete(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, DeleteProductById)); export default productRouter; \ No newline at end of file diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.js index c731f858..71d750d1 100644 --- a/src/Routers/uploadRouter.js +++ b/src/Routers/uploadRouter.js @@ -1,5 +1,5 @@ import { EXPRESS } from './../libs/constants.js'; -import catchAsync from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import multer from 'multer'; import { UploadSingleImage diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 2dec1f30..191b09a5 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -1,5 +1,5 @@ import { EXPRESS } from './../libs/constants.js'; -import catchAsync from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import { login, register } from '../controller/userController.js'; const userRouter = EXPRESS.Router(); diff --git a/src/controller/commentController.js b/src/controller/commentController.js index 174cd15b..1f1ee3aa 100644 --- a/src/controller/commentController.js +++ b/src/controller/commentController.js @@ -4,127 +4,112 @@ import { CreateComment, PatchComment } from '../libs/structs.js'; -export async function GetComment(req, res, next) { - try { - const { productId, articleId } = req.query; - const { take = '10', cursor } = req.query; - const parsedTake = parseInt(take, 10); - - if (isNaN(parsedTake) || parsedTake <= 0) { - return res.status(400).send({ error: 'Invalid "take" parameter.' }); - } - - const whereClause = {}; - - if (productId) { - whereClause.productId = productId; // 상품 ID로 필터링 - } else if (articleId) { - whereClause.articleId = articleId; // 게시글 ID로 필터링 - } - - const findOptions = { - take: parsedTake, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, +export async function GetComment(req, res) { + const { productId, articleId } = req.query; + const { take = '10', cursor } = req.query; + const parsedTake = parseInt(take, 10); + + if (isNaN(parsedTake) || parsedTake <= 0) { + return res.status(400).send({ error: 'Invalid "take" parameter.' }); + } + + const whereClause = {}; + + if (productId) { + whereClause.productId = productId; // 상품 ID로 필터링 + } else if (articleId) { + whereClause.articleId = articleId; // 게시글 ID로 필터링 + } + + const findOptions = { + take: parsedTake, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, }; + } - if (cursor) { - findOptions.skip = 1; - findOptions.cursor = { - id: cursor, - }; - } - - const comments = await prismaClient.comment.findMany(findOptions); // - let nextCursor = null; - if (comments.length === parsedTake) { - nextCursor = comments[comments.length - 1].id; - } - res.status(200).json({ - comments, - nextCursor, - }); + const comments = await prismaClient.comment.findMany(findOptions); // + let nextCursor = null; + if (comments.length === parsedTake) { + nextCursor = comments[comments.length - 1].id; } - catch { next(); } + res.status(200).json({ + comments, + nextCursor, + }); } -export async function GetCommentById(req, res, next) { - try { - const { id } = req.params; - const Comment = await prismaClient.comment.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(Comment); - } - catch { next(); } +export async function GetCommentById(req, res) { + const { id } = req.params; + const Comment = await prismaClient.comment.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(Comment); } -export async function PostComment(req, res, next) { - try { - assert(req.body, CreateComment); - const { content, productId, articleId } = req.body; - if ((productId && articleId) || (!productId && !articleId)) { - return res.status(400).json({ - error: 'Comment must belong to EITHER a Product OR an Article.', - }); - } - if (!content || content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - - const comment = await prismaClient.comment.create({ - data: { - content: content, - productId: productId, - articleId: articleId, - }, +export async function PostComment(req, res) { + assert(req.body, CreateComment); + const { content, productId, articleId } = req.body; + if ((productId && articleId) || (!productId && !articleId)) { + return res.status(400).json({ + error: 'Comment must belong to EITHER a Product OR an Article.', }); - res.status(201).send(comment); } - catch { next(); } + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + + const comment = await prismaClient.comment.create({ + data: { + content: content, + productId: productId, + articleId: articleId, + }, + }); + res.status(201).send(comment); } -export async function PatchCommentById(req, res, next) { - try { - const { id } = req.params; - assert(req.body, PatchComment); - const { content } = req.body; - - if (content !== undefined && content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - - const comment = await prismaClient.comment.update({ - where: { - id - }, - data: { - content: content, - }, - }); - res.send(comment); +export async function PatchCommentById(req, res) { + const { id } = req.params; + assert(req.body, PatchComment); + const { content } = req.body; + + if (content !== undefined && content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); } - catch { next(); } + + const comment = await prismaClient.comment.update({ + where: { + id + }, + data: { + content: content, + }, + }); + res.send(comment); } -export async function DeleteCommentById(req, res, next) { - try { - const { id } = req.params; - const Comment = await prismaClient.comment.delete({ - where: { - id - }, - }); - res.send(Comment); - } - catch { next(); } +export async function DeleteCommentById(req, res) { + const { id } = req.params; + const Comment = await prismaClient.comment.delete({ + where: { + id + }, + }); + res.send(Comment); } diff --git a/src/controller/productController.js b/src/controller/productController.js index 624f585f..1aa7edf7 100644 --- a/src/controller/productController.js +++ b/src/controller/productController.js @@ -1,103 +1,81 @@ import { prismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; import { CreateProduct, PatchProduct } from '../libs/structs.js'; +import productRepository from '../repositories/productRepository.js'; +export async function GetProduct(req, res) { + const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); -export async function GetProduct(req, res, next) { - try { - const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - - const findOptions = { - where: { - name: { - contains: name, - }, - description: { - contains: description, - }, + const findOptions = { + where: { + name: { + contains: name, }, - orderBy, - skip: parsedOffset, - }; - - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; - const product = await prismaClient.product.findMany(findOptions); - res.send(product); + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; } - catch { next(); } + + const product = await prismaClient.product.findMany(findOptions); + res.send(product); } -export async function GetProductById(req, res, next) { - try { - const { id } = req.params; - const product = await prismaClient.product.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(product); +export async function GetProductById(req, res) { + const id = Number(req.params.id); + + if (isNaN(id)) { + throw new CustomError(400, 'ID는 숫자여야 합니다.'); } - catch { next(); } + const product = await productRepository.findById(id); + res.send(product); } -export async function PostProduct(req, res, next) { - try { - assert(req.body, CreateProduct); - const { ...userFields } = req.body; - const product = await prismaClient.product.create({ - data: { - ...userFields - }, - }); - res.status(201).send(product); - } - catch { next(); } +export async function PostProduct(req, res) { + assert(req.body, CreateProduct); + const { ...userFields } = req.body; + const product = await productRepository.create(userFields); + res.status(201).send(product); } -export async function PatchProductById(req, res, next) { - try { - const { id } = req.params; - assert(req.body, PatchProduct); - const { ...userFields } = req.body; - const Product = await prismaClient.product.update({ - where: { - id - }, - data: { - ...userFields - }, - }); - res.send(Product); +export async function PatchProductById(req, res) { + const id = Number(req.params.id); + + if (isNaN(id)) { + throw new CustomError(400, 'ID는 숫자여야 합니다.'); } - catch { next(); } + assert(req.body, PatchProduct); + const { ...userFields } = req.body; + const Product = await productRepository.update(id, userFields); + res.send(Product); } -export async function DeleteProductById(req, res, next) { - try { - const { id } = req.params; - const Product = await prismaClient.product.delete({ - where: { - id - }, - }); - res.send(Product); +export async function DeleteProductById(req, res) { + const id = Number(req.params.id); + + if (isNaN(id)) { + throw new CustomError(400, 'ID는 숫자여야 합니다.'); } - catch { next(); } + const Product = await productRepository.ondelete(id); + res.send(Product); } \ No newline at end of file diff --git a/src/controller/uploadController.js b/src/controller/uploadController.js index 4df9c0a5..815bcccf 100644 --- a/src/controller/uploadController.js +++ b/src/controller/uploadController.js @@ -1,10 +1,7 @@ -export function UploadSingleImage(req, res, next) { - try { - const { filename } = req.file; - const path = `files/${filename}`; - res.json({ path }); - } - catch { next(); } +export function UploadSingleImage(req, res) { + const { filename } = req.file; + const path = `files/${filename}`; + res.json({ path }); } \ No newline at end of file diff --git a/src/controller/userController.js b/src/controller/userController.js index 93b9ad38..75ff894c 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -1,20 +1,14 @@ import { userService } from '../services/userService.js'; -export async function register(req, res, next) { - try { - const user = await userService.createUser(req.body); - return res.status(201).json(user); - } - catch (err) { next(err); } +export async function register(req, res) { + const user = await userService.createUser(req.body); + return res.status(201).json(user); } -export async function login(req, res, next) { - try { - const { email, password } = req.body; - const user = await userService.getUser(email, password); - const accessToken = userService.createToken(user); - return res.status(200).json({ accessToken }); - } - catch (err) { next(err); } +export async function login(req, res) { + const { email, password } = req.body; + const user = await userService.getUser(email, password); + const accessToken = userService.createToken(user); + return res.status(200).json({ accessToken }); } diff --git a/src/libs/Handler/errorHandler.js b/src/libs/Handler/errorHandler.js index 81a0b492..6b9e1cd6 100644 --- a/src/libs/Handler/errorHandler.js +++ b/src/libs/Handler/errorHandler.js @@ -1,7 +1,6 @@ import multer from 'multer'; const errorHandler = (err, req, res, next) => { - console.error(`Error: ${err}`); if (err.name === 'PrismaCluentInitializationError') return res.status(500).json({ success: false, @@ -33,7 +32,7 @@ const errorHandler = (err, req, res, next) => { }); } } - const statusCode = err.statusCode || 500; + const statusCode = err.statusCode || err.status || 500; const message = err.message || '서버 오류가 발생했습니다.'; const path = err.path || null; const response = { diff --git a/src/libs/catchAsync.js b/src/libs/catchAsync.js index 06a87194..0409bd01 100644 --- a/src/libs/catchAsync.js +++ b/src/libs/catchAsync.js @@ -1,5 +1,4 @@ -const catchAsync = (fn) => (req, res, next) => { +export const catchAsync = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; - -export default catchAsync; \ No newline at end of file +export const catchAsyncAll = (...fns) => fns.map(catchAsync); \ No newline at end of file diff --git a/src/libs/structs.js b/src/libs/structs.js index ddd86136..57eacbdd 100644 --- a/src/libs/structs.js +++ b/src/libs/structs.js @@ -1,5 +1,4 @@ import * as s from "superstruct"; -import isUuid from "is-uuid"; export const CreateProduct = s.object({ @@ -17,8 +16,8 @@ export const CreateArticle = s.object({ export const CreateComment = s.refine(s.object({ content: s.string(), - productId: s.optional(s.define('Uuid', (value) => isUuid.v4(value))), - articleId: s.optional(s.define('Uuid', (value) => isUuid.v4(value))), + productId: s.optional(s.number()), + articleId: s.optional(s.number()), }), 'EitherProductIdOrArticleId',//검증 규칙 이름 (value) => { diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js new file mode 100644 index 00000000..f7ee3188 --- /dev/null +++ b/src/middlewares/auth.js @@ -0,0 +1,31 @@ +import { expressjwt } from 'express-jwt'; +import productRepository from '../repositories/productRepository.js'; +import { CustomError } from '../libs/Handler/errorHandler.js'; + +const verifyAccessToken = expressjwt({ + secret: process.env.JWT_SECRET, + algorithms: ["HS256"], + requestProperty: 'user' +}); + +async function verifyProduectAuth(req, res, next) { + const id = Number(req.params.id); + + if (isNaN(id)) { + throw new CustomError(400, "ID는 숫자여야 합니다."); + } + + const product = await productRepository.findById(id); + if (!product) { + throw new CustomError(404, 'product not found'); + } + if (product.authorId !== req.user.id) { + throw new CustomError(403, 'Forbidden'); + } + return next(); +}; + +export default { + verifyAccessToken, + verifyProduectAuth, +}; \ No newline at end of file diff --git a/src/repositories/productRepository.js b/src/repositories/productRepository.js new file mode 100644 index 00000000..36826813 --- /dev/null +++ b/src/repositories/productRepository.js @@ -0,0 +1,43 @@ +import { prismaClient } from '../libs/constants.js'; + +async function findById(id) { + return prismaClient.product.findUniqueOrThrow({ + where: { + id, + }, + }); +} + +async function update(id, data) { + return prismaClient.product.update({ + where: { + id + }, + data: { + ...data + }, + }); +} + +async function create(userFields) { + return await prismaClient.product.create({ + data: { + ...userFields + }, + }); +} + +async function ondelete(id) { + return await prismaClient.product.delete({ + where: { + id + }, + }); +} + +export default { + findById, + update, + create, + ondelete, +}; From 573f68f61d4e5a8601c81db9ef5cc7acd69bb6eb Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 27 Nov 2025 18:09:42 +0900 Subject: [PATCH 17/53] =?UTF-8?q?[FEAT]=20refresh=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 50 ++++++++++++++++++++++---------- src/Routers/userRouter.js | 4 ++- src/controller/userController.js | 22 +++++++++++++- src/middlewares/auth.js | 7 ++++- src/services/userService.js | 21 +++++++++++--- 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 7706d5bb..2268042a 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,55 @@ ## 미션 목표 -- [ ] 토큰 기반 유저 인증/인가 구현하기 +- [x] 토큰 기반 유저 인증/인가 구현하기 - [ ] (심화) Refresh Token 구현하기 - [ ] (심화) Prisma로 관계형 활용하기 ## 기본 요구사항 -- [ ] 스프린트 미션 3의 구현이 완료된 상태에서 진행을 권장합니다. -- [ ] 가능하다면 Prisma 스키마를 수정할 때 Prisma 마이그레이션을 함께 진행해 보고, 잘되지 않는다면 마이그레이션 파일과 데이터베이스를 초기화하고 진행해도 좋습니다. +- [x] 스프린트 미션 3의 구현이 완료된 상태에서 진행을 권장합니다. +- [x] 가능하다면 Prisma 스키마를 수정할 때 Prisma 마이그레이션을 함께 진행해 보고, 잘되지 않는다면 마이그레이션 파일과 데이터베이스를 초기화하고 진행해도 좋습니다. ### 인증 -- [ ] User 스키마를 작성해 주세요. -- [ ] id, email, nickname, image, password, createdAt, updatedAt 필드를 가집니다. -- [ ] 회원가입 API를 만들어 주세요. -- [ ] email, nickname, password 를 입력하여 회원가입을 진행합니다. -- [ ] password는 해싱해 저장합니다. -- [ ] 토큰 기반 인증: 로그인에 성공하면 Access Token을 발급하는 기능을 구현합니다. ###상품 기능 인가 -- [ ] 로그인한 유저만 상품을 등록할 수 있습니다. -- [ ] 상품을 등록한 유저만 해당 상품의 정보를 수정하거나 삭제할 수 있습니다. ###게시글 기능 인가 +- [x] User 스키마를 작성해 주세요. +- [x] id, email, nickname, image, password, createdAt, updatedAt 필드를 가집니다. +- [x] 회원가입 API를 만들어 주세요. +- [x] email, nickname, password 를 입력하여 회원가입을 진행합니다. +- [x] password는 해싱해 저장합니다. +- [x] 토큰 기반 인증: 로그인에 성공하면 Access Token을 발급하는 기능을 구현합니다. + +###상품 기능 인가 + +- [x] 로그인한 유저만 상품을 등록할 수 있습니다. +- [x] 상품을 등록한 유저만 해당 상품의 정보를 수정하거나 삭제할 수 있습니다. + +###게시글 기능 인가 + - [ ] 로그인한 유저만 게시글을 등록할 수 있습니다. -- [ ] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. ###댓글 기능 인가 +- [ ] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. + +###댓글 기능 인가 + - [ ] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. - [ ] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. -- [ ] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. ###유저 정보 +- [ ] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. + +###유저 정보 + - [ ] 유저가 자신의 정보를 조회하는 기능을 구현합니다. - [ ] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. - [ ] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. - [ ] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. -- [ ] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. ##심화 요구사항 ###인증 -- [ ] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. ###좋아요 기능 +- [ ] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. + +##심화 요구사항 + +###인증 + +- [ ] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. + +###좋아요 기능 + - [ ] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. - [ ] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. - [ ] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 191b09a5..58efe3d5 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -1,11 +1,13 @@ import { EXPRESS } from './../libs/constants.js'; import { catchAsync } from './../libs/catchAsync.js'; -import { login, register } from '../controller/userController.js'; +import { login, register, refresh } from '../controller/userController.js'; +import auth from '../middlewares/auth.js'; const userRouter = EXPRESS.Router(); userRouter.post('/', catchAsync(register)); userRouter.post('/login', catchAsync(login)); +userRouter.post('/token/refresh', auth.verifyRefreshToken, catchAsync(refresh)); export default userRouter; \ No newline at end of file diff --git a/src/controller/userController.js b/src/controller/userController.js index 75ff894c..3457c5af 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -10,5 +10,25 @@ export async function login(req, res) { const { email, password } = req.body; const user = await userService.getUser(email, password); const accessToken = userService.createToken(user); + const refreshToken = userService.createToken(user, 'refresh'); + await userService.updateUser(user.id, { refreshToken }); + res.cookie('refreshToken', refreshToken, { + httpOnly: true, + sameSite: 'none', + secure: true + }); return res.status(200).json({ accessToken }); -} +} +export async function refresh(req, res) { + const { refreshToken } = req.cookies; + const { userId } = req.auth; + const { accessToken, newRefreshToken } = await userService.refreshToken(userId, refreshToken); // 변경 + await userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 + res.cookie('refreshToken', newRefreshToken, { // 추가 + path: '/token/refresh', + httpOnly: true, + sameSite: 'none', + secure: true, + }); + return res.json({ accessToken }); +} \ No newline at end of file diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index f7ee3188..2e44d756 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -7,7 +7,11 @@ const verifyAccessToken = expressjwt({ algorithms: ["HS256"], requestProperty: 'user' }); - +const verifyRefreshToken = expressjwt({ + secret: process.env.JWT_SECRET, + algorithms: ['HS256'], + getToken: (req) => req.cookies.refreshToken, +}); async function verifyProduectAuth(req, res, next) { const id = Number(req.params.id); @@ -28,4 +32,5 @@ async function verifyProduectAuth(req, res, next) { export default { verifyAccessToken, verifyProduectAuth, + verifyRefreshToken, }; \ No newline at end of file diff --git a/src/services/userService.js b/src/services/userService.js index abf7c9aa..ef2f82d7 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -20,7 +20,7 @@ class UserService { } filterSensitivceUserData(user) { - const { password, ...rest } = user; + const { password, refreshToken, ...rest } = user; return rest; } @@ -31,17 +31,30 @@ class UserService { await this.verifyPassword(password, user.password); return this.filterSensitivceUserData(user); } - + async updateUser(id, data) { + return await userRepository.update(id, data); + } async verifyPassword(inputPassword, savedPassword) { const isValid = await bcrypt.compare(inputPassword, savedPassword); if (!isValid) throw new CustomError(401, 'Unauthorized'); } - createToken(user) { + createToken(user, type) { const payload = { userId: user.id }; - const options = { expiresIn: '1h' }; + const options = { + expiresIn: type === 'refresh' ? '1d' : '10m', + }; return jwt.sign(payload, process.env.JWT_SECRET, options); } + async refreshToken(userId, refreshToken) { + const user = await userRepository.findById(userId); + if (!user || user.refreshToken !== refreshToken) { + throw new CustomError(401, 'Unauthorized'); + } + const accessToken = createToken(user); // 변경 + const newRefreshToken = createToken(user, 'refresh'); // 추가 + return { accessToken, newRefreshToken }; // 변경 + } } export const userService = new UserService(); \ No newline at end of file From 79cddba7d6198167077f65cd1aa559127eacbd55 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 27 Nov 2025 18:18:35 +0900 Subject: [PATCH 18/53] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8B=9C=EA=B8=80,=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EB=93=B1=EB=A1=9D=20=EC=9D=B8=EA=B0=80=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 --- README.md | 14 +++++++------- src/Routers/articleRouter.js | 9 +++++---- src/Routers/commentRouter.js | 9 +++++---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2268042a..d01bcbe2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 미션 목표 - [x] 토큰 기반 유저 인증/인가 구현하기 -- [ ] (심화) Refresh Token 구현하기 +- [x] (심화) Refresh Token 구현하기 - [ ] (심화) Prisma로 관계형 활용하기 ## 기본 요구사항 @@ -25,14 +25,14 @@ ###게시글 기능 인가 -- [ ] 로그인한 유저만 게시글을 등록할 수 있습니다. -- [ ] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. +- [x] 로그인한 유저만 게시글을 등록할 수 있습니다. +- [x] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. ###댓글 기능 인가 -- [ ] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. -- [ ] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. -- [ ] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. +- [x] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. +- [x] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. +- [x] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. ###유저 정보 @@ -46,7 +46,7 @@ ###인증 -- [ ] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. +- [x] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. ###좋아요 기능 diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index 61e9af39..c57b29da 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,5 +1,6 @@ import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; +import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; +import auth from './../middlewares/auth.js'; import { GetArticle, PostArticle, @@ -14,12 +15,12 @@ const articleRouter = EXPRESS.Router(); articleRouter.route('/') .get(catchAsync(GetArticle)) - .post(catchAsync(PostArticle)); + .post(auth.verifyAccessToken, catchAsync(PostArticle)); articleRouter.route('/:id') .get(catchAsync(GetArticleById)) - .patch(catchAsync(PatchArticleById)) - .delete(catchAsync(DeleteArticleById)); + .patch(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, PatchArticleById)) + .delete(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, DeleteArticleById)); export default articleRouter; \ No newline at end of file diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.js index c9bc7a38..0a4ab53f 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.js @@ -1,5 +1,6 @@ import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; +import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; +import auth from './../middlewares/auth.js'; import { GetComment, PostComment, @@ -12,12 +13,12 @@ const commentRouter = EXPRESS.Router(); commentRouter.route('/') .get(catchAsync(GetComment)) - .post(catchAsync(PostComment)); + .post(auth.verifyAccessToken, catchAsync(PostComment)); commentRouter.route('/:id') .get(catchAsync(GetCommentById)) - .patch(catchAsync(PatchCommentById)) - .delete(catchAsync(DeleteCommentById)); + .patch(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, PatchCommentById)) + .delete(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, DeleteCommentById)); export default commentRouter; \ No newline at end of file From e57d75c664bbb2aadf4c52178cb3451881596b19 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Thu, 27 Nov 2025 19:00:29 +0900 Subject: [PATCH 19/53] =?UTF-8?q?[feat]=20=EC=9E=90=EA=B8=B0=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Routers/userRouter.js | 13 ++++-- src/controller/userController.js | 66 +++++++++++++++------------ src/repositories/productRepository.js | 17 +++++-- src/services/userService.js | 26 +++++++++-- 4 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 58efe3d5..6398eb1f 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -1,13 +1,18 @@ import { EXPRESS } from './../libs/constants.js'; import { catchAsync } from './../libs/catchAsync.js'; -import { login, register, refresh } from '../controller/userController.js'; +import UserServiceController from '../controller/userController.js'; import auth from '../middlewares/auth.js'; const userRouter = EXPRESS.Router(); +const userController = new UserServiceController(); -userRouter.post('/', catchAsync(register)); -userRouter.post('/login', catchAsync(login)); -userRouter.post('/token/refresh', auth.verifyRefreshToken, catchAsync(refresh)); + +userRouter.post('/', catchAsync(userController.register)); +userRouter.post('/login', catchAsync(userController.login)); +userRouter.post('/token/refresh', + auth.verifyRefreshToken, catchAsync(userController.refresh)); + +userRouter.get('/me', auth.verifyAccessToken, catchAsync(userController.GetMe)); export default userRouter; \ No newline at end of file diff --git a/src/controller/userController.js b/src/controller/userController.js index 3457c5af..9cb94de0 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -1,34 +1,40 @@ import { userService } from '../services/userService.js'; +export default class UserServiceController { + async register(req, res) { + const user = await userService.createUser(req.body); + return res.status(201).json(user); + } -export async function register(req, res) { - const user = await userService.createUser(req.body); - return res.status(201).json(user); + async login(req, res) { + const { email, password } = req.body; + const user = await userService.getUser(email, password); + const accessToken = userService.createToken(user); + const refreshToken = userService.createToken(user, 'refresh'); + await userService.updateUser(user.id, { refreshToken }); + res.cookie('refreshToken', refreshToken, { + httpOnly: true, + sameSite: 'none', + secure: true + }); + return res.status(200).json({ accessToken }); + } + async refresh(req, res) { + const { refreshToken } = req.cookies; + const { userId } = req.auth; + const { accessToken, newRefreshToken } = await userService.refreshToken(userId, refreshToken); // 변경 + await userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 + res.cookie('refreshToken', newRefreshToken, { // 추가 + path: '/token/refresh', + httpOnly: true, + sameSite: 'none', + secure: true, + }); + return res.json({ accessToken }); + } + async GetMe(req, res) { + const userId = req.user.userId; + const user = await userService.getUserById(userId); + return res.status(200).json(user); + } } - -export async function login(req, res) { - const { email, password } = req.body; - const user = await userService.getUser(email, password); - const accessToken = userService.createToken(user); - const refreshToken = userService.createToken(user, 'refresh'); - await userService.updateUser(user.id, { refreshToken }); - res.cookie('refreshToken', refreshToken, { - httpOnly: true, - sameSite: 'none', - secure: true - }); - return res.status(200).json({ accessToken }); -} -export async function refresh(req, res) { - const { refreshToken } = req.cookies; - const { userId } = req.auth; - const { accessToken, newRefreshToken } = await userService.refreshToken(userId, refreshToken); // 변경 - await userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 - res.cookie('refreshToken', newRefreshToken, { // 추가 - path: '/token/refresh', - httpOnly: true, - sameSite: 'none', - secure: true, - }); - return res.json({ accessToken }); -} \ No newline at end of file diff --git a/src/repositories/productRepository.js b/src/repositories/productRepository.js index 36826813..6ee576f7 100644 --- a/src/repositories/productRepository.js +++ b/src/repositories/productRepository.js @@ -1,5 +1,13 @@ import { prismaClient } from '../libs/constants.js'; +async function findByAuthorId(authorId) { + return prismaClient.product.findMany({ + where: { + authorId, + }, + }); +} + async function findById(id) { return prismaClient.product.findUniqueOrThrow({ where: { @@ -11,10 +19,10 @@ async function findById(id) { async function update(id, data) { return prismaClient.product.update({ where: { - id + id, }, data: { - ...data + ...data, }, }); } @@ -22,7 +30,7 @@ async function update(id, data) { async function create(userFields) { return await prismaClient.product.create({ data: { - ...userFields + ...userFields, }, }); } @@ -30,7 +38,7 @@ async function create(userFields) { async function ondelete(id) { return await prismaClient.product.delete({ where: { - id + id, }, }); } @@ -40,4 +48,5 @@ export default { update, create, ondelete, + findByAuthorId, }; diff --git a/src/services/userService.js b/src/services/userService.js index ef2f82d7..75539b93 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,21 +1,27 @@ import { CustomError } from '../libs/Handler/errorHandler.js'; import userRepository from '../repositories/userRepository.js'; +import productRepository from '../repositories/productRepository.js'; import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; -async function hashingPassword(password) { // 함수 추가 +async function hashingPassword(password) { + // 함수 추가 return bcrypt.hash(password, 12); } - class UserService { async createUser(user) { const existedUser = await userRepository.findByEmail(user.email); if (existedUser) { - throw new CustomError(422, 'User already exists', { email: user.email }); + throw new CustomError(422, 'User already exists', { + email: user.email, + }); } const hashedPassword = await hashingPassword(user.password); - const createdUser = await userRepository.save({ ...user, password: hashedPassword }); + const createdUser = await userRepository.save({ + ...user, + password: hashedPassword, + }); return this.filterSensitivceUserData(createdUser); } @@ -31,13 +37,23 @@ class UserService { await this.verifyPassword(password, user.password); return this.filterSensitivceUserData(user); } + + + async getUserById(id) { + const user = await userRepository.findById(id); + if (!user) { + throw new CustomError(404, 'User not found'); + } + return this.filterSensitivceUserData(user); + } + + async updateUser(id, data) { return await userRepository.update(id, data); } async verifyPassword(inputPassword, savedPassword) { const isValid = await bcrypt.compare(inputPassword, savedPassword); if (!isValid) throw new CustomError(401, 'Unauthorized'); - } createToken(user, type) { const payload = { userId: user.id }; From 39cf97ff3fac2e23105300a091e630996664592e Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sat, 29 Nov 2025 12:03:00 +0900 Subject: [PATCH 20/53] =?UTF-8?q?[FEAT]=20=EC=9C=A0=EC=A0=80=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EA=B8=B0=EB=8A=A5,=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Routers/articleRouter.js | 24 ++-- src/Routers/productRouter.js | 25 ++-- src/Routers/userRouter.js | 4 + src/controller/articleController.js | 147 ++++++++++------------ src/controller/commentController.js | 2 +- src/controller/productController.js | 130 ++++++++++--------- src/controller/userController.js | 23 ++++ src/middlewares/auth.js | 38 +++++- src/repositories/articleLikeRepository.js | 35 ++++++ src/repositories/articleRepository.js | 58 +++++++++ src/repositories/productLikeRepository.js | 47 +++++++ src/repositories/productRepository.js | 23 +++- src/services/articleService.js | 32 +++++ src/services/productService.js | 32 +++++ src/services/userService.js | 37 +++++- src/{libs => structs}/structs.js | 0 src/structs/userStructs.js | 24 ++++ 17 files changed, 494 insertions(+), 187 deletions(-) create mode 100644 src/repositories/articleLikeRepository.js create mode 100644 src/repositories/articleRepository.js create mode 100644 src/repositories/productLikeRepository.js create mode 100644 src/services/articleService.js create mode 100644 src/services/productService.js rename src/{libs => structs}/structs.js (100%) create mode 100644 src/structs/userStructs.js diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.js index c57b29da..3de648b6 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.js @@ -1,26 +1,20 @@ import { EXPRESS } from './../libs/constants.js'; -import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import auth from './../middlewares/auth.js'; -import { - GetArticle, - PostArticle, - GetArticleById, - PatchArticleById, - DeleteArticleById -} from '../controller/articleController.js'; +import ArticleController from '../controller/articleController.js'; const articleRouter = EXPRESS.Router(); - - +const articleController = new ArticleController(); articleRouter.route('/') - .get(catchAsync(GetArticle)) - .post(auth.verifyAccessToken, catchAsync(PostArticle)); + .get(auth.softVerifyAccessToken, catchAsync(articleController.getArticles)) + .post(auth.verifyAccessToken, catchAsync(articleController.postArticle)); articleRouter.route('/:id') - .get(catchAsync(GetArticleById)) - .patch(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, PatchArticleById)) - .delete(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, DeleteArticleById)); + .get(auth.softVerifyAccessToken, catchAsync(articleController.getArticleById)) + .patch(auth.verifyAccessToken, auth.verifyArticleAuth, catchAsync(articleController.patchArticleById)) + .delete(auth.verifyAccessToken, auth.verifyArticleAuth, catchAsync(articleController.deleteArticleById)); +articleRouter.post('/:id/like', auth.verifyAccessToken, catchAsync(articleController.likeArticle)); export default articleRouter; \ No newline at end of file diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.js index 22419b80..2bd7a7ea 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.js @@ -1,25 +1,20 @@ import { EXPRESS } from './../libs/constants.js'; -import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; +import { catchAsync } from './../libs/catchAsync.js'; import auth from './../middlewares/auth.js'; -import { - GetProduct, - PostProduct, - GetProductById, - PatchProductById, - DeleteProductById -} from '../controller/productController.js'; +import ProductController from '../controller/productController.js'; const productRouter = EXPRESS.Router(); - - +const productController = new ProductController(); productRouter.route('/') - .get(catchAsync(GetProduct)) - .post(auth.verifyAccessToken, catchAsync(PostProduct)); + .get(auth.softVerifyAccessToken, catchAsync(productController.GetProduct)) + .post(auth.verifyAccessToken, catchAsync(productController.PostProduct)); productRouter.route('/:id') - .get(catchAsync(GetProductById)) - .patch(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, PatchProductById)) - .delete(auth.verifyAccessToken, catchAsyncAll(auth.verifyProduectAuth, DeleteProductById)); + .get(auth.softVerifyAccessToken, catchAsync(productController.GetProductById)) + .patch(auth.verifyAccessToken, catchAsync(productController.PatchProductById)) + .delete(auth.verifyAccessToken, catchAsync(productController.DeleteProductById)); + +productRouter.post('/:id/like', auth.verifyAccessToken, catchAsync(productController.likeProduct)); export default productRouter; \ No newline at end of file diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.js index 6398eb1f..28c01417 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.js @@ -13,6 +13,10 @@ userRouter.post('/token/refresh', auth.verifyRefreshToken, catchAsync(userController.refresh)); userRouter.get('/me', auth.verifyAccessToken, catchAsync(userController.GetMe)); +userRouter.patch('/me', auth.verifyAccessToken, catchAsync(userController.updateMe)); +userRouter.patch('/me/password', auth.verifyAccessToken, catchAsync(userController.updateMyPassword)); +userRouter.get('/me/products', auth.verifyAccessToken, catchAsync(userController.getMyProducts)); + export default userRouter; \ No newline at end of file diff --git a/src/controller/articleController.js b/src/controller/articleController.js index b454ce54..1fffde4a 100644 --- a/src/controller/articleController.js +++ b/src/controller/articleController.js @@ -1,92 +1,81 @@ -import { prismaClient } from '../libs/constants.js'; +import { articleService } from '../services/articleService.js'; import { assert } from 'superstruct'; -import { CreateArticle, PatchArticle } from '../libs/structs.js'; +import { CreateArticle, PatchArticle } from '../structs/structs.js'; +import articleRepository from '../repositories/articleRepository.js'; +export default class ArticleController { + async getArticles(req, res) { + const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); - -export async function GetArticle(req, res) { - const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - - const findOptions = { - where: { - title: { - contains: title, - }, - content: { - contains: content, + const findOptions = { + where: { + title: { + contains: title, + }, + content: { + contains: content, + }, }, - }, - orderBy, - skip: parsedOffset, - }; + orderBy, + skip: parsedOffset, + }; - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } - const article = await prismaClient.article.findMany(findOptions); - res.send(article); -} + const userId = req.user ? req.user.userId : null; + const articles = await articleService.getArticles(findOptions, userId); + res.send(articles); + } + async getArticleById(req, res) { + const { id } = req.params; + const userId = req.user ? req.user.userId : null; + const article = await articleService.getArticleById(id, userId); + res.send(article); + } -export async function GetArticleById(req, res) { - const { id } = req.params; - const article = await prismaClient.article.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(article); -} + async postArticle(req, res) { + assert(req.body, CreateArticle); + const { ...userFields } = req.body; + const article = await articleRepository.create(userFields); + res.status(201).send(article); + } -export async function PostArticle(req, res) { - assert(req.body, CreateArticle); - const { ...userFields } = req.body; - const article = await prismaClient.article.create({ - data: { - ...userFields - }, - }); - res.status(201).send(article); -} + async patchArticleById(req, res) { + const { id } = req.params; + assert(req.body, PatchArticle); + const { ...userFields } = req.body; + const article = await articleRepository.update(id, userFields); + res.send(article); + } -export async function PatchArticleById(req, res) { - const { id } = req.params; - assert(req.body, PatchArticle); - const { ...userFields } = req.body; - const article = await prismaClient.article.update({ - where: { - id - }, - data: { - ...userFields - }, - }); - res.send(article); -} + async deleteArticleById(req, res) { + const { id } = req.params; + const article = await articleRepository.ondelete(id); + res.send(article); + } -export async function DeleteArticleById(req, res) { - const { id } = req.params; - const article = await prismaClient.article.delete({ - where: { - id - }, - }); - res.send(article); + async likeArticle(req, res) { + const userId = req.user.userId; + const articleId = req.params.id; + const result = await articleService.likeArticle(userId, articleId); + return res.status(200).json(result); + } } - diff --git a/src/controller/commentController.js b/src/controller/commentController.js index 1f1ee3aa..8cce4be4 100644 --- a/src/controller/commentController.js +++ b/src/controller/commentController.js @@ -1,6 +1,6 @@ import { prismaClient } from '../libs/constants.js'; import { assert } from 'superstruct'; -import { CreateComment, PatchComment } from '../libs/structs.js'; +import { CreateComment, PatchComment } from '../structs/structs.js'; diff --git a/src/controller/productController.js b/src/controller/productController.js index 1aa7edf7..7bb8162b 100644 --- a/src/controller/productController.js +++ b/src/controller/productController.js @@ -1,81 +1,79 @@ -import { prismaClient } from '../libs/constants.js'; +import { productService } from '../services/productService.js'; import { assert } from 'superstruct'; -import { CreateProduct, PatchProduct } from '../libs/structs.js'; +import { CreateProduct, PatchProduct } from '../structs/structs.js'; import productRepository from '../repositories/productRepository.js'; -export async function GetProduct(req, res) { - const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - - const findOptions = { - where: { - name: { - contains: name, - }, - description: { - contains: description, - }, - }, - orderBy, - skip: parsedOffset, - }; - - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } - const product = await prismaClient.product.findMany(findOptions); - res.send(product); -} +export default class ProductController { + async GetProduct(req, res) { + const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + const findOptions = { + where: { + name: { + contains: name, + }, + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; -export async function GetProductById(req, res) { - const id = Number(req.params.id); + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } - if (isNaN(id)) { - throw new CustomError(400, 'ID는 숫자여야 합니다.'); + const userId = req.user ? req.user.userId : null; + const products = await productService.getProducts(findOptions, userId); + res.send(products); } - const product = await productRepository.findById(id); - res.send(product); -} -export async function PostProduct(req, res) { - assert(req.body, CreateProduct); - const { ...userFields } = req.body; - const product = await productRepository.create(userFields); - res.status(201).send(product); -} + async GetProductById(req, res) { + const id = req.params.id; + const userId = req.user ? req.user.userId : null; + const product = await productService.getProductById(id, userId); + res.send(product); + } -export async function PatchProductById(req, res) { - const id = Number(req.params.id); + async PostProduct(req, res) { + assert(req.body, CreateProduct); + const { ...userFields } = req.body; + const product = await productRepository.create(userFields); + res.status(201).send(product); + } - if (isNaN(id)) { - throw new CustomError(400, 'ID는 숫자여야 합니다.'); + async PatchProductById(req, res) { + const id = req.params.id; + assert(req.body, PatchProduct); + const { ...userFields } = req.body; + const Product = await productRepository.update(id, userFields); + res.send(Product); } - assert(req.body, PatchProduct); - const { ...userFields } = req.body; - const Product = await productRepository.update(id, userFields); - res.send(Product); -} -export async function DeleteProductById(req, res) { - const id = Number(req.params.id); + async DeleteProductById(req, res) { + const id = req.params.id; + const Product = await productRepository.ondelete(id); + res.send(Product); + } - if (isNaN(id)) { - throw new CustomError(400, 'ID는 숫자여야 합니다.'); + async likeProduct(req, res) { + const userId = req.user.userId; + const productId = req.params.id; + const result = await productService.likeProduct(userId, productId); + return res.status(200).json(result); } - const Product = await productRepository.ondelete(id); - res.send(Product); } \ No newline at end of file diff --git a/src/controller/userController.js b/src/controller/userController.js index 9cb94de0..1c2feef8 100644 --- a/src/controller/userController.js +++ b/src/controller/userController.js @@ -37,4 +37,27 @@ export default class UserServiceController { const user = await userService.getUserById(userId); return res.status(200).json(user); } + async updateMe(req, res) { + const userId = req.user.userId; + const updatedUser = await userService.updateProfile(userId, req.body); + return res.status(200).json(updatedUser); + } + + async updateMyPassword(req, res) { + const userId = req.user.userId; + const updatedUser = await userService.updatePassword(userId, req.body); + return res.status(200).json(updatedUser); + } + + async getMyProducts(req, res) { + const userId = req.user.userId; + const products = await userService.getProductsByUserId(userId); + return res.status(200).json(products); + } + + async getMyLikedProducts(req, res) { + const userId = req.user.userId; + const products = await userService.getLikedProductsByUserId(userId); + return res.status(200).json(products); + } } diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index 2e44d756..4562519a 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,23 +1,36 @@ import { expressjwt } from 'express-jwt'; import productRepository from '../repositories/productRepository.js'; +import articleRepository from '../repositories/articleRepository.js'; import { CustomError } from '../libs/Handler/errorHandler.js'; +import jwt from 'jsonwebtoken'; const verifyAccessToken = expressjwt({ secret: process.env.JWT_SECRET, algorithms: ["HS256"], requestProperty: 'user' }); + +const softVerifyAccessToken = (req, res, next) => { + const token = req.headers.authorization?.split(' ')[1]; + if (token) { + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (!err) { + req.user = user; + } + next(); + }); + } else { + next(); + } +}; + const verifyRefreshToken = expressjwt({ secret: process.env.JWT_SECRET, algorithms: ['HS256'], getToken: (req) => req.cookies.refreshToken, }); async function verifyProduectAuth(req, res, next) { - const id = Number(req.params.id); - - if (isNaN(id)) { - throw new CustomError(400, "ID는 숫자여야 합니다."); - } + const id = req.params.id; const product = await productRepository.findById(id); if (!product) { @@ -29,8 +42,23 @@ async function verifyProduectAuth(req, res, next) { return next(); }; +async function verifyArticleAuth(req, res, next) { + const id = req.params.id; + + const article = await articleRepository.findById(id); + if (!article) { + throw new CustomError(404, 'article not found'); + } + if (article.authorId !== req.user.id) { + throw new CustomError(403, 'Forbidden'); + } + return next(); +} + export default { verifyAccessToken, + softVerifyAccessToken, verifyProduectAuth, + verifyArticleAuth, verifyRefreshToken, }; \ No newline at end of file diff --git a/src/repositories/articleLikeRepository.js b/src/repositories/articleLikeRepository.js new file mode 100644 index 00000000..825729f8 --- /dev/null +++ b/src/repositories/articleLikeRepository.js @@ -0,0 +1,35 @@ +import { prismaClient } from '../libs/constants.js'; + +async function find(userId, articleId) { + return prismaClient.articleLike.findUnique({ + where: { + userId_articleId: { + userId, + articleId, + }, + }, + }); +} + +async function create(userId, articleId) { + return prismaClient.articleLike.create({ + data: { + userId, + articleId, + }, + }); +} + +async function remove(id) { + return prismaClient.articleLike.delete({ + where: { + id, + }, + }); +} + +export default { + find, + create, + remove, +}; diff --git a/src/repositories/articleRepository.js b/src/repositories/articleRepository.js new file mode 100644 index 00000000..bd0dcbc4 --- /dev/null +++ b/src/repositories/articleRepository.js @@ -0,0 +1,58 @@ +import { prismaClient } from '../libs/constants.js'; + +async function findById(id, userId) { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return prismaClient.article.findUniqueOrThrow({ + where: { + id, + }, + include, + }); +} + +async function findAll(findOptions, userId) { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return prismaClient.article.findMany({ + ...findOptions, + include, + }); +} + +async function create(userFields) { + return await prismaClient.article.create({ + data: { + ...userFields, + }, + }); +} + +async function update(id, data) { + return prismaClient.article.update({ + where: { + id, + }, + data: { + ...data, + }, + }); +} + +async function ondelete(id) { + return await prismaClient.article.delete({ + where: { + id, + }, + }); +} + +export default { + findById, + findAll, + create, + update, + ondelete, +}; diff --git a/src/repositories/productLikeRepository.js b/src/repositories/productLikeRepository.js new file mode 100644 index 00000000..ee5e8278 --- /dev/null +++ b/src/repositories/productLikeRepository.js @@ -0,0 +1,47 @@ +import { prismaClient } from '../libs/constants.js'; + +async function find(userId, productId) { + return prismaClient.productLike.findUnique({ + where: { + userId_productId: { + userId, + productId, + }, + }, + }); +} + +async function create(userId, productId) { + return prismaClient.productLike.create({ + data: { + userId, + productId, + }, + }); +} + +async function remove(id) { + return prismaClient.productLike.delete({ + where: { + id, + }, + }); +} + +async function findLikedProductsByUserId(userId) { + return prismaClient.productLike.findMany({ + where: { + userId, + }, + include: { + product: true, + }, + }); +} + +export default { + find, + create, + remove, + findLikedProductsByUserId, +}; diff --git a/src/repositories/productRepository.js b/src/repositories/productRepository.js index 6ee576f7..6f41fb62 100644 --- a/src/repositories/productRepository.js +++ b/src/repositories/productRepository.js @@ -1,18 +1,32 @@ import { prismaClient } from '../libs/constants.js'; -async function findByAuthorId(authorId) { +async function findByUserId(userId) { return prismaClient.product.findMany({ where: { - authorId, + id: userId, }, }); } -async function findById(id) { +async function findById(id, userId) { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; return prismaClient.product.findUniqueOrThrow({ where: { id, }, + include, + }); +} + +async function findAll(findOptions, userId) { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; + return prismaClient.product.findMany({ + ...findOptions, + include, }); } @@ -45,8 +59,9 @@ async function ondelete(id) { export default { findById, + findAll, update, create, ondelete, - findByAuthorId, + findByUserId, }; diff --git a/src/services/articleService.js b/src/services/articleService.js new file mode 100644 index 00000000..6704a4b0 --- /dev/null +++ b/src/services/articleService.js @@ -0,0 +1,32 @@ +import articleLikeRepository from '../repositories/articleLikeRepository.js'; +import articleRepository from '../repositories/articleRepository.js'; + +class ArticleService { + async likeArticle(userId, articleId) { + const existingLike = await articleLikeRepository.find(userId, articleId); + + if (existingLike) { + await articleLikeRepository.remove(existingLike.id); + return { liked: false }; + } else { + await articleLikeRepository.create(userId, articleId); + return { liked: true }; + } + } + + async getArticleById(articleId, userId) { + const article = await articleRepository.findById(articleId, userId); + const { articleLikes, ...rest } = article; + return { ...rest, isLiked: articleLikes.length > 0 }; + } + + async getArticles(findOptions, userId) { + const articles = await articleRepository.findAll(findOptions, userId); + return articles.map((article) => { + const { articleLikes, ...rest } = article; + return { ...rest, isLiked: articleLikes.length > 0 }; + }); + } +} + +export const articleService = new ArticleService(); diff --git a/src/services/productService.js b/src/services/productService.js new file mode 100644 index 00000000..282d14a2 --- /dev/null +++ b/src/services/productService.js @@ -0,0 +1,32 @@ +import productRepository from '../repositories/productRepository.js'; +import productLikeRepository from '../repositories/productLikeRepository.js'; + +class ProductService { + async likeProduct(userId, productId) { + const existingLike = await productLikeRepository.find(userId, productId); + + if (existingLike) { + await productLikeRepository.remove(existingLike.id); + return { liked: false }; + } else { + await productLikeRepository.create(userId, productId); + return { liked: true }; + } + } + + async getProductById(productId, userId) { + const product = await productRepository.findById(productId, userId); + const { productLikes, ...rest } = product; + return { ...rest, isLiked: productLikes.length > 0 }; + } + + async getProducts(findOptions, userId) { + const products = await productRepository.findAll(findOptions, userId); + return products.map((product) => { + const { productLikes, ...rest } = product; + return { ...rest, isLiked: productLikes.length > 0 }; + }); + } +} + +export const productService = new ProductService(); diff --git a/src/services/userService.js b/src/services/userService.js index 75539b93..c9f03a77 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,8 +1,11 @@ +import { assert } from 'superstruct'; import { CustomError } from '../libs/Handler/errorHandler.js'; import userRepository from '../repositories/userRepository.js'; import productRepository from '../repositories/productRepository.js'; +import productLikeRepository from '../repositories/productLikeRepository.js'; import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; +import { PatchUser, ChangePassword } from '../structs/userStructs.js'; async function hashingPassword(password) { // 함수 추가 @@ -47,6 +50,36 @@ class UserService { return this.filterSensitivceUserData(user); } + async updateProfile(id, data) { + assert(data, PatchUser); + const user = await userRepository.update(id, data); + return this.filterSensitivceUserData(user); + } + + async updatePassword(id, data) { + assert(data, ChangePassword); + const { currentPassword, newPassword, confirmNewPassword } = data; + if (newPassword !== confirmNewPassword) { + throw new CustomError(400, "Passwords don't match"); + } + const user = await userRepository.findById(id); + await this.verifyPassword(currentPassword, user.password); + + const hashedPassword = await hashingPassword(newPassword); + const updatedUser = await userRepository.update(id, { password: hashedPassword }); + return this.filterSensitivceUserData(updatedUser); + } + + async getProductsByUserId(id) { + const products = await productRepository.findByUserId(id); + return products; + } + + async getLikedProductsByUserId(id) { + const products = await productLikeRepository.findLikedProductsByUserId(id); + return products; + } + async updateUser(id, data) { return await userRepository.update(id, data); @@ -67,8 +100,8 @@ class UserService { if (!user || user.refreshToken !== refreshToken) { throw new CustomError(401, 'Unauthorized'); } - const accessToken = createToken(user); // 변경 - const newRefreshToken = createToken(user, 'refresh'); // 추가 + const accessToken = this.createToken(user); // 변경 + const newRefreshToken = this.createToken(user, 'refresh'); // 추가 return { accessToken, newRefreshToken }; // 변경 } } diff --git a/src/libs/structs.js b/src/structs/structs.js similarity index 100% rename from src/libs/structs.js rename to src/structs/structs.js diff --git a/src/structs/userStructs.js b/src/structs/userStructs.js new file mode 100644 index 00000000..d7dca614 --- /dev/null +++ b/src/structs/userStructs.js @@ -0,0 +1,24 @@ +import * as s from "superstruct"; +import isEmail from 'is-email'; + +const email = s.refine(s.string(), 'is_email', (v) => isEmail(v)); + +export const CreateUser = s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), + password: s.string(), +}); + + +export const PatchUser = s.partial(s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), +})); + +export const ChangePassword = s.object({ + currentPassword: s.string(), + newPassword: s.string(), + confirmNewPassword: s.string(), +}); \ No newline at end of file From f84aa7db6c791de9c6209f91f136ee30255ada12 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Wed, 3 Dec 2025 10:02:05 +0900 Subject: [PATCH 21/53] =?UTF-8?q?[FIX]=20REAMD.me=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d01bcbe2..14db9fbb 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,11 @@ ###유저 정보 -- [ ] 유저가 자신의 정보를 조회하는 기능을 구현합니다. -- [ ] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. -- [ ] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. -- [ ] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. -- [ ] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. +- [x] 유저가 자신의 정보를 조회하는 기능을 구현합니다. +- [x] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. +- [x] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. +- [x] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. +- [x] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. ##심화 요구사항 @@ -50,7 +50,7 @@ ###좋아요 기능 -- [ ] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. -- [ ] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. -- [ ] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. -- [ ] 유저가 '좋아요'를 표시한 상품의 목록을 조회하는 기능을 구현합니다. +- [x] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [x] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [x] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. +- [x] 유저가 '좋아요'를 표시한 상품의 목록을 조회하는 기능을 구현합니다. From af5a1f961aa96512b8d41e2ab708af88ae805c92 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 5 Dec 2025 09:31:54 +0900 Subject: [PATCH 22/53] =?UTF-8?q?Service,structs,main=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 67 +-- dist/Routers/articleRouter.d.ts | 3 + dist/Routers/articleRouter.d.ts.map | 1 + dist/Routers/articleRouter.js | 21 + dist/Routers/articleRouter.js.map | 1 + dist/Routers/commentRouter.d.ts | 3 + dist/Routers/commentRouter.d.ts.map | 1 + dist/Routers/commentRouter.js | 19 + dist/Routers/commentRouter.js.map | 1 + dist/Routers/productRouter.d.ts | 3 + dist/Routers/productRouter.d.ts.map | 1 + dist/Routers/productRouter.js | 21 + dist/Routers/productRouter.js.map | 1 + dist/Routers/routerManager.d.ts | 2 + dist/Routers/routerManager.d.ts.map | 1 + dist/Routers/routerManager.js | 19 + dist/Routers/routerManager.js.map | 1 + dist/Routers/uploadRouter.d.ts | 3 + dist/Routers/uploadRouter.d.ts.map | 1 + dist/Routers/uploadRouter.js | 15 + dist/Routers/uploadRouter.js.map | 1 + dist/Routers/userRouter.d.ts | 3 + dist/Routers/userRouter.d.ts.map | 1 + dist/Routers/userRouter.js | 20 + dist/Routers/userRouter.js.map | 1 + dist/controller/articleController.d.ts | 9 + dist/controller/articleController.d.ts.map | 1 + dist/controller/articleController.js | 110 +++++ dist/controller/articleController.js.map | 1 + dist/controller/commentController.d.ts | 6 + dist/controller/commentController.d.ts.map | 1 + dist/controller/commentController.js | 122 ++++++ dist/controller/commentController.js.map | 1 + dist/controller/productController.d.ts | 9 + dist/controller/productController.d.ts.map | 1 + dist/controller/productController.js | 109 +++++ dist/controller/productController.js.map | 1 + dist/controller/uploadController.d.ts | 2 + dist/controller/uploadController.d.ts.map | 1 + dist/controller/uploadController.js | 9 + dist/controller/uploadController.js.map | 1 + dist/controller/userController.d.ts | 11 + dist/controller/userController.d.ts.map | 1 + dist/controller/userController.js | 87 ++++ dist/controller/userController.js.map | 1 + dist/libs/Handler/errorHandler.d.ts | 11 + dist/libs/Handler/errorHandler.d.ts.map | 1 + dist/libs/Handler/errorHandler.js | 80 ++++ dist/libs/Handler/errorHandler.js.map | 1 + dist/libs/catchAsync.d.ts | 3 + dist/libs/catchAsync.d.ts.map | 1 + dist/libs/catchAsync.js | 10 + dist/libs/catchAsync.js.map | 1 + dist/libs/constants.d.ts | 9 + dist/libs/constants.d.ts.map | 1 + dist/libs/constants.js | 47 ++ dist/libs/constants.js.map | 1 + dist/libs/corsSetUp.d.ts | 3 + dist/libs/corsSetUp.d.ts.map | 1 + dist/libs/corsSetUp.js | 15 + dist/libs/corsSetUp.js.map | 1 + dist/libs/interfaces.d.ts | 73 ++++ dist/libs/interfaces.d.ts.map | 1 + dist/libs/interfaces.js | 3 + dist/libs/interfaces.js.map | 1 + dist/main.d.ts | 2 + dist/main.d.ts.map | 1 + dist/main.js | 26 ++ dist/main.js.map | 1 + dist/middlewares/auth.d.ts | 17 + dist/middlewares/auth.d.ts.map | 1 + dist/middlewares/auth.js | 79 ++++ dist/middlewares/auth.js.map | 1 + dist/repositories/articleLikeRepository.d.ts | 25 ++ .../articleLikeRepository.d.ts.map | 1 + dist/repositories/articleLikeRepository.js | 49 +++ .../repositories/articleLikeRepository.js.map | 1 + dist/repositories/articleRepository.d.ts | 59 +++ dist/repositories/articleRepository.d.ts.map | 1 + dist/repositories/articleRepository.js | 79 ++++ dist/repositories/articleRepository.js.map | 1 + dist/repositories/productLikeRepository.d.ts | 42 ++ .../productLikeRepository.d.ts.map | 1 + dist/repositories/productLikeRepository.js | 62 +++ .../repositories/productLikeRepository.js.map | 1 + dist/repositories/productRepository.d.ts | 79 ++++ dist/repositories/productRepository.d.ts.map | 1 + dist/repositories/productRepository.js | 91 ++++ dist/repositories/productRepository.js.map | 1 + dist/repositories/userRepository.d.ts | 49 +++ dist/repositories/userRepository.d.ts.map | 1 + dist/repositories/userRepository.js | 78 ++++ dist/repositories/userRepository.js.map | 1 + dist/services/articleService.d.ts | 25 ++ dist/services/articleService.d.ts.map | 1 + dist/services/articleService.js | 61 +++ dist/services/articleService.js.map | 1 + dist/services/productService.d.ts | 29 ++ dist/services/productService.d.ts.map | 1 + dist/services/productService.js | 61 +++ dist/services/productService.js.map | 1 + dist/services/userService.d.ts | 107 +++++ dist/services/userService.d.ts.map | 1 + dist/services/userService.js | 151 +++++++ dist/services/userService.js.map | 1 + dist/structs/structs.d.ts | 52 +++ dist/structs/structs.d.ts.map | 1 + dist/structs/structs.js | 65 +++ dist/structs/structs.js.map | 1 + dist/structs/userStructs.d.ts | 31 ++ dist/structs/userStructs.d.ts.map | 1 + dist/structs/userStructs.js | 59 +++ dist/structs/userStructs.js.map | 1 + package-lock.json | 413 +++++++++++++++++- package.json | 24 +- sprint4-README.md | 56 +++ .../{articleRouter.js => articleRouter.ts} | 6 +- .../{commentRouter.js => commentRouter.ts} | 6 +- .../{productRouter.js => productRouter.ts} | 6 +- .../{routerManager.js => routerManager.ts} | 2 +- .../{uploadRouter.js => uploadRouter.ts} | 4 +- src/Routers/{userRouter.js => userRouter.ts} | 4 +- ...icleController.js => articleController.ts} | 0 ...mentController.js => commentController.ts} | 0 ...ductController.js => productController.ts} | 0 ...ploadController.js => uploadController.ts} | 0 .../{userController.js => userController.ts} | 0 .../{errorHandler.js => errorHandler.ts} | 0 src/libs/{catchAsync.js => catchAsync.ts} | 0 src/libs/{constants.js => constants.ts} | 8 +- src/libs/corsSetUp.js | 28 -- src/libs/corsSetUp.ts | 11 + src/libs/interfaces.ts | 80 ++++ src/{main.js => main.ts} | 8 +- src/middlewares/{auth.js => auth.ts} | 11 +- ...Repository.js => articleLikeRepository.ts} | 8 +- ...icleRepository.js => articleRepository.ts} | 16 +- ...Repository.js => productLikeRepository.ts} | 8 +- ...ductRepository.js => productRepository.ts} | 25 +- src/repositories/userRepository.js | 52 --- src/repositories/userRepository.ts | 56 +++ .../{articleService.js => articleService.ts} | 17 +- .../{productService.js => productService.ts} | 12 +- .../{userService.js => userService.ts} | 55 ++- src/structs/{structs.js => structs.ts} | 0 .../{userStructs.js => userStructs.ts} | 0 tsconfig.json | 39 ++ 147 files changed, 3083 insertions(+), 233 deletions(-) create mode 100644 dist/Routers/articleRouter.d.ts create mode 100644 dist/Routers/articleRouter.d.ts.map create mode 100644 dist/Routers/articleRouter.js create mode 100644 dist/Routers/articleRouter.js.map create mode 100644 dist/Routers/commentRouter.d.ts create mode 100644 dist/Routers/commentRouter.d.ts.map create mode 100644 dist/Routers/commentRouter.js create mode 100644 dist/Routers/commentRouter.js.map create mode 100644 dist/Routers/productRouter.d.ts create mode 100644 dist/Routers/productRouter.d.ts.map create mode 100644 dist/Routers/productRouter.js create mode 100644 dist/Routers/productRouter.js.map create mode 100644 dist/Routers/routerManager.d.ts create mode 100644 dist/Routers/routerManager.d.ts.map create mode 100644 dist/Routers/routerManager.js create mode 100644 dist/Routers/routerManager.js.map create mode 100644 dist/Routers/uploadRouter.d.ts create mode 100644 dist/Routers/uploadRouter.d.ts.map create mode 100644 dist/Routers/uploadRouter.js create mode 100644 dist/Routers/uploadRouter.js.map create mode 100644 dist/Routers/userRouter.d.ts create mode 100644 dist/Routers/userRouter.d.ts.map create mode 100644 dist/Routers/userRouter.js create mode 100644 dist/Routers/userRouter.js.map create mode 100644 dist/controller/articleController.d.ts create mode 100644 dist/controller/articleController.d.ts.map create mode 100644 dist/controller/articleController.js create mode 100644 dist/controller/articleController.js.map create mode 100644 dist/controller/commentController.d.ts create mode 100644 dist/controller/commentController.d.ts.map create mode 100644 dist/controller/commentController.js create mode 100644 dist/controller/commentController.js.map create mode 100644 dist/controller/productController.d.ts create mode 100644 dist/controller/productController.d.ts.map create mode 100644 dist/controller/productController.js create mode 100644 dist/controller/productController.js.map create mode 100644 dist/controller/uploadController.d.ts create mode 100644 dist/controller/uploadController.d.ts.map create mode 100644 dist/controller/uploadController.js create mode 100644 dist/controller/uploadController.js.map create mode 100644 dist/controller/userController.d.ts create mode 100644 dist/controller/userController.d.ts.map create mode 100644 dist/controller/userController.js create mode 100644 dist/controller/userController.js.map create mode 100644 dist/libs/Handler/errorHandler.d.ts create mode 100644 dist/libs/Handler/errorHandler.d.ts.map create mode 100644 dist/libs/Handler/errorHandler.js create mode 100644 dist/libs/Handler/errorHandler.js.map create mode 100644 dist/libs/catchAsync.d.ts create mode 100644 dist/libs/catchAsync.d.ts.map create mode 100644 dist/libs/catchAsync.js create mode 100644 dist/libs/catchAsync.js.map create mode 100644 dist/libs/constants.d.ts create mode 100644 dist/libs/constants.d.ts.map create mode 100644 dist/libs/constants.js create mode 100644 dist/libs/constants.js.map create mode 100644 dist/libs/corsSetUp.d.ts create mode 100644 dist/libs/corsSetUp.d.ts.map create mode 100644 dist/libs/corsSetUp.js create mode 100644 dist/libs/corsSetUp.js.map create mode 100644 dist/libs/interfaces.d.ts create mode 100644 dist/libs/interfaces.d.ts.map create mode 100644 dist/libs/interfaces.js create mode 100644 dist/libs/interfaces.js.map create mode 100644 dist/main.d.ts create mode 100644 dist/main.d.ts.map create mode 100644 dist/main.js create mode 100644 dist/main.js.map create mode 100644 dist/middlewares/auth.d.ts create mode 100644 dist/middlewares/auth.d.ts.map create mode 100644 dist/middlewares/auth.js create mode 100644 dist/middlewares/auth.js.map create mode 100644 dist/repositories/articleLikeRepository.d.ts create mode 100644 dist/repositories/articleLikeRepository.d.ts.map create mode 100644 dist/repositories/articleLikeRepository.js create mode 100644 dist/repositories/articleLikeRepository.js.map create mode 100644 dist/repositories/articleRepository.d.ts create mode 100644 dist/repositories/articleRepository.d.ts.map create mode 100644 dist/repositories/articleRepository.js create mode 100644 dist/repositories/articleRepository.js.map create mode 100644 dist/repositories/productLikeRepository.d.ts create mode 100644 dist/repositories/productLikeRepository.d.ts.map create mode 100644 dist/repositories/productLikeRepository.js create mode 100644 dist/repositories/productLikeRepository.js.map create mode 100644 dist/repositories/productRepository.d.ts create mode 100644 dist/repositories/productRepository.d.ts.map create mode 100644 dist/repositories/productRepository.js create mode 100644 dist/repositories/productRepository.js.map create mode 100644 dist/repositories/userRepository.d.ts create mode 100644 dist/repositories/userRepository.d.ts.map create mode 100644 dist/repositories/userRepository.js create mode 100644 dist/repositories/userRepository.js.map create mode 100644 dist/services/articleService.d.ts create mode 100644 dist/services/articleService.d.ts.map create mode 100644 dist/services/articleService.js create mode 100644 dist/services/articleService.js.map create mode 100644 dist/services/productService.d.ts create mode 100644 dist/services/productService.d.ts.map create mode 100644 dist/services/productService.js create mode 100644 dist/services/productService.js.map create mode 100644 dist/services/userService.d.ts create mode 100644 dist/services/userService.d.ts.map create mode 100644 dist/services/userService.js create mode 100644 dist/services/userService.js.map create mode 100644 dist/structs/structs.d.ts create mode 100644 dist/structs/structs.d.ts.map create mode 100644 dist/structs/structs.js create mode 100644 dist/structs/structs.js.map create mode 100644 dist/structs/userStructs.d.ts create mode 100644 dist/structs/userStructs.d.ts.map create mode 100644 dist/structs/userStructs.js create mode 100644 dist/structs/userStructs.js.map create mode 100644 sprint4-README.md rename src/Routers/{articleRouter.js => articleRouter.ts} (84%) rename src/Routers/{commentRouter.js => commentRouter.ts} (78%) rename src/Routers/{productRouter.js => productRouter.ts} (83%) rename src/Routers/{routerManager.js => routerManager.ts} (91%) rename src/Routers/{uploadRouter.js => uploadRouter.ts} (77%) rename src/Routers/{userRouter.js => userRouter.ts} (88%) rename src/controller/{articleController.js => articleController.ts} (100%) rename src/controller/{commentController.js => commentController.ts} (100%) rename src/controller/{productController.js => productController.ts} (100%) rename src/controller/{uploadController.js => uploadController.ts} (100%) rename src/controller/{userController.js => userController.ts} (100%) rename src/libs/Handler/{errorHandler.js => errorHandler.ts} (100%) rename src/libs/{catchAsync.js => catchAsync.ts} (100%) rename src/libs/{constants.js => constants.ts} (53%) delete mode 100644 src/libs/corsSetUp.js create mode 100644 src/libs/corsSetUp.ts create mode 100644 src/libs/interfaces.ts rename src/{main.js => main.ts} (66%) rename src/middlewares/{auth.js => auth.ts} (86%) rename src/repositories/{articleLikeRepository.js => articleLikeRepository.ts} (69%) rename src/repositories/{articleRepository.js => articleRepository.ts} (65%) rename src/repositories/{productLikeRepository.js => productLikeRepository.ts} (77%) rename src/repositories/{productRepository.js => productRepository.ts} (54%) delete mode 100644 src/repositories/userRepository.js create mode 100644 src/repositories/userRepository.ts rename src/services/{articleService.js => articleService.ts} (71%) rename src/services/{productService.js => productService.ts} (79%) rename src/services/{userService.js => userService.ts} (68%) rename src/structs/{structs.js => structs.ts} (100%) rename src/structs/{userStructs.js => userStructs.ts} (100%) create mode 100644 tsconfig.json diff --git a/README.md b/README.md index 14db9fbb..363882a0 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,35 @@ ## 미션 목표 -- [x] 토큰 기반 유저 인증/인가 구현하기 -- [x] (심화) Refresh Token 구현하기 -- [ ] (심화) Prisma로 관계형 활용하기 +- [ ] 타입스크립트 마이그레이션하기 +- [ ] 타입스크립트 개발 환경 세팅하기 +- [ ] (심화) Layered Architecture 적용하기 -## 기본 요구사항 +## 기본 요구 사항 -- [x] 스프린트 미션 3의 구현이 완료된 상태에서 진행을 권장합니다. -- [x] 가능하다면 Prisma 스키마를 수정할 때 Prisma 마이그레이션을 함께 진행해 보고, 잘되지 않는다면 마이그레이션 파일과 데이터베이스를 초기화하고 진행해도 좋습니다. +- [ ] 스프린트 미션 4의 구현이 완료된 상태에서 진행을 권장합니다. +- [ ] 타입스크립트 마이그레이션을 먼저 진행해 보고, 이전 미션에서 구현하지 못한 부분이 있다면 추가로 구현해 보세요. -### 인증 +## 프로젝트 세팅 -- [x] User 스키마를 작성해 주세요. -- [x] id, email, nickname, image, password, createdAt, updatedAt 필드를 가집니다. -- [x] 회원가입 API를 만들어 주세요. -- [x] email, nickname, password 를 입력하여 회원가입을 진행합니다. -- [x] password는 해싱해 저장합니다. -- [x] 토큰 기반 인증: 로그인에 성공하면 Access Token을 발급하는 기능을 구현합니다. +- [] tsconfig.json 파일을 생성하고, 필요한 옵션을 설정해 주세요. (예: outDir). +- [] 필요한 npm script를 설정해 주세요. (예: 빌드 및 개발 서버 실행 명령어) -###상품 기능 인가 +## 타입스크립트 마이그레이션 -- [x] 로그인한 유저만 상품을 등록할 수 있습니다. -- [x] 상품을 등록한 유저만 해당 상품의 정보를 수정하거나 삭제할 수 있습니다. +- [ ] 기존 Express.js 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요. +- [ ] 필요한 타입 패키지를 설치해 주세요. +- [ ] any 타입의 사용은 최소화해주세요. +- [ ] 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요. +- [ ] 필요한 경우, 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요. +- [ ] 필요한 경우, declare를 사용하여 타입을 오버라이드하거나 확장합니다. (예: req.user) -###게시글 기능 인가 +## 개발 환경 설정 -- [x] 로그인한 유저만 게시글을 등록할 수 있습니다. -- [x] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. +- [ ] ts-node 를 사용해 .ts 코드를 바로 실행할 수 있는 npm script를 만들어 주세요. (예: npm run dev) +- [ ] nodemon을 사용해 .ts 코드가 변경될 때마다 서버가 다시 실행되는 npm script를 만들어 주세요. (예: npm run dev) -###댓글 기능 인가 +## 심화 요구 사항 -- [x] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. -- [x] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. -- [x] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. - -###유저 정보 - -- [x] 유저가 자신의 정보를 조회하는 기능을 구현합니다. -- [x] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. -- [x] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. -- [x] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. -- [x] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. - -##심화 요구사항 - -###인증 - -- [x] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. - -###좋아요 기능 - -- [x] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. -- [x] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. -- [x] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. -- [x] 유저가 '좋아요'를 표시한 상품의 목록을 조회하는 기능을 구현합니다. +- [ ] Layered Architecture 적용하기 +- [ ] Controller, Service, Repository로 나누어 코드를 리팩토링해 주세요. +- [ ] 필요하다면, 계층 사이에서 데이터를 주고 받을 때 DTO를 활용해 주세요. diff --git a/dist/Routers/articleRouter.d.ts b/dist/Routers/articleRouter.d.ts new file mode 100644 index 00000000..8878a904 --- /dev/null +++ b/dist/Routers/articleRouter.d.ts @@ -0,0 +1,3 @@ +declare const articleRouter: import("express-serve-static-core").Router; +export default articleRouter; +//# sourceMappingURL=articleRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.d.ts.map b/dist/Routers/articleRouter.d.ts.map new file mode 100644 index 00000000..8986ff63 --- /dev/null +++ b/dist/Routers/articleRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/articleRouter.js b/dist/Routers/articleRouter.js new file mode 100644 index 00000000..199ef703 --- /dev/null +++ b/dist/Routers/articleRouter.js @@ -0,0 +1,21 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +const catchAsync_js_1 = require("../libs/catchAsync.js"); +const auth_js_1 = __importDefault(require("../middlewares/auth.js")); +const articleController_js_1 = __importDefault(require("../controller/articleController.js")); +const articleRouter = constants_js_1.EXPRESS.Router(); +const articleController = new articleController_js_1.default(); +articleRouter.route('/') + .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.getArticles)) + .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.postArticle)); +articleRouter.route('/:id') + .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.getArticleById)) + .patch(auth_js_1.default.verifyAccessToken, auth_js_1.default.verifyArticleAuth, (0, catchAsync_js_1.catchAsync)(articleController.patchArticleById)) + .delete(auth_js_1.default.verifyAccessToken, auth_js_1.default.verifyArticleAuth, (0, catchAsync_js_1.catchAsync)(articleController.deleteArticleById)); +articleRouter.post('/:id/like', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.likeArticle)); +exports.default = articleRouter; +//# sourceMappingURL=articleRouter.js.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.js.map b/dist/Routers/articleRouter.js.map new file mode 100644 index 00000000..01a62ee5 --- /dev/null +++ b/dist/Routers/articleRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRouter.js","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,qEAA0C;AAC1C,8FAAmE;AAEnE,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,8BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;KAC1E,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KACrG,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAE7G,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts b/dist/Routers/commentRouter.d.ts new file mode 100644 index 00000000..06b1655f --- /dev/null +++ b/dist/Routers/commentRouter.d.ts @@ -0,0 +1,3 @@ +declare const commentRouter: import("express-serve-static-core").Router; +export default commentRouter; +//# sourceMappingURL=commentRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts.map b/dist/Routers/commentRouter.d.ts.map new file mode 100644 index 00000000..415ffed9 --- /dev/null +++ b/dist/Routers/commentRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAYvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.js b/dist/Routers/commentRouter.js new file mode 100644 index 00000000..4ded349f --- /dev/null +++ b/dist/Routers/commentRouter.js @@ -0,0 +1,19 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +const catchAsync_js_1 = require("../libs/catchAsync.js"); +const auth_js_1 = __importDefault(require("../middlewares/auth.js")); +const commentController_js_1 = require("../controller/commentController.js"); +const commentRouter = constants_js_1.EXPRESS.Router(); +commentRouter.route('/') + .get((0, catchAsync_js_1.catchAsync)(commentController_js_1.GetComment)) + .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(commentController_js_1.PostComment)); +commentRouter.route('/:id') + .get((0, catchAsync_js_1.catchAsync)(commentController_js_1.GetCommentById)) + .patch(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsyncAll)(auth_js_1.default.verifyProduectAuth, commentController_js_1.PatchCommentById)) + .delete(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsyncAll)(auth_js_1.default.verifyProduectAuth, commentController_js_1.DeleteCommentById)); +exports.default = commentRouter; +//# sourceMappingURL=commentRouter.js.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.js.map b/dist/Routers/commentRouter.js.map new file mode 100644 index 00000000..eb74a379 --- /dev/null +++ b/dist/Routers/commentRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRouter.js","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAkE;AAClE,qEAA0C;AAC1C,6EAM4C;AAE5C,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAEvC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,IAAA,0BAAU,EAAC,iCAAU,CAAC,CAAC;KAC3B,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,kCAAW,CAAC,CAAC,CAAC;AAE3D,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,IAAA,0BAAU,EAAC,qCAAc,CAAC,CAAC;KAC/B,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,6BAAa,EAAC,iBAAI,CAAC,kBAAkB,EAAE,uCAAgB,CAAC,CAAC;KACvF,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,6BAAa,EAAC,iBAAI,CAAC,kBAAkB,EAAE,wCAAiB,CAAC,CAAC,CAAC;AAG/F,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts b/dist/Routers/productRouter.d.ts new file mode 100644 index 00000000..c3eadb5d --- /dev/null +++ b/dist/Routers/productRouter.d.ts @@ -0,0 +1,3 @@ +declare const productRouter: import("express-serve-static-core").Router; +export default productRouter; +//# sourceMappingURL=productRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts.map b/dist/Routers/productRouter.d.ts.map new file mode 100644 index 00000000..0c9ad952 --- /dev/null +++ b/dist/Routers/productRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.js b/dist/Routers/productRouter.js new file mode 100644 index 00000000..10a43338 --- /dev/null +++ b/dist/Routers/productRouter.js @@ -0,0 +1,21 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +const catchAsync_js_1 = require("../libs/catchAsync.js"); +const auth_js_1 = __importDefault(require("../middlewares/auth.js")); +const productController_js_1 = __importDefault(require("../controller/productController.js")); +const productRouter = constants_js_1.EXPRESS.Router(); +const productController = new productController_js_1.default(); +productRouter.route('/') + .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.GetProduct)) + .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.PostProduct)); +productRouter.route('/:id') + .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.GetProductById)) + .patch(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.PatchProductById)) + .delete(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.DeleteProductById)); +productRouter.post('/:id/like', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.likeProduct)); +exports.default = productRouter; +//# sourceMappingURL=productRouter.js.map \ No newline at end of file diff --git a/dist/Routers/productRouter.js.map b/dist/Routers/productRouter.js.map new file mode 100644 index 00000000..e73f56fa --- /dev/null +++ b/dist/Routers/productRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productRouter.js","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,qEAA0C;AAC1C,8FAAmE;AAEnE,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,8BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;KACzE,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KAC7E,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErF,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts b/dist/Routers/routerManager.d.ts new file mode 100644 index 00000000..c5d39a67 --- /dev/null +++ b/dist/Routers/routerManager.d.ts @@ -0,0 +1,2 @@ +export declare const RouterManager: import("express-serve-static-core").Router; +//# sourceMappingURL=routerManager.d.ts.map \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts.map b/dist/Routers/routerManager.d.ts.map new file mode 100644 index 00000000..92a9a935 --- /dev/null +++ b/dist/Routers/routerManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"routerManager.d.ts","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,4CAAmB,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.js b/dist/Routers/routerManager.js new file mode 100644 index 00000000..43373e55 --- /dev/null +++ b/dist/Routers/routerManager.js @@ -0,0 +1,19 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RouterManager = void 0; +const constants_js_1 = require("../libs/constants.js"); +const productRouter_js_1 = __importDefault(require("./productRouter.js")); +const articleRouter_js_1 = __importDefault(require("./articleRouter.js")); +const commentRouter_js_1 = __importDefault(require("./commentRouter.js")); +const uploadRouter_js_1 = __importDefault(require("./uploadRouter.js")); +const userRouter_js_1 = __importDefault(require("./userRouter.js")); +exports.RouterManager = constants_js_1.EXPRESS.Router(); +exports.RouterManager.use('/products', productRouter_js_1.default); +exports.RouterManager.use('/articles', articleRouter_js_1.default); +exports.RouterManager.use('/comments', commentRouter_js_1.default); +exports.RouterManager.use('/files', uploadRouter_js_1.default); +exports.RouterManager.use('/user', userRouter_js_1.default); +//# sourceMappingURL=routerManager.js.map \ No newline at end of file diff --git a/dist/Routers/routerManager.js.map b/dist/Routers/routerManager.js.map new file mode 100644 index 00000000..6ccaa03b --- /dev/null +++ b/dist/Routers/routerManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,uDAA+C;AAC/C,0EAA+C;AAC/C,0EAA+C;AAC/C,0EAA+C;AAC/C,wEAA6C;AAC7C,oEAAyC;AAG5B,QAAA,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,yBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,uBAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts b/dist/Routers/uploadRouter.d.ts new file mode 100644 index 00000000..a036c514 --- /dev/null +++ b/dist/Routers/uploadRouter.d.ts @@ -0,0 +1,3 @@ +declare const uploadRouter: import("express-serve-static-core").Router; +export default uploadRouter; +//# sourceMappingURL=uploadRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts.map b/dist/Routers/uploadRouter.d.ts.map new file mode 100644 index 00000000..732e2bf6 --- /dev/null +++ b/dist/Routers/uploadRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,YAAY,4CAAmB,CAAC;AAStC,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js b/dist/Routers/uploadRouter.js new file mode 100644 index 00000000..b5877488 --- /dev/null +++ b/dist/Routers/uploadRouter.js @@ -0,0 +1,15 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +const catchAsync_js_1 = require("../libs/catchAsync.js"); +const multer_1 = __importDefault(require("multer")); +const uploadController_js_1 = require("../controller/uploadController.js"); +const uploadRouter = constants_js_1.EXPRESS.Router(); +const upload = (0, multer_1.default)({ dest: 'upload/' }); +uploadRouter.post('/', upload.single('attachment'), (0, catchAsync_js_1.catchAsync)(uploadController_js_1.UploadSingleImage)); +uploadRouter.use('/', constants_js_1.EXPRESS.static('upload')); +exports.default = uploadRouter; +//# sourceMappingURL=uploadRouter.js.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js.map b/dist/Routers/uploadRouter.js.map new file mode 100644 index 00000000..b8ed5cc5 --- /dev/null +++ b/dist/Routers/uploadRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadRouter.js","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,oDAA4B;AAC5B,2EAE2C;AAE3C,MAAM,YAAY,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAEtC,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;AAE3C,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAC9C,IAAA,0BAAU,EAAC,uCAAiB,CAAC,CAAC,CAAC;AAEnC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,sBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEhD,kBAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts b/dist/Routers/userRouter.d.ts new file mode 100644 index 00000000..23df20cc --- /dev/null +++ b/dist/Routers/userRouter.d.ts @@ -0,0 +1,3 @@ +declare const userRouter: import("express-serve-static-core").Router; +export default userRouter; +//# sourceMappingURL=userRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts.map b/dist/Routers/userRouter.d.ts.map new file mode 100644 index 00000000..6564ec89 --- /dev/null +++ b/dist/Routers/userRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,UAAU,4CAAmB,CAAC;AAgBpC,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.js b/dist/Routers/userRouter.js new file mode 100644 index 00000000..e81cb3ca --- /dev/null +++ b/dist/Routers/userRouter.js @@ -0,0 +1,20 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +const catchAsync_js_1 = require("../libs/catchAsync.js"); +const userController_js_1 = __importDefault(require("../controller/userController.js")); +const auth_js_1 = __importDefault(require("../middlewares/auth.js")); +const userRouter = constants_js_1.EXPRESS.Router(); +const userController = new userController_js_1.default(); +userRouter.post('/', (0, catchAsync_js_1.catchAsync)(userController.register)); +userRouter.post('/login', (0, catchAsync_js_1.catchAsync)(userController.login)); +userRouter.post('/token/refresh', auth_js_1.default.verifyRefreshToken, (0, catchAsync_js_1.catchAsync)(userController.refresh)); +userRouter.get('/me', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.GetMe)); +userRouter.patch('/me', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.updateMe)); +userRouter.patch('/me/password', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.updateMyPassword)); +userRouter.get('/me/products', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.getMyProducts)); +exports.default = userRouter; +//# sourceMappingURL=userRouter.js.map \ No newline at end of file diff --git a/dist/Routers/userRouter.js.map b/dist/Routers/userRouter.js.map new file mode 100644 index 00000000..66dfdb18 --- /dev/null +++ b/dist/Routers/userRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,wFAAoE;AACpE,qEAA0C;AAE1C,MAAM,UAAU,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,2BAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1D,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,iBAAI,CAAC,kBAAkB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/controller/articleController.d.ts b/dist/controller/articleController.d.ts new file mode 100644 index 00000000..0c4c8648 --- /dev/null +++ b/dist/controller/articleController.d.ts @@ -0,0 +1,9 @@ +export default class ArticleController { + getArticles(req: any, res: any): Promise; + getArticleById(req: any, res: any): Promise; + postArticle(req: any, res: any): Promise; + patchArticleById(req: any, res: any): Promise; + deleteArticleById(req: any, res: any): Promise; + likeArticle(req: any, res: any): Promise; +} +//# sourceMappingURL=articleController.d.ts.map \ No newline at end of file diff --git a/dist/controller/articleController.d.ts.map b/dist/controller/articleController.d.ts.map new file mode 100644 index 00000000..262ac3b0 --- /dev/null +++ b/dist/controller/articleController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAC5B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAuCpB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOvB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOpB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAQzB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAM1B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAM7B"} \ No newline at end of file diff --git a/dist/controller/articleController.js b/dist/controller/articleController.js new file mode 100644 index 00000000..784588da --- /dev/null +++ b/dist/controller/articleController.js @@ -0,0 +1,110 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const articleService_js_1 = require("../services/articleService.js"); +const superstruct_1 = require("superstruct"); +const structs_js_1 = require("../structs/structs.js"); +const articleRepository_js_1 = __importDefault(require("../repositories/articleRepository.js")); +class ArticleController { + getArticles(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + // parse offset/limit and only include `take` when a positive integer is provided + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + const findOptions = { + where: { + title: { + contains: title, + }, + content: { + contains: content, + }, + }, + orderBy, + skip: parsedOffset, + }; + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + const userId = req.user ? req.user.userId : null; + const articles = yield articleService_js_1.articleService.getArticles(findOptions, userId); + res.send(articles); + }); + } + getArticleById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const userId = req.user ? req.user.userId : null; + const article = yield articleService_js_1.articleService.getArticleById(id, userId); + res.send(article); + }); + } + postArticle(req, res) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_js_1.CreateArticle); + const userFields = __rest(req.body, []); + const article = yield articleRepository_js_1.default.create(userFields); + res.status(201).send(article); + }); + } + patchArticleById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + (0, superstruct_1.assert)(req.body, structs_js_1.PatchArticle); + const userFields = __rest(req.body, []); + const article = yield articleRepository_js_1.default.update(id, userFields); + res.send(article); + }); + } + deleteArticleById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const article = yield articleRepository_js_1.default.ondelete(id); + res.send(article); + }); + } + likeArticle(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const articleId = req.params.id; + const result = yield articleService_js_1.articleService.likeArticle(userId, articleId); + return res.status(200).json(result); + }); + } +} +exports.default = ArticleController; +//# sourceMappingURL=articleController.js.map \ No newline at end of file diff --git a/dist/controller/articleController.js.map b/dist/controller/articleController.js.map new file mode 100644 index 00000000..0e76b016 --- /dev/null +++ b/dist/controller/articleController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/commentController.d.ts b/dist/controller/commentController.d.ts new file mode 100644 index 00000000..280c7142 --- /dev/null +++ b/dist/controller/commentController.d.ts @@ -0,0 +1,6 @@ +export declare function GetComment(req: any, res: any): Promise; +export declare function GetCommentById(req: any, res: any): Promise; +export declare function PostComment(req: any, res: any): Promise; +export declare function PatchCommentById(req: any, res: any): Promise; +export declare function DeleteCommentById(req: any, res: any): Promise; +//# sourceMappingURL=commentController.d.ts.map \ No newline at end of file diff --git a/dist/controller/commentController.d.ts.map b/dist/controller/commentController.d.ts.map new file mode 100644 index 00000000..059331ca --- /dev/null +++ b/dist/controller/commentController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAMA,wBAAsB,UAAU,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBA0CxC;AAGD,wBAAsB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,iBAQ5C;AAGD,wBAAsB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBAoBzC;AAED,wBAAsB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBAkB9C;AAED,wBAAsB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,iBAQ/C"} \ No newline at end of file diff --git a/dist/controller/commentController.js b/dist/controller/commentController.js new file mode 100644 index 00000000..f0f5dba6 --- /dev/null +++ b/dist/controller/commentController.js @@ -0,0 +1,122 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetComment = GetComment; +exports.GetCommentById = GetCommentById; +exports.PostComment = PostComment; +exports.PatchCommentById = PatchCommentById; +exports.DeleteCommentById = DeleteCommentById; +const constants_js_1 = require("../libs/constants.js"); +const superstruct_1 = require("superstruct"); +const structs_js_1 = require("../structs/structs.js"); +function GetComment(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { productId, articleId } = req.query; + const { take = '10', cursor } = req.query; + const parsedTake = parseInt(take, 10); + if (isNaN(parsedTake) || parsedTake <= 0) { + return res.status(400).send({ error: 'Invalid "take" parameter.' }); + } + const whereClause = {}; + if (productId) { + whereClause.productId = productId; // 상품 ID로 필터링 + } + else if (articleId) { + whereClause.articleId = articleId; // 게시글 ID로 필터링 + } + const findOptions = { + take: parsedTake, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, + }; + } + const comments = yield constants_js_1.prismaClient.comment.findMany(findOptions); // + let nextCursor = null; + if (comments.length === parsedTake) { + nextCursor = comments[comments.length - 1].id; + } + res.status(200).json({ + comments, + nextCursor, + }); + }); +} +function GetCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const Comment = yield constants_js_1.prismaClient.comment.findUniqueOrThrow({ + where: { + id + }, + }); + res.send(Comment); + }); +} +function PostComment(req, res) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_js_1.CreateComment); + const { content, productId, articleId } = req.body; + if ((productId && articleId) || (!productId && !articleId)) { + return res.status(400).json({ + error: 'Comment must belong to EITHER a Product OR an Article.', + }); + } + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + const comment = yield constants_js_1.prismaClient.comment.create({ + data: { + content: content, + productId: productId, + articleId: articleId, + }, + }); + res.status(201).send(comment); + }); +} +function PatchCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + (0, superstruct_1.assert)(req.body, structs_js_1.PatchComment); + const { content } = req.body; + if (content !== undefined && content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + const comment = yield constants_js_1.prismaClient.comment.update({ + where: { + id + }, + data: { + content: content, + }, + }); + res.send(comment); + }); +} +function DeleteCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const Comment = yield constants_js_1.prismaClient.comment.delete({ + where: { + id + }, + }); + res.send(Comment); + }); +} +//# sourceMappingURL=commentController.js.map \ No newline at end of file diff --git a/dist/controller/commentController.js.map b/dist/controller/commentController.js.map new file mode 100644 index 00000000..d67221bc --- /dev/null +++ b/dist/controller/commentController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/productController.d.ts b/dist/controller/productController.d.ts new file mode 100644 index 00000000..49d097ef --- /dev/null +++ b/dist/controller/productController.d.ts @@ -0,0 +1,9 @@ +export default class ProductController { + GetProduct(req: any, res: any): Promise; + GetProductById(req: any, res: any): Promise; + PostProduct(req: any, res: any): Promise; + PatchProductById(req: any, res: any): Promise; + DeleteProductById(req: any, res: any): Promise; + likeProduct(req: any, res: any): Promise; +} +//# sourceMappingURL=productController.d.ts.map \ No newline at end of file diff --git a/dist/controller/productController.d.ts.map b/dist/controller/productController.d.ts.map new file mode 100644 index 00000000..fac19115 --- /dev/null +++ b/dist/controller/productController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAC5B,UAAU,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAsCnB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOvB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOpB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAQzB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAM1B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAM7B"} \ No newline at end of file diff --git a/dist/controller/productController.js b/dist/controller/productController.js new file mode 100644 index 00000000..47a58c0b --- /dev/null +++ b/dist/controller/productController.js @@ -0,0 +1,109 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const productService_js_1 = require("../services/productService.js"); +const superstruct_1 = require("superstruct"); +const structs_js_1 = require("../structs/structs.js"); +const productRepository_js_1 = __importDefault(require("../repositories/productRepository.js")); +class ProductController { + GetProduct(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); + const parsedLimit = parseInt(limit); + const findOptions = { + where: { + name: { + contains: name, + }, + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + const userId = req.user ? req.user.userId : null; + const products = yield productService_js_1.productService.getProducts(findOptions, userId); + res.send(products); + }); + } + GetProductById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + const userId = req.user ? req.user.userId : null; + const product = yield productService_js_1.productService.getProductById(id, userId); + res.send(product); + }); + } + PostProduct(req, res) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_js_1.CreateProduct); + const userFields = __rest(req.body, []); + const product = yield productRepository_js_1.default.create(userFields); + res.status(201).send(product); + }); + } + PatchProductById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + (0, superstruct_1.assert)(req.body, structs_js_1.PatchProduct); + const userFields = __rest(req.body, []); + const Product = yield productRepository_js_1.default.update(id, userFields); + res.send(Product); + }); + } + DeleteProductById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + const Product = yield productRepository_js_1.default.ondelete(id); + res.send(Product); + }); + } + likeProduct(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const productId = req.params.id; + const result = yield productService_js_1.productService.likeProduct(userId, productId); + return res.status(200).json(result); + }); + } +} +exports.default = ProductController; +//# sourceMappingURL=productController.js.map \ No newline at end of file diff --git a/dist/controller/productController.js.map b/dist/controller/productController.js.map new file mode 100644 index 00000000..eb085861 --- /dev/null +++ b/dist/controller/productController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts b/dist/controller/uploadController.d.ts new file mode 100644 index 00000000..d6517695 --- /dev/null +++ b/dist/controller/uploadController.d.ts @@ -0,0 +1,2 @@ +export declare function UploadSingleImage(req: any, res: any): void; +//# sourceMappingURL=uploadController.d.ts.map \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts.map b/dist/controller/uploadController.d.ts.map new file mode 100644 index 00000000..38d4676c --- /dev/null +++ b/dist/controller/uploadController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,QAIzC"} \ No newline at end of file diff --git a/dist/controller/uploadController.js b/dist/controller/uploadController.js new file mode 100644 index 00000000..d249fedb --- /dev/null +++ b/dist/controller/uploadController.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UploadSingleImage = UploadSingleImage; +function UploadSingleImage(req, res) { + const { filename } = req.file; + const path = `files/${filename}`; + res.json({ path }); +} +//# sourceMappingURL=uploadController.js.map \ No newline at end of file diff --git a/dist/controller/uploadController.js.map b/dist/controller/uploadController.js.map new file mode 100644 index 00000000..0301614f --- /dev/null +++ b/dist/controller/uploadController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAEA,8CAIC;AAJD,SAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG;IACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/dist/controller/userController.d.ts b/dist/controller/userController.d.ts new file mode 100644 index 00000000..f67785ac --- /dev/null +++ b/dist/controller/userController.d.ts @@ -0,0 +1,11 @@ +export default class UserServiceController { + register(req: any, res: any): Promise; + login(req: any, res: any): Promise; + refresh(req: any, res: any): Promise; + GetMe(req: any, res: any): Promise; + updateMe(req: any, res: any): Promise; + updateMyPassword(req: any, res: any): Promise; + getMyProducts(req: any, res: any): Promise; + getMyLikedProducts(req: any, res: any): Promise; +} +//# sourceMappingURL=userController.d.ts.map \ No newline at end of file diff --git a/dist/controller/userController.d.ts.map b/dist/controller/userController.d.ts.map new file mode 100644 index 00000000..c6c4aee1 --- /dev/null +++ b/dist/controller/userController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userController.d.ts","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAChC,QAAQ,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAKjB,KAAK,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAad,OAAO,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAahB,KAAK,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAKd,QAAQ,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMjB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMzB,aAAa,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMtB,kBAAkB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAKpC"} \ No newline at end of file diff --git a/dist/controller/userController.js b/dist/controller/userController.js new file mode 100644 index 00000000..cf383018 --- /dev/null +++ b/dist/controller/userController.js @@ -0,0 +1,87 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const userService_js_1 = require("../services/userService.js"); +class UserServiceController { + register(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userService_js_1.userService.createUser(req.body); + return res.status(201).json(user); + }); + } + login(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { email, password } = req.body; + const user = yield userService_js_1.userService.getUser(email, password); + const accessToken = userService_js_1.userService.createToken(user); + const refreshToken = userService_js_1.userService.createToken(user, 'refresh'); + yield userService_js_1.userService.updateUser(user.id, { refreshToken }); + res.cookie('refreshToken', refreshToken, { + httpOnly: true, + sameSite: 'none', + secure: true + }); + return res.status(200).json({ accessToken }); + }); + } + refresh(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { refreshToken } = req.cookies; + const { userId } = req.auth; + const { accessToken, newRefreshToken } = yield userService_js_1.userService.refreshToken(userId, refreshToken); // 변경 + yield userService_js_1.userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 + res.cookie('refreshToken', newRefreshToken, { + path: '/token/refresh', + httpOnly: true, + sameSite: 'none', + secure: true, + }); + return res.json({ accessToken }); + }); + } + GetMe(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const user = yield userService_js_1.userService.getUserById(userId); + return res.status(200).json(user); + }); + } + updateMe(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const updatedUser = yield userService_js_1.userService.updateProfile(userId, req.body); + return res.status(200).json(updatedUser); + }); + } + updateMyPassword(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const updatedUser = yield userService_js_1.userService.updatePassword(userId, req.body); + return res.status(200).json(updatedUser); + }); + } + getMyProducts(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const products = yield userService_js_1.userService.getProductsByUserId(userId); + return res.status(200).json(products); + }); + } + getMyLikedProducts(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const products = yield userService_js_1.userService.getLikedProductsByUserId(userId); + return res.status(200).json(products); + }); + } +} +exports.default = UserServiceController; +//# sourceMappingURL=userController.js.map \ No newline at end of file diff --git a/dist/controller/userController.js.map b/dist/controller/userController.js.map new file mode 100644 index 00000000..7504c7c5 --- /dev/null +++ b/dist/controller/userController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userController.js","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts b/dist/libs/Handler/errorHandler.d.ts new file mode 100644 index 00000000..fda3843a --- /dev/null +++ b/dist/libs/Handler/errorHandler.d.ts @@ -0,0 +1,11 @@ +declare const errorHandler: (err: any, req: any, res: any, next: any) => any; +export default errorHandler; +/** + * 모든 커스텀 에러의 기본이 되는 클래스 + * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. + */ +export declare class CustomError extends Error { + constructor(statusCode?: number, message?: string, data?: {}); +} +export declare const globalErrorHandler: (err: any, req: any, res: any, next: any) => void; +//# sourceMappingURL=errorHandler.d.ts.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts.map b/dist/libs/Handler/errorHandler.d.ts.map new file mode 100644 index 00000000..e04e6e35 --- /dev/null +++ b/dist/libs/Handler/errorHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,YAAY,GAAI,QAAG,EAAE,QAAG,EAAE,QAAG,EAAE,SAAI,QAyCxC,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;gBACtB,UAAU,SAAM,EAAE,OAAO,SAAK,EAAE,IAAI,KAAK;CASxD;AAED,eAAO,MAAM,kBAAkB,GAAI,QAAG,EAAE,QAAG,EAAE,QAAG,EAAE,SAAI,SAcrD,CAAC"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js b/dist/libs/Handler/errorHandler.js new file mode 100644 index 00000000..47aaf7bb --- /dev/null +++ b/dist/libs/Handler/errorHandler.js @@ -0,0 +1,80 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.globalErrorHandler = exports.CustomError = void 0; +const multer_1 = __importDefault(require("multer")); +const errorHandler = (err, req, res, next) => { + if (err.name === 'PrismaCluentInitializationError') + return res.status(500).json({ + success: false, + message: '데이터베이스 연결에 실패했씁니다. DATABASE_URL을 확인해주세요.' + }); + if (err.code === 'P2002') + return res.status(409).json({ + success: false, + message: '중복된 데이터가 존재합니다.' + }); + if (err.code === 'P2025') { + return res.status(404).json({ + success: false, + message: '요청한 데이터를 찾을 수 없습니다.' + }); + } + if (err instanceof multer_1.default.MulterError) { + if (err.code === 'LIMIT_FILE_SIZE') { + return res.status(400).json({ + success: false, + message: '파일 크기가 너무 큽니다.', + }); + } + if (err.code === 'LIMIT_FILE_COUNT') { + return res.status(400).json({ + success: false, + message: '파일 개수가 너무 많습니다.', + }); + } + } + const statusCode = err.statusCode || err.status || 500; + const message = err.message || '서버 오류가 발생했습니다.'; + const path = err.path || null; + const response = { + success: false, + message, + path + }; + return res.status(statusCode).json(response); +}; +exports.default = errorHandler; +/** + * 모든 커스텀 에러의 기본이 되는 클래스 + * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. + */ +class CustomError extends Error { + constructor(statusCode = 500, message = '', data = {}) { + super(message); + this.statusCode = statusCode; + // Error stack 추적을 위해 클래스 이름 설정 + this.name = this.constructor.name; + this.data = data; + // 생성자 함수를 호출 스택에서 제외 + Error.captureStackTrace(this, this.constructor); + } +} +exports.CustomError = CustomError; +const globalErrorHandler = (err, req, res, next) => { + // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. + const statusCode = err.statusCode || 500; + const message = err.message || 'Internal Server Error'; + // 에러를 콘솔에 기록 + console.error(`[${err.name} - ${statusCode}] ${err.message}`, err.stack); + res.status(statusCode).json({ + status: 'error', + message: message, + // 개발 환경에서만 스택 트레이스를 포함할 수 있습니다. + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, + }); +}; +exports.globalErrorHandler = globalErrorHandler; +//# sourceMappingURL=errorHandler.js.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js.map b/dist/libs/Handler/errorHandler.js.map new file mode 100644 index 00000000..471e837e --- /dev/null +++ b/dist/libs/Handler/errorHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts b/dist/libs/catchAsync.d.ts new file mode 100644 index 00000000..eea73b6b --- /dev/null +++ b/dist/libs/catchAsync.d.ts @@ -0,0 +1,3 @@ +export declare const catchAsync: (fn: any) => (req: any, res: any, next: any) => void; +export declare const catchAsyncAll: (...fns: any[]) => ((req: any, res: any, next: any) => void)[]; +//# sourceMappingURL=catchAsync.d.ts.map \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts.map b/dist/libs/catchAsync.d.ts.map new file mode 100644 index 00000000..4a1cfdcc --- /dev/null +++ b/dist/libs/catchAsync.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"catchAsync.d.ts","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,OAAE,MAAM,QAAG,EAAE,QAAG,EAAE,SAAI,SAEhD,CAAC;AACF,eAAO,MAAM,aAAa,GAAI,GAAG,UAAG,gDAAwB,CAAC"} \ No newline at end of file diff --git a/dist/libs/catchAsync.js b/dist/libs/catchAsync.js new file mode 100644 index 00000000..4c9a11b1 --- /dev/null +++ b/dist/libs/catchAsync.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.catchAsyncAll = exports.catchAsync = void 0; +const catchAsync = (fn) => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); +}; +exports.catchAsync = catchAsync; +const catchAsyncAll = (...fns) => fns.map(exports.catchAsync); +exports.catchAsyncAll = catchAsyncAll; +//# sourceMappingURL=catchAsync.js.map \ No newline at end of file diff --git a/dist/libs/catchAsync.js.map b/dist/libs/catchAsync.js.map new file mode 100644 index 00000000..d3e83ed2 --- /dev/null +++ b/dist/libs/catchAsync.js.map @@ -0,0 +1 @@ +{"version":3,"file":"catchAsync.js","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":";;;AAAO,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACjD,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB;AACK,MAAM,aAAa,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAU,CAAC,CAAC;AAAhD,QAAA,aAAa,iBAAmC"} \ No newline at end of file diff --git a/dist/libs/constants.d.ts b/dist/libs/constants.d.ts new file mode 100644 index 00000000..58c966e4 --- /dev/null +++ b/dist/libs/constants.d.ts @@ -0,0 +1,9 @@ +import { PrismaClient } from '@prisma/client'; +import express, { Request, Response, NextFunction } from 'express'; +export declare const EXPRESS: typeof express; +export declare const PORT: string | number; +export declare const prismaClient: PrismaClient; +export type ExpressRequest = Request; +export type ExpressResponse = Response; +export type ExpressNextFunction = NextFunction; +//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/dist/libs/constants.d.ts.map b/dist/libs/constants.d.ts.map new file mode 100644 index 00000000..9f2dcff1 --- /dev/null +++ b/dist/libs/constants.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAInE,eAAO,MAAM,OAAO,gBAAU,CAAC;AAC/B,eAAO,MAAM,IAAI,iBAA2B,CAAC;AAC7C,eAAO,MAAM,YAAY,gIAAmB,CAAC;AAG7C,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AACrC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;AACvC,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/constants.js b/dist/libs/constants.js new file mode 100644 index 00000000..6d819d84 --- /dev/null +++ b/dist/libs/constants.js @@ -0,0 +1,47 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.prismaClient = exports.PORT = exports.EXPRESS = void 0; +const dotenv = __importStar(require("dotenv")); +const client_1 = require("@prisma/client"); +const express_1 = __importDefault(require("express")); +dotenv.config(); +exports.EXPRESS = express_1.default; +exports.PORT = process.env.PORT || 3000; +exports.prismaClient = new client_1.PrismaClient; +//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/dist/libs/constants.js.map b/dist/libs/constants.js.map new file mode 100644 index 00000000..b55f83c1 --- /dev/null +++ b/dist/libs/constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,2CAA8C;AAC9C,sDAAmE;AAEnE,MAAM,CAAC,MAAM,EAAE,CAAC;AAEH,QAAA,OAAO,GAAG,iBAAO,CAAC;AAClB,QAAA,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAChC,QAAA,YAAY,GAAG,IAAI,qBAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts b/dist/libs/corsSetUp.d.ts new file mode 100644 index 00000000..898f7ad0 --- /dev/null +++ b/dist/libs/corsSetUp.d.ts @@ -0,0 +1,3 @@ +import 'dotenv/config'; +export declare const getCorsOrigin: () => string | string[]; +//# sourceMappingURL=corsSetUp.d.ts.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts.map b/dist/libs/corsSetUp.d.ts.map new file mode 100644 index 00000000..a54081d8 --- /dev/null +++ b/dist/libs/corsSetUp.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"corsSetUp.d.ts","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,eAAO,MAAM,aAAa,yBASzB,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.js b/dist/libs/corsSetUp.js new file mode 100644 index 00000000..7563d23d --- /dev/null +++ b/dist/libs/corsSetUp.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getCorsOrigin = void 0; +require("dotenv/config"); +const getCorsOrigin = () => { + const corsOrigin = process.env.CORS_ORIGIN; + if (!corsOrigin || corsOrigin === '*') + return '*'; + if (corsOrigin.includes(',')) + return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); + // 단일 origin (trailing slash 제거) + return corsOrigin.trim().replace(/\/$/, ''); +}; +exports.getCorsOrigin = getCorsOrigin; +//# sourceMappingURL=corsSetUp.js.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.js.map b/dist/libs/corsSetUp.js.map new file mode 100644 index 00000000..e7570d29 --- /dev/null +++ b/dist/libs/corsSetUp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"corsSetUp.js","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":";;;AAAA,yBAAuB;AAChB,MAAM,aAAa,GAAG,GAAG,EAAE;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC3C,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAEnF,gCAAgC;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC;AATW,QAAA,aAAa,iBASxB"} \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts b/dist/libs/interfaces.d.ts new file mode 100644 index 00000000..012e7ebf --- /dev/null +++ b/dist/libs/interfaces.d.ts @@ -0,0 +1,73 @@ +import { Prisma } from '@prisma/client'; +export interface ProductType { + id: number; + name: string; + description?: string; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + comments?: CommentType[]; + productLikes?: ProductLikeType[]; +} +export interface ArticleType { + id: number; + title: string; + content: string; + createdAt: Date; + comments: CommentType[]; + articleLikes: ArticleLikeType[]; +} +export interface CommentType { + id: number; + content: string; + createdAt: Date; + updatedAt: Date; + productId?: number; + articleId?: number; + product?: ProductType; + article?: ArticleType; +} +export interface UserType { + id: number; + email: string; + password: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + refreshToken?: string | null; + productLikes?: ProductLikeType[]; + articleLikes?: ArticleLikeType[]; +} +export interface UpdateUserPasswordType { + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} +export interface ProductLikeType { + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + productId: number; + user: UserType; + product: ProductType; +} +export interface ArticleLikeType { + id: number; + userId: number; + articleId: number; + user: UserType; + article: ArticleType; + createdAt: Date; +} +export type ProductFindOptions = Prisma.ProductFindManyArgs; +export type ArticleFindOptions = Prisma.ArticleFindManyArgs; +export interface ProductWithLikes extends ProductType { + productLikes: ProductLikeType[]; +} +export interface ProductWithIsLiked extends ProductType { + isLiked: boolean; +} +//# sourceMappingURL=interfaces.d.ts.map \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts.map b/dist/libs/interfaces.d.ts.map new file mode 100644 index 00000000..230b1e29 --- /dev/null +++ b/dist/libs/interfaces.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC;AACD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,eAAe,EAAE,CAAC;CACnC;AACD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,CAAC;CACzB;AACD,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC;AAGD,MAAM,WAAW,sBAAsB;IAEnC,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;CACxB;AACD,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAE5D,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACjD,YAAY,EAAE,eAAe,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACnD,OAAO,EAAE,OAAO,CAAC;CACpB"} \ No newline at end of file diff --git a/dist/libs/interfaces.js b/dist/libs/interfaces.js new file mode 100644 index 00000000..db919115 --- /dev/null +++ b/dist/libs/interfaces.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=interfaces.js.map \ No newline at end of file diff --git a/dist/libs/interfaces.js.map b/dist/libs/interfaces.js.map new file mode 100644 index 00000000..cbb18070 --- /dev/null +++ b/dist/libs/interfaces.js.map @@ -0,0 +1 @@ +{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts new file mode 100644 index 00000000..371115b3 --- /dev/null +++ b/dist/main.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/main.d.ts.map b/dist/main.d.ts.map new file mode 100644 index 00000000..28b87d89 --- /dev/null +++ b/dist/main.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 00000000..e5d4ec53 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,26 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("./libs/constants"); +const cors_1 = __importDefault(require("cors")); +const routerManager_1 = require("./Routers/routerManager"); +const corsSetUp_1 = require("./libs/corsSetUp"); +const errorHandler_1 = __importDefault(require("./libs/Handler/errorHandler")); +const app = (0, constants_1.EXPRESS)(); +app.use((0, cors_1.default)({ + origin: (0, corsSetUp_1.getCorsOrigin)(), + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], + allowedHeaders: ['Content-Type', 'Authorization'] +})); +app.use(constants_1.EXPRESS.json()); +app.use(constants_1.EXPRESS.urlencoded({ extended: true })); +app.use('/upload', constants_1.EXPRESS.static('upload')); +app.use('/', routerManager_1.RouterManager); +app.use(errorHandler_1.default); +app.listen(constants_1.PORT, () => { + console.log(`Server is running`); +}); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map new file mode 100644 index 00000000..4a49c239 --- /dev/null +++ b/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;AAAA,gDAAiD;AACjD,gDAAwB;AACxB,2DAAwD;AACxD,gDAAiD;AACjD,+EAAuD;AAEvD,MAAM,GAAG,GAAG,IAAA,mBAAO,GAAE,CAAC;AAGtB,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC;IACT,MAAM,EAAE,IAAA,yBAAa,GAAE;IACvB,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC;IAClD,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;CACpD,CAAC,CAAC,CAAC;AAIJ,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAI7C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,6BAAa,CAAC,CAAC;AAI5B,GAAG,CAAC,GAAG,CAAC,sBAAY,CAAC,CAAC;AAEtB,GAAG,CAAC,MAAM,CAAC,gBAAI,EAAE,GAAG,EAAE;IAClB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts b/dist/middlewares/auth.d.ts new file mode 100644 index 00000000..2c7f9c72 --- /dev/null +++ b/dist/middlewares/auth.d.ts @@ -0,0 +1,17 @@ +declare function verifyProduectAuth(req: any, res: any, next: any): Promise; +declare function verifyArticleAuth(req: any, res: any, next: any): Promise; +declare const _default: { + verifyAccessToken: { + (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction): Promise; + unless: typeof import("express-unless").unless; + }; + softVerifyAccessToken: (req: any, res: any, next: any) => void; + verifyProduectAuth: typeof verifyProduectAuth; + verifyArticleAuth: typeof verifyArticleAuth; + verifyRefreshToken: { + (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction): Promise; + unless: typeof import("express-unless").unless; + }; +}; +export default _default; +//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts.map b/dist/middlewares/auth.d.ts.map new file mode 100644 index 00000000..0ca5bae9 --- /dev/null +++ b/dist/middlewares/auth.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":"AA+BA,iBAAe,kBAAkB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,EAAE,IAAI,KAAA,gBAW/C;AAED,iBAAe,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,EAAE,IAAI,KAAA,gBAW9C;;;;;;;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/middlewares/auth.js b/dist/middlewares/auth.js new file mode 100644 index 00000000..51fcbfa1 --- /dev/null +++ b/dist/middlewares/auth.js @@ -0,0 +1,79 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_jwt_1 = require("express-jwt"); +const productRepository_js_1 = __importDefault(require("../repositories/productRepository.js")); +const articleRepository_js_1 = __importDefault(require("../repositories/articleRepository.js")); +const errorHandler_js_1 = require("../libs/Handler/errorHandler.js"); +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); +const verifyAccessToken = (0, express_jwt_1.expressjwt)({ + secret: process.env.JWT_SECRET, + algorithms: ["HS256"], + requestProperty: 'user' +}); +const softVerifyAccessToken = (req, res, next) => { + var _a; + const token = (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.split(' ')[1]; + if (token) { + jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET, (err, user) => { + if (!err) { + req.user = user; + } + next(); + }); + } + else { + next(); + } +}; +const verifyRefreshToken = (0, express_jwt_1.expressjwt)({ + secret: process.env.JWT_SECRET, + algorithms: ['HS256'], + getToken: (req) => req.cookies.refreshToken, +}); +function verifyProduectAuth(req, res, next) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + const product = yield productRepository_js_1.default.findById(id); + if (!product) { + throw new errorHandler_js_1.CustomError(404, 'product not found'); + } + if (product.authorId !== req.user.id) { + throw new errorHandler_js_1.CustomError(403, 'Forbidden'); + } + return next(); + }); +} +; +function verifyArticleAuth(req, res, next) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + const article = yield articleRepository_js_1.default.findById(id); + if (!article) { + throw new errorHandler_js_1.CustomError(404, 'article not found'); + } + if (article.authorId !== req.user.id) { + throw new errorHandler_js_1.CustomError(403, 'Forbidden'); + } + return next(); + }); +} +exports.default = { + verifyAccessToken, + softVerifyAccessToken, + verifyProduectAuth, + verifyArticleAuth, + verifyRefreshToken, +}; +//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/dist/middlewares/auth.js.map b/dist/middlewares/auth.js.map new file mode 100644 index 00000000..feb3e7b3 --- /dev/null +++ b/dist/middlewares/auth.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,6CAAyC;AACzC,gGAAqE;AACrE,gGAAqE;AACrE,qEAA8D;AAC9D,gEAA+B;AAE/B,MAAM,iBAAiB,GAAG,IAAA,wBAAU,EAAC;IACjC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAC9B,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM;CAC1B,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;;IAC7C,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,OAAO,CAAC,aAAa,0CAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACvD,IAAI,KAAK,EAAE,CAAC;QACR,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACpD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACP,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YACpB,CAAC;YACD,IAAI,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,IAAI,EAAE,CAAC;IACX,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,IAAA,wBAAU,EAAC;IAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAC9B,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY;CAC9C,CAAC,CAAC;AACH,SAAe,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI;;QAC5C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,MAAM,8BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAClB,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI;;QAC3C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,MAAM,8BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAClB,CAAC;CAAA;AAED,kBAAe;IACX,iBAAiB;IACjB,qBAAqB;IACrB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;CACrB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts b/dist/repositories/articleLikeRepository.d.ts new file mode 100644 index 00000000..9424a24e --- /dev/null +++ b/dist/repositories/articleLikeRepository.d.ts @@ -0,0 +1,25 @@ +declare function find(userId: any, articleId: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +} | null>; +declare function create(userId: any, articleId: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +}>; +declare function remove(id: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +}>; +declare const _default: { + find: typeof find; + create: typeof create; + remove: typeof remove; +}; +export default _default; +//# sourceMappingURL=articleLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts.map b/dist/repositories/articleLikeRepository.d.ts.map new file mode 100644 index 00000000..fe0b5184 --- /dev/null +++ b/dist/repositories/articleLikeRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;UASpC;AAED,iBAAe,MAAM,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;GAOtC;AAED,iBAAe,MAAM,CAAC,EAAE,KAAA;;;;;GAMvB;;;;;;AAED,wBAIE"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js b/dist/repositories/articleLikeRepository.js new file mode 100644 index 00000000..37052fc7 --- /dev/null +++ b/dist/repositories/articleLikeRepository.js @@ -0,0 +1,49 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function find(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.findUnique({ + where: { + userId_articleId: { + userId, + articleId, + }, + }, + }); + }); +} +function create(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.create({ + data: { + userId, + articleId, + }, + }); + }); +} +function remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + find, + create, + remove, +}; +//# sourceMappingURL=articleLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js.map b/dist/repositories/articleLikeRepository.js.map new file mode 100644 index 00000000..f24384c6 --- /dev/null +++ b/dist/repositories/articleLikeRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAM,EAAE,SAAS;;QACjC,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAM,EAAE,SAAS;;QACnC,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAE;;QACpB,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts b/dist/repositories/articleRepository.d.ts new file mode 100644 index 00000000..96470f6a --- /dev/null +++ b/dist/repositories/articleRepository.d.ts @@ -0,0 +1,59 @@ +import { ArticleType, ArticleFindOptions } from '../libs/interfaces.js'; +declare function findById(id: number, userId: number): Promise<{ + articleLikes: { + id: number; + createdAt: Date; + userId: number; + articleId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function findAll(findOptions: ArticleFindOptions, userId: number): Promise<({ + articleLikes: { + id: number; + createdAt: Date; + userId: number; + articleId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +})[]>; +declare function create(userFields: ArticleType): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function update(id: number, data: ArticleFindOptions): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function ondelete(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare const _default: { + findById: typeof findById; + findAll: typeof findAll; + create: typeof create; + update: typeof update; + ondelete: typeof ondelete; +}; +export default _default; +//# sourceMappingURL=articleRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts.map b/dist/repositories/articleRepository.d.ts.map new file mode 100644 index 00000000..7ece1f57 --- /dev/null +++ b/dist/repositories/articleRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAExE,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;GAUjD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,WAAW;;;;;;GAQ5C;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;;;;;;GASzD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.js b/dist/repositories/articleRepository.js new file mode 100644 index 00000000..f072c370 --- /dev/null +++ b/dist/repositories/articleRepository.js @@ -0,0 +1,79 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +function findById(id, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return constants_js_1.prismaClient.article.findUniqueOrThrow({ + where: { + id, + }, + include, + }); + }); +} +function findAll(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return constants_js_1.prismaClient.article.findMany(Object.assign(Object.assign({}, findOptions), { include })); + }); +} +function create(userFields) { + return __awaiter(this, void 0, void 0, function* () { + const { createdAt, comments, articleLikes } = userFields, NewuserFields = __rest(userFields, ["createdAt", "comments", "articleLikes"]); + return yield constants_js_1.prismaClient.article.create({ + data: Object.assign({}, NewuserFields), + }); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + return constants_js_1.prismaClient.article.update({ + where: { + id, + }, + data: Object.assign({}, data), + }); + }); +} +function ondelete(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_js_1.prismaClient.article.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + findById, + findAll, + create, + update, + ondelete, +}; +//# sourceMappingURL=articleRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.js.map b/dist/repositories/articleRepository.js.map new file mode 100644 index 00000000..2ec0985c --- /dev/null +++ b/dist/repositories/articleRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRepository.js","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,uDAAoD;AAGpD,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAc;;QAC9C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,2BAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QAEF,OAAO,2BAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAAuB;;QACzC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAApE,yCAAuD,CAAa,CAAC;QAE3E,OAAO,MAAM,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAwB;;QACtD,OAAO,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,IAAI,CACV;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;CACX,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts b/dist/repositories/productLikeRepository.d.ts new file mode 100644 index 00000000..cff87d62 --- /dev/null +++ b/dist/repositories/productLikeRepository.d.ts @@ -0,0 +1,42 @@ +declare function find(userId: any, productId: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +} | null>; +declare function create(userId: any, productId: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +}>; +declare function remove(id: any): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +}>; +declare function findLikedProductsByUserId(userId: any): Promise<({ + product: { + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + }; +} & { + id: number; + createdAt: Date; + userId: number; + productId: number; +})[]>; +declare const _default: { + find: typeof find; + create: typeof create; + remove: typeof remove; + findLikedProductsByUserId: typeof findLikedProductsByUserId; +}; +export default _default; +//# sourceMappingURL=productLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts.map b/dist/repositories/productLikeRepository.d.ts.map new file mode 100644 index 00000000..e362aad5 --- /dev/null +++ b/dist/repositories/productLikeRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;UASpC;AAED,iBAAe,MAAM,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;GAOtC;AAED,iBAAe,MAAM,CAAC,EAAE,KAAA;;;;;GAMvB;AAED,iBAAe,yBAAyB,CAAC,MAAM,KAAA;;;;;;;;;;;;;;;MAS9C;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js b/dist/repositories/productLikeRepository.js new file mode 100644 index 00000000..a32f5cae --- /dev/null +++ b/dist/repositories/productLikeRepository.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_js_1 = require("../libs/constants.js"); +function find(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_js_1.prismaClient.productLike.findUnique({ + where: { + userId_productId: { + userId, + productId, + }, + }, + }); + }); +} +function create(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_js_1.prismaClient.productLike.create({ + data: { + userId, + productId, + }, + }); + }); +} +function remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_js_1.prismaClient.productLike.delete({ + where: { + id, + }, + }); + }); +} +function findLikedProductsByUserId(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_js_1.prismaClient.productLike.findMany({ + where: { + userId, + }, + include: { + product: true, + }, + }); + }); +} +exports.default = { + find, + create, + remove, + findLikedProductsByUserId, +}; +//# sourceMappingURL=productLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js.map b/dist/repositories/productLikeRepository.js.map new file mode 100644 index 00000000..d57fdece --- /dev/null +++ b/dist/repositories/productLikeRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,uDAAoD;AAEpD,SAAe,IAAI,CAAC,MAAM,EAAE,SAAS;;QACjC,OAAO,2BAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAM,EAAE,SAAS;;QACnC,OAAO,2BAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAE;;QACpB,OAAO,2BAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,yBAAyB,CAAC,MAAM;;QAC3C,OAAO,2BAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE;gBACH,MAAM;aACT;YACD,OAAO,EAAE;gBACL,OAAO,EAAE,IAAI;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;IACN,yBAAyB;CAC5B,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts b/dist/repositories/productRepository.d.ts new file mode 100644 index 00000000..ebcb3b55 --- /dev/null +++ b/dist/repositories/productRepository.d.ts @@ -0,0 +1,79 @@ +import { ProductType, ProductFindOptions } from './../libs/interfaces'; +declare function findByUserId(userId: number): Promise<{ + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +}[]>; +declare function findById(id: number, userId: number): Promise<{ + productLikes: { + id: number; + createdAt: Date; + userId: number; + productId: number; + }[]; +} & { + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +}>; +declare function findAll(findOptions: ProductFindOptions, userId: number): Promise<({ + productLikes: { + id: number; + createdAt: Date; + userId: number; + productId: number; + }[]; +} & { + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +})[]>; +declare function update(id: number, data: ProductType): Promise<{ + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +}>; +declare function create(userFields: ProductType): Promise<{ + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +}>; +declare function ondelete(id: number): Promise<{ + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; +}>; +declare const _default: { + findById: typeof findById; + findAll: typeof findAll; + update: typeof update; + create: typeof create; + ondelete: typeof ondelete; + findByUserId: typeof findByUserId; +}; +export default _default; +//# sourceMappingURL=productRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts.map b/dist/repositories/productRepository.d.ts.map new file mode 100644 index 00000000..b39fee46 --- /dev/null +++ b/dist/repositories/productRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGvE,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;GAUjD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW;;;;;;;;GASlD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,WAAW;;;;;;;;GAO5C;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;GAMjC;;;;;;;;;AAED,wBAOE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.js b/dist/repositories/productRepository.js new file mode 100644 index 00000000..6e21821b --- /dev/null +++ b/dist/repositories/productRepository.js @@ -0,0 +1,91 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findByUserId(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.product.findMany({ + where: { + id: userId, + }, + }); + }); +} +function findById(id, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.product.findUniqueOrThrow({ + where: { + id, + }, + include, + }); + }); +} +function findAll(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.product.findMany(Object.assign(Object.assign({}, findOptions), { include })); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id: _, createdAt, updatedAt, comments, productLikes } = data, updateData = __rest(data, ["id", "createdAt", "updatedAt", "comments", "productLikes"]); + return constants_1.prismaClient.product.update({ + where: { + id, + }, + data: updateData, + }); + }); +} +function create(userFields) { + return __awaiter(this, void 0, void 0, function* () { + const { createdAt, updatedAt, comments, productLikes } = userFields, NewuserFields = __rest(userFields, ["createdAt", "updatedAt", "comments", "productLikes"]); + return yield constants_1.prismaClient.product.create({ + data: Object.assign({}, NewuserFields), + }); + }); +} +function ondelete(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_1.prismaClient.product.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + findById, + findAll, + update, + create, + ondelete, + findByUserId, +}; +//# sourceMappingURL=productRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productRepository.js.map b/dist/repositories/productRepository.js.map new file mode 100644 index 00000000..d0ad132f --- /dev/null +++ b/dist/repositories/productRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAIjD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAc;;QAC9C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAiB;;QAC/C,6DAA6D;QAC7D,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAA7E,4DAAsE,CAAO,CAAC;QACpF,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAAuB;;QACzC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAA/E,sDAAkE,CAAa,CAAC;QACtF,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;CACf,CAAC"} \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts b/dist/repositories/userRepository.d.ts new file mode 100644 index 00000000..3d839f5f --- /dev/null +++ b/dist/repositories/userRepository.d.ts @@ -0,0 +1,49 @@ +import { UserType } from "./../libs/interfaces"; +declare function findById(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + email: string; + nickname: string; + image: string | null; + password: string; + refreshToken: string | null; +} | null>; +declare function findByEmail(email: string): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + email: string; + nickname: string; + image: string | null; + password: string; + refreshToken: string | null; +} | null>; +declare function save(user: UserType): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + email: string; + nickname: string; + image: string | null; + password: string; + refreshToken: string | null; +}>; +declare function update(id: number, data: Partial): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + email: string; + nickname: string; + image: string | null; + password: string; + refreshToken: string | null; +}>; +declare const _default: { + findById: typeof findById; + findByEmail: typeof findByEmail; + save: typeof save; + update: typeof update; +}; +export default _default; +//# sourceMappingURL=userRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts.map b/dist/repositories/userRepository.d.ts.map new file mode 100644 index 00000000..6789714b --- /dev/null +++ b/dist/repositories/userRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAMjC;AAED,iBAAe,WAAW,CAAC,KAAK,EAAE,MAAM;;;;;;;;;UAMvC;AAED,iBAAe,IAAI,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;GAQjC;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;GAWxD;;;;;;;AAUD,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/userRepository.js b/dist/repositories/userRepository.js new file mode 100644 index 00000000..04deef26 --- /dev/null +++ b/dist/repositories/userRepository.js @@ -0,0 +1,78 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findById(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.user.findUnique({ + where: { + id, + }, + }); + }); +} +function findByEmail(email) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_1.prismaClient.user.findUnique({ + where: { + email, + }, + }); + }); +} +; +function save(user) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.user.create({ + data: { + email: user.email, + nickname: user.nickname, + password: user.password, + }, + }); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + const { createdAt, updatedAt, articleLikes, productLikes } = data, Newdata = __rest(data, ["createdAt", "updatedAt", "articleLikes", "productLikes"]); + return constants_1.prismaClient.user.update({ + where: { + id, + }, + data: Object.assign({}, Newdata), + }); + }); +} +// async function createOrUpdate(provider: string, providerId: string, email: string, name: string) { +// return prismaClient.user.upsert({ +// where: { provider, providerId }, +// update: { email, name }, +// create: { provider, providerId, email, name }, +// }); +// } +exports.default = { + findById, + findByEmail, + save, + update +}; +//# sourceMappingURL=userRepository.js.map \ No newline at end of file diff --git a/dist/repositories/userRepository.js.map b/dist/repositories/userRepository.js.map new file mode 100644 index 00000000..a4c7fa52 --- /dev/null +++ b/dist/repositories/userRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userRepository.js","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAGjD,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,KAAa;;QACpC,OAAO,MAAM,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE;gBACH,KAAK;aACR;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,IAAI,CAAC,IAAc;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,IAAI,EAAE;gBACF,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aAC1B;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,KAAiB,IAAI,EAAhB,OAAO,UAAK,IAAI,EAAvE,0DAAgE,CAAO,CAAC;QAE9E,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,OAAO,CACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,qGAAqG;AACrG,wCAAwC;AACxC,2CAA2C;AAC3C,mCAAmC;AACnC,yDAAyD;AACzD,UAAU;AACV,IAAI;AAEJ,kBAAe;IACX,QAAQ;IACR,WAAW;IACX,IAAI;IACJ,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.d.ts b/dist/services/articleService.d.ts new file mode 100644 index 00000000..b2f37a7b --- /dev/null +++ b/dist/services/articleService.d.ts @@ -0,0 +1,25 @@ +import { ArticleFindOptions } from './../libs/interfaces'; +declare class ArticleService { + likeArticle(userId: number, articleId: number): Promise<{ + liked: boolean; + }>; + getArticleById(articleId: number, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; + }>; + getArticles(findOptions: ArticleFindOptions, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; + }[]>; +} +export declare const articleService: ArticleService; +export {}; +//# sourceMappingURL=articleService.d.ts.map \ No newline at end of file diff --git a/dist/services/articleService.d.ts.map b/dist/services/articleService.d.ts.map new file mode 100644 index 00000000..6f1f8383 --- /dev/null +++ b/dist/services/articleService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.js b/dist/services/articleService.js new file mode 100644 index 00000000..69373fa4 --- /dev/null +++ b/dist/services/articleService.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.articleService = void 0; +const articleLikeRepository_1 = __importDefault(require("../repositories/articleLikeRepository")); +const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); +class ArticleService { + likeArticle(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + const existingLike = yield articleLikeRepository_1.default.find(userId, articleId); + if (existingLike) { + yield articleLikeRepository_1.default.remove(existingLike.id); + return { liked: false }; + } + else { + yield articleLikeRepository_1.default.create(userId, articleId); + return { liked: true }; + } + }); + } + getArticleById(articleId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const article = yield articleRepository_1.default.findById(articleId, userId); + const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); + }); + } + getArticles(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const articles = yield articleRepository_1.default.findAll(findOptions, userId); + return articles.map((article) => { + const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); + }); + }); + } +} +exports.articleService = new ArticleService(); +//# sourceMappingURL=articleService.js.map \ No newline at end of file diff --git a/dist/services/articleService.js.map b/dist/services/articleService.js.map new file mode 100644 index 00000000..bb8c3b10 --- /dev/null +++ b/dist/services/articleService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kGAA0E;AAC1E,0FAAkE;AAGlE,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.d.ts b/dist/services/productService.d.ts new file mode 100644 index 00000000..3ba1eccb --- /dev/null +++ b/dist/services/productService.d.ts @@ -0,0 +1,29 @@ +import { ProductFindOptions } from '../libs/interfaces'; +declare class ProductService { + likeProduct(userId: number, productId: number): Promise<{ + liked: boolean; + }>; + getProductById(productId: number, userId: number): Promise<{ + isLiked: boolean; + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + }>; + getProducts(findOptions: ProductFindOptions, userId: number): Promise<{ + isLiked: boolean; + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + }[]>; +} +export declare const productService: ProductService; +export {}; +//# sourceMappingURL=productService.d.ts.map \ No newline at end of file diff --git a/dist/services/productService.d.ts.map b/dist/services/productService.d.ts.map new file mode 100644 index 00000000..49eab074 --- /dev/null +++ b/dist/services/productService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.js b/dist/services/productService.js new file mode 100644 index 00000000..045d62ec --- /dev/null +++ b/dist/services/productService.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.productService = void 0; +const productRepository_1 = __importDefault(require("../repositories/productRepository")); +const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); +class ProductService { + likeProduct(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + const existingLike = yield productLikeRepository_1.default.find(userId, productId); + if (existingLike) { + yield productLikeRepository_1.default.remove(existingLike.id); + return { liked: false }; + } + else { + yield productLikeRepository_1.default.create(userId, productId); + return { liked: true }; + } + }); + } + getProductById(productId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const product = yield productRepository_1.default.findById(productId, userId); + const { productLikes } = product, rest = __rest(product, ["productLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); + }); + } + getProducts(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productRepository_1.default.findAll(findOptions, userId); + return products.map((product) => { + const { productLikes } = product, rest = __rest(product, ["productLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); + }); + }); + } +} +exports.productService = new ProductService(); +//# sourceMappingURL=productService.js.map \ No newline at end of file diff --git a/dist/services/productService.js.map b/dist/services/productService.js.map new file mode 100644 index 00000000..865751b4 --- /dev/null +++ b/dist/services/productService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,kGAA0E;AAI1E,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.d.ts b/dist/services/userService.d.ts new file mode 100644 index 00000000..00217b83 --- /dev/null +++ b/dist/services/userService.d.ts @@ -0,0 +1,107 @@ +import { UserType, UpdateUserPasswordType } from "./../libs/interfaces"; +declare class UserService { + createUser(user: UserType): Promise<{ + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }>; + filterSensitivceUserData(user: UserType): { + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }; + getUser(email: string, password: string): Promise<{ + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }>; + getUserById(id: number): Promise<{ + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }>; + updateProfile(id: number, data: UserType): Promise<{ + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }>; + updatePassword(id: number, data: UpdateUserPasswordType): Promise<{ + id: number; + email: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + productLikes?: import("./../libs/interfaces").ProductLikeType[]; + articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; + }>; + getProductsByUserId(id: number): Promise<{ + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + }[]>; + getLikedProductsByUserId(id: number): Promise<({ + product: { + id: number; + name: string; + description: string | null; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + }; + } & { + id: number; + createdAt: Date; + userId: number; + productId: number; + })[]>; + updateUser(id: number, data: UserType): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + email: string; + nickname: string; + image: string | null; + password: string; + refreshToken: string | null; + }>; + verifyPassword(inputPassword: string, savedPassword: string): Promise; + createToken(user: UserType, type?: string): string; + refreshToken(userId: number, refreshToken: string): Promise<{ + accessToken: string; + newRefreshToken: string; + }>; +} +export declare const userService: UserService; +export {}; +//# sourceMappingURL=userService.d.ts.map \ No newline at end of file diff --git a/dist/services/userService.d.ts.map b/dist/services/userService.d.ts.map new file mode 100644 index 00000000..07e47c02 --- /dev/null +++ b/dist/services/userService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAQxE,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;IAe/B,wBAAwB,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;IAKjC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;IASvC,WAAW,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;IAQtB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;;;;;;;;;;IASxC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB;;;;;;;;;;IAiBvD,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;;;;;;;;;;IAGrC,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUnC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.js b/dist/services/userService.js new file mode 100644 index 00000000..f9f6bf0c --- /dev/null +++ b/dist/services/userService.js @@ -0,0 +1,151 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.userService = void 0; +const superstruct_1 = require("superstruct"); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +const userRepository_1 = __importDefault(require("../repositories/userRepository")); +const productRepository_1 = __importDefault(require("../repositories/productRepository")); +const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); +const bcrypt_1 = __importDefault(require("bcrypt")); +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); +const userStructs_1 = require("../structs/userStructs"); +function hashingPassword(password) { + return __awaiter(this, void 0, void 0, function* () { + // 함수 추가 + return bcrypt_1.default.hash(password, 12); + }); +} +class UserService { + createUser(user) { + return __awaiter(this, void 0, void 0, function* () { + const existedUser = yield userRepository_1.default.findByEmail(user.email); + if (existedUser) { + throw new errorHandler_1.CustomError(422, 'User already exists', { + email: user.email, + }); + } + const hashedPassword = yield hashingPassword(user.password); + const createdUser = yield userRepository_1.default.save(Object.assign(Object.assign({}, user), { password: hashedPassword })); + return this.filterSensitivceUserData(createdUser); + }); + } + filterSensitivceUserData(user) { + const { password, refreshToken } = user, rest = __rest(user, ["password", "refreshToken"]); + return rest; + } + getUser(email, password) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findByEmail(email); + if (!user) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + yield this.verifyPassword(password, user.password); + return this.filterSensitivceUserData(user); + }); + } + getUserById(id) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findById(id); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + return this.filterSensitivceUserData(user); + }); + } + updateProfile(id, data) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(data, userStructs_1.PatchUser); + const user = yield userRepository_1.default.update(id, data); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + return this.filterSensitivceUserData(user); + }); + } + updatePassword(id, data) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(data, userStructs_1.ChangePassword); + const { currentPassword, newPassword, confirmNewPassword } = data; + if (newPassword !== confirmNewPassword) { + throw new errorHandler_1.CustomError(400, "Passwords don't match"); + } + const user = yield userRepository_1.default.findById(id); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + yield this.verifyPassword(currentPassword, user.password); + const hashedPassword = yield hashingPassword(newPassword); + const updatedUser = yield userRepository_1.default.update(id, { password: hashedPassword }); + return this.filterSensitivceUserData(updatedUser); + }); + } + getProductsByUserId(id) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productRepository_1.default.findByUserId(id); + return products; + }); + } + getLikedProductsByUserId(id) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productLikeRepository_1.default.findLikedProductsByUserId(id); + return products; + }); + } + updateUser(id, data) { + return __awaiter(this, void 0, void 0, function* () { + return yield userRepository_1.default.update(id, data); + }); + } + verifyPassword(inputPassword, savedPassword) { + return __awaiter(this, void 0, void 0, function* () { + const isValid = yield bcrypt_1.default.compare(inputPassword, savedPassword); + if (!isValid) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + }); + } + createToken(user, type) { + const JWTsecretKey = process.env.JWT_SECRET; + if (!JWTsecretKey) + throw new errorHandler_1.CustomError(500, 'JWT_SECRET is not defined'); + const payload = { userId: user.id }; + const options = { + expiresIn: type === 'refresh' ? '1d' : '10m', + }; + return jsonwebtoken_1.default.sign(payload, JWTsecretKey, options); + } + refreshToken(userId, refreshToken) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findById(userId); + if (!user || user.refreshToken !== refreshToken) { + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + } + const accessToken = this.createToken(user); // 변경 + const newRefreshToken = this.createToken(user, 'refresh'); // 추가 + return { accessToken, newRefreshToken }; // 변경 + }); + } +} +exports.userService = new UserService(); +//# sourceMappingURL=userService.js.map \ No newline at end of file diff --git a/dist/services/userService.js.map b/dist/services/userService.js.map new file mode 100644 index 00000000..417aeef6 --- /dev/null +++ b/dist/services/userService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userService.js","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/structs/structs.d.ts b/dist/structs/structs.d.ts new file mode 100644 index 00000000..abb29fad --- /dev/null +++ b/dist/structs/structs.d.ts @@ -0,0 +1,52 @@ +import * as s from "superstruct"; +export declare const CreateProduct: s.Struct<{ + name: string; + description: string; + price: number; + tags: string[]; +}, { + name: s.Struct; + description: s.Struct; + price: s.Struct; + tags: s.Struct>; +}>; +export declare const CreateArticle: s.Struct<{ + title: string; + content: string; +}, { + title: s.Struct; + content: s.Struct; +}>; +export declare const CreateComment: s.Struct<{ + content: string; + productId?: number | undefined; + articleId?: number | undefined; +}, { + content: s.Struct; + productId: s.Struct; + articleId: s.Struct; +}>; +export declare const PatchProduct: s.Struct<{ + name?: string | undefined; + description?: string | undefined; + price?: number | undefined; + tags?: string[] | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + name: s.Struct; + description: s.Struct; + price: s.Struct; + tags: s.Struct>; +}>>; +export declare const PatchArticle: s.Struct<{ + title?: string | undefined; + content?: string | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + title: s.Struct; + content: s.Struct; +}>>; +export declare const PatchComment: s.Struct<{ + content?: string | undefined; +}, { + content: s.Struct; +}>; +//# sourceMappingURL=structs.d.ts.map \ No newline at end of file diff --git a/dist/structs/structs.d.ts.map b/dist/structs/structs.d.ts.map new file mode 100644 index 00000000..40033255 --- /dev/null +++ b/dist/structs/structs.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAGjC,eAAO,MAAM,aAAa;;;;;;;;;;EAKxB,CAAC;AAGH,eAAO,MAAM,aAAa;;;;;;EAGxB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;EAepB,CAAC;AAEP,eAAO,MAAM,YAAY;;;;;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;EAAiD,CAAC"} \ No newline at end of file diff --git a/dist/structs/structs.js b/dist/structs/structs.js new file mode 100644 index 00000000..2f15113f --- /dev/null +++ b/dist/structs/structs.js @@ -0,0 +1,65 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PatchComment = exports.PatchArticle = exports.PatchProduct = exports.CreateComment = exports.CreateArticle = exports.CreateProduct = void 0; +const s = __importStar(require("superstruct")); +exports.CreateProduct = s.object({ + name: s.size(s.string(), 1, 100), + description: s.string(), + price: s.min(s.number(), 0), + tags: s.array(s.string()), +}); +exports.CreateArticle = s.object({ + title: s.size(s.string(), 1, 100), + content: s.string(), +}); +exports.CreateComment = s.refine(s.object({ + content: s.string(), + productId: s.optional(s.number()), + articleId: s.optional(s.number()), +}), 'EitherProductIdOrArticleId', //검증 규칙 이름 +(value) => { + const { productId, articleId } = value; + // 실패 케이스: (둘 다 있거나) OR (둘 다 없거나) + if ((productId && articleId) || (!productId && !articleId)) { + return 'Comment must have *either* a productId *or* an articleId, but not both.'; + } + // 성공 케이스: 둘 중 하나만 존재함 + return true; +}); +exports.PatchProduct = s.partial(exports.CreateProduct); +exports.PatchArticle = s.partial(exports.CreateArticle); +exports.PatchComment = s.object({ content: s.optional(s.string()), }); +//# sourceMappingURL=structs.js.map \ No newline at end of file diff --git a/dist/structs/structs.js.map b/dist/structs/structs.js.map new file mode 100644 index 00000000..ad9a7c5d --- /dev/null +++ b/dist/structs/structs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"structs.js","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGpB,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAGU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAEU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACpC,CAAC,EACE,4BAA4B,EAAC,UAAU;AACvC,CAAC,KAAK,EAAE,EAAE;IACN,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACvC,iCAAiC;IACjC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,yEAAyE,CAAC;IACrF,CAAC;IAED,sBAAsB;IACtB,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEM,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts b/dist/structs/userStructs.d.ts new file mode 100644 index 00000000..99320c96 --- /dev/null +++ b/dist/structs/userStructs.d.ts @@ -0,0 +1,31 @@ +import * as s from "superstruct"; +export declare const CreateUser: s.Struct<{ + email: string; + nickname: string; + password: string; + image?: string | undefined; +}, { + email: s.Struct; + nickname: s.Struct; + image: s.Struct; + password: s.Struct; +}>; +export declare const PatchUser: s.Struct<{ + email?: string | undefined; + nickname?: string | undefined; + image?: string | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + email: s.Struct; + nickname: s.Struct; + image: s.Struct; +}>>; +export declare const ChangePassword: s.Struct<{ + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +}, { + currentPassword: s.Struct; + newPassword: s.Struct; + confirmNewPassword: s.Struct; +}>; +//# sourceMappingURL=userStructs.d.ts.map \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts.map b/dist/structs/userStructs.d.ts.map new file mode 100644 index 00000000..c3a5c9b7 --- /dev/null +++ b/dist/structs/userStructs.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userStructs.d.ts","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAKjC,eAAO,MAAM,UAAU;;;;;;;;;;EAKrB,CAAC;AAGH,eAAO,MAAM,SAAS;;;;;;;;GAInB,CAAC;AAEJ,eAAO,MAAM,cAAc;;;;;;;;EAIzB,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.js b/dist/structs/userStructs.js new file mode 100644 index 00000000..bde1f424 --- /dev/null +++ b/dist/structs/userStructs.js @@ -0,0 +1,59 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChangePassword = exports.PatchUser = exports.CreateUser = void 0; +const s = __importStar(require("superstruct")); +const is_email_1 = __importDefault(require("is-email")); +const email = s.refine(s.string(), 'is_email', (v) => (0, is_email_1.default)(v)); +exports.CreateUser = s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), + password: s.string(), +}); +exports.PatchUser = s.partial(s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), +})); +exports.ChangePassword = s.object({ + currentPassword: s.string(), + newPassword: s.string(), + confirmNewPassword: s.string(), +}); +//# sourceMappingURL=userStructs.js.map \ No newline at end of file diff --git a/dist/structs/userStructs.js.map b/dist/structs/userStructs.js.map new file mode 100644 index 00000000..662473fb --- /dev/null +++ b/dist/structs/userStructs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userStructs.js","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,wDAA+B;AAE/B,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,kBAAO,EAAC,CAAC,CAAC,CAAC,CAAC;AAErD,QAAA,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGU,QAAA,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC,CAAC;AAES,QAAA,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;IAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 98a37f71..ec129f90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,14 +16,64 @@ "express": "^5.1.0", "express-jwt": "^8.5.1", "is-email": "^1.0.2", - "is-uuid": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", - "prisma": "^6.18.0", "superstruct": "^2.0.2" }, "devDependencies": { - "nodemon": "^3.1.10" + "@types/bcrypt": "^6.0.0", + "@types/cors": "^2.8.19", + "@types/dotenv": "^6.1.1", + "@types/express": "^5.0.6", + "@types/is-email": "^1.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/multer": "^2.0.0", + "@types/node": "^24.10.1", + "nodemon": "^3.1.11", + "prisma": "^6.18.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@prisma/client": { @@ -52,6 +102,7 @@ "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", @@ -64,12 +115,14 @@ "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -83,12 +136,14 @@ "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.18.0", @@ -100,6 +155,7 @@ "version": "6.18.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.18.0" @@ -109,6 +165,125 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/dotenv": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", + "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/is-email": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/is-email/-/is-email-1.0.0.tgz", + "integrity": "sha512-b/76ooKpYY/b+oPrOuc/pmM5eag+ZlzctPsKcRCIKs+TFzh0FL58OeXtSPkbXt3uKNK84YCKHmjnoREtwve5Kg==", + "dev": true, "license": "MIT" }, "node_modules/@types/jsonwebtoken": { @@ -127,6 +302,16 @@ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -136,6 +321,41 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -149,6 +369,32 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -169,6 +415,13 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -283,6 +536,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.3", @@ -311,6 +565,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -326,6 +581,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -338,6 +594,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -405,6 +662,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -436,12 +694,14 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -499,6 +759,13 @@ "node": ">= 0.10" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -520,6 +787,7 @@ "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -529,6 +797,7 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, "license": "MIT" }, "node_modules/depd": { @@ -544,8 +813,19 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, "license": "MIT" }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dotenv": { "version": "17.2.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", @@ -591,6 +871,7 @@ "version": "3.18.4", "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "devOptional": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -601,6 +882,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=14" @@ -723,15 +1005,17 @@ "license": "MIT" }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "devOptional": true, "license": "MIT" }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, "funding": [ { "type": "individual", @@ -863,6 +1147,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -1052,16 +1337,11 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/is-uuid": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-uuid/-/is-uuid-1.0.2.tgz", - "integrity": "sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ==", - "license": "MIT" - }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -1152,6 +1432,13 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1326,6 +1613,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -1340,9 +1628,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", "dev": true, "license": "MIT", "dependencies": { @@ -1382,6 +1670,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "devOptional": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -1422,6 +1711,7 @@ "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, "license": "MIT" }, "node_modules/on-finished": { @@ -1468,12 +1758,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, "license": "MIT" }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, "license": "MIT" }, "node_modules/picomatch": { @@ -1493,6 +1785,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -1504,6 +1797,7 @@ "version": "6.18.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1549,6 +1843,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -1620,6 +1915,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -1884,10 +2180,14 @@ } }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "license": "MIT" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -1921,6 +2221,50 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -1941,6 +2285,20 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -1969,6 +2327,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1992,6 +2357,16 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } } } } diff --git a/package.json b/package.json index 70a2a6da..1852d756 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,11 @@ "description": "", "license": "ISC", "author": "", - "type": "module", - "main": "main.js", + "main": "./dist/main.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "nodemon ./src/main.js", - "start": "node ./src/main.js" + "build": "tsc", + "dev": "nodemon --watch src --exec ts-node./src/main.ts", + "start": "node ./dist/main.js" }, "dependencies": { "@prisma/client": "^6.18.0", @@ -19,13 +18,22 @@ "express": "^5.1.0", "express-jwt": "^8.5.1", "is-email": "^1.0.2", - "is-uuid": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", - "prisma": "^6.18.0", "superstruct": "^2.0.2" }, "devDependencies": { - "nodemon": "^3.1.10" + "@types/bcrypt": "^6.0.0", + "@types/cors": "^2.8.19", + "@types/dotenv": "^6.1.1", + "@types/express": "^5.0.6", + "@types/is-email": "^1.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/multer": "^2.0.0", + "@types/node": "^24.10.1", + "nodemon": "^3.1.11", + "prisma": "^6.18.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" } } diff --git a/sprint4-README.md b/sprint4-README.md new file mode 100644 index 00000000..14db9fbb --- /dev/null +++ b/sprint4-README.md @@ -0,0 +1,56 @@ +## 미션 목표 + +- [x] 토큰 기반 유저 인증/인가 구현하기 +- [x] (심화) Refresh Token 구현하기 +- [ ] (심화) Prisma로 관계형 활용하기 + +## 기본 요구사항 + +- [x] 스프린트 미션 3의 구현이 완료된 상태에서 진행을 권장합니다. +- [x] 가능하다면 Prisma 스키마를 수정할 때 Prisma 마이그레이션을 함께 진행해 보고, 잘되지 않는다면 마이그레이션 파일과 데이터베이스를 초기화하고 진행해도 좋습니다. + +### 인증 + +- [x] User 스키마를 작성해 주세요. +- [x] id, email, nickname, image, password, createdAt, updatedAt 필드를 가집니다. +- [x] 회원가입 API를 만들어 주세요. +- [x] email, nickname, password 를 입력하여 회원가입을 진행합니다. +- [x] password는 해싱해 저장합니다. +- [x] 토큰 기반 인증: 로그인에 성공하면 Access Token을 발급하는 기능을 구현합니다. + +###상품 기능 인가 + +- [x] 로그인한 유저만 상품을 등록할 수 있습니다. +- [x] 상품을 등록한 유저만 해당 상품의 정보를 수정하거나 삭제할 수 있습니다. + +###게시글 기능 인가 + +- [x] 로그인한 유저만 게시글을 등록할 수 있습니다. +- [x] 게시글을 등록한 유저만 해당 게시글을 수정하거나 삭제할 수 있습니다. + +###댓글 기능 인가 + +- [x] 로그인한 유저만 상품에 댓글을 등록할 수 있습니다. +- [x] 로그인한 유저만 게시글에 댓글을 등록할 수 있습니다. +- [x] 댓글을 등록한 유저만 해당 댓글을 수정하거나 삭제할 수 있습니다. + +###유저 정보 + +- [x] 유저가 자신의 정보를 조회하는 기능을 구현합니다. +- [x] 유저가 자신의 정보를 수정할 수 있는 기능을 구현합니다. +- [x] 유저가 자신의 비밀번호를 변경할 수 있는 기능을 구현합니다. +- [x] 유저가 자신이 등록한 상품의 목록을 조회하는 기능을 구현합니다. +- [x] 유저의 비밀번호는 리스폰스로 노출하지 않습니다. + +##심화 요구사항 + +###인증 + +- [x] 토큰 기반 인증: Refresh Token으로 토큰을 갱신하는 기능을 구현합니다. + +###좋아요 기능 + +- [x] 로그인한 유저는 상품에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [x] 로그인한 유저는 게시글에 '좋아요'와 '좋아요 취소'를 할 수 있습니다. +- [x] 상품 또는 게시글을 조회할 때, 유저가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked와 같은 불린형 필드를 리스폰스 객체에 포함시켜 리스폰스해 주세요. +- [x] 유저가 '좋아요'를 표시한 상품의 목록을 조회하는 기능을 구현합니다. diff --git a/src/Routers/articleRouter.js b/src/Routers/articleRouter.ts similarity index 84% rename from src/Routers/articleRouter.js rename to src/Routers/articleRouter.ts index 3de648b6..e294ced4 100644 --- a/src/Routers/articleRouter.js +++ b/src/Routers/articleRouter.ts @@ -1,6 +1,6 @@ -import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; -import auth from './../middlewares/auth.js'; +import { EXPRESS } from '../libs/constants.js'; +import { catchAsync } from '../libs/catchAsync.js'; +import auth from '../middlewares/auth.js'; import ArticleController from '../controller/articleController.js'; const articleRouter = EXPRESS.Router(); diff --git a/src/Routers/commentRouter.js b/src/Routers/commentRouter.ts similarity index 78% rename from src/Routers/commentRouter.js rename to src/Routers/commentRouter.ts index 0a4ab53f..e02479d3 100644 --- a/src/Routers/commentRouter.js +++ b/src/Routers/commentRouter.ts @@ -1,6 +1,6 @@ -import { EXPRESS } from './../libs/constants.js'; -import { catchAsync, catchAsyncAll } from './../libs/catchAsync.js'; -import auth from './../middlewares/auth.js'; +import { EXPRESS } from '../libs/constants.js'; +import { catchAsync, catchAsyncAll } from '../libs/catchAsync.js'; +import auth from '../middlewares/auth.js'; import { GetComment, PostComment, diff --git a/src/Routers/productRouter.js b/src/Routers/productRouter.ts similarity index 83% rename from src/Routers/productRouter.js rename to src/Routers/productRouter.ts index 2bd7a7ea..9b94e89e 100644 --- a/src/Routers/productRouter.js +++ b/src/Routers/productRouter.ts @@ -1,6 +1,6 @@ -import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; -import auth from './../middlewares/auth.js'; +import { EXPRESS } from '../libs/constants.js'; +import { catchAsync } from '../libs/catchAsync.js'; +import auth from '../middlewares/auth.js'; import ProductController from '../controller/productController.js'; const productRouter = EXPRESS.Router(); diff --git a/src/Routers/routerManager.js b/src/Routers/routerManager.ts similarity index 91% rename from src/Routers/routerManager.js rename to src/Routers/routerManager.ts index 3b1c053d..332d0cf2 100644 --- a/src/Routers/routerManager.js +++ b/src/Routers/routerManager.ts @@ -1,4 +1,4 @@ -import { EXPRESS } from './../libs/constants.js'; +import { EXPRESS } from '../libs/constants.js'; import productRouter from './productRouter.js'; import articleRouter from './articleRouter.js'; import commentRouter from './commentRouter.js'; diff --git a/src/Routers/uploadRouter.js b/src/Routers/uploadRouter.ts similarity index 77% rename from src/Routers/uploadRouter.js rename to src/Routers/uploadRouter.ts index 71d750d1..4a2ea19a 100644 --- a/src/Routers/uploadRouter.js +++ b/src/Routers/uploadRouter.ts @@ -1,5 +1,5 @@ -import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; +import { EXPRESS } from '../libs/constants.js'; +import { catchAsync } from '../libs/catchAsync.js'; import multer from 'multer'; import { UploadSingleImage diff --git a/src/Routers/userRouter.js b/src/Routers/userRouter.ts similarity index 88% rename from src/Routers/userRouter.js rename to src/Routers/userRouter.ts index 28c01417..1160c9bb 100644 --- a/src/Routers/userRouter.js +++ b/src/Routers/userRouter.ts @@ -1,5 +1,5 @@ -import { EXPRESS } from './../libs/constants.js'; -import { catchAsync } from './../libs/catchAsync.js'; +import { EXPRESS } from '../libs/constants.js'; +import { catchAsync } from '../libs/catchAsync.js'; import UserServiceController from '../controller/userController.js'; import auth from '../middlewares/auth.js'; diff --git a/src/controller/articleController.js b/src/controller/articleController.ts similarity index 100% rename from src/controller/articleController.js rename to src/controller/articleController.ts diff --git a/src/controller/commentController.js b/src/controller/commentController.ts similarity index 100% rename from src/controller/commentController.js rename to src/controller/commentController.ts diff --git a/src/controller/productController.js b/src/controller/productController.ts similarity index 100% rename from src/controller/productController.js rename to src/controller/productController.ts diff --git a/src/controller/uploadController.js b/src/controller/uploadController.ts similarity index 100% rename from src/controller/uploadController.js rename to src/controller/uploadController.ts diff --git a/src/controller/userController.js b/src/controller/userController.ts similarity index 100% rename from src/controller/userController.js rename to src/controller/userController.ts diff --git a/src/libs/Handler/errorHandler.js b/src/libs/Handler/errorHandler.ts similarity index 100% rename from src/libs/Handler/errorHandler.js rename to src/libs/Handler/errorHandler.ts diff --git a/src/libs/catchAsync.js b/src/libs/catchAsync.ts similarity index 100% rename from src/libs/catchAsync.js rename to src/libs/catchAsync.ts diff --git a/src/libs/constants.js b/src/libs/constants.ts similarity index 53% rename from src/libs/constants.js rename to src/libs/constants.ts index 17e5cf9b..d6796350 100644 --- a/src/libs/constants.js +++ b/src/libs/constants.ts @@ -1,8 +1,14 @@ import * as dotenv from 'dotenv'; import { PrismaClient } from '@prisma/client'; -import express from 'express'; +import express, { Request, Response, NextFunction } from 'express'; + dotenv.config(); export const EXPRESS = express; export const PORT = process.env.PORT || 3000; export const prismaClient = new PrismaClient; + + +export type ExpressRequest = Request; +export type ExpressResponse = Response; +export type ExpressNextFunction = NextFunction; \ No newline at end of file diff --git a/src/libs/corsSetUp.js b/src/libs/corsSetUp.js deleted file mode 100644 index ebc4611c..00000000 --- a/src/libs/corsSetUp.js +++ /dev/null @@ -1,28 +0,0 @@ -import 'dotenv/config'; -export const getCorsOrigin = () => { - const corsOrigin = process.env.CORS_ORIGIN; - if (!corsOrigin || corsOrigin === '*') return '*'; - - if (corsOrigin.includes(',')) - return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); - - // 단일 origin (trailing slash 제거) - return corsOrigin.trim().replace(/\/$/, ''); -}; - -export const corsOriginChecker = (origin, callback) => { - const allowedOrigins = getCorsOrigin(); - if (allowedOrigins === '*') return callback(null, true); - - const origins = Array.isArray(allowedOrigins) ? allowedOrigins : [allowedOrigins]; - if (!origins) return callback(null, false); - - const normalizedOrigin = origin.replace(/\/$/, ''); - const isAllowed = origins.some((allowed) => { - const normalizedAllowed = allowed.replace(/\/$/, ''); - return normalizedOrigin === normalizedAllowed; - - }); - callback(null, isAllowed); - -}; \ No newline at end of file diff --git a/src/libs/corsSetUp.ts b/src/libs/corsSetUp.ts new file mode 100644 index 00000000..0a6a6bc4 --- /dev/null +++ b/src/libs/corsSetUp.ts @@ -0,0 +1,11 @@ +import 'dotenv/config'; +export const getCorsOrigin = () => { + const corsOrigin = process.env.CORS_ORIGIN; + if (!corsOrigin || corsOrigin === '*') return '*'; + + if (corsOrigin.includes(',')) + return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); + + // 단일 origin (trailing slash 제거) + return corsOrigin.trim().replace(/\/$/, ''); +}; diff --git a/src/libs/interfaces.ts b/src/libs/interfaces.ts new file mode 100644 index 00000000..78333d03 --- /dev/null +++ b/src/libs/interfaces.ts @@ -0,0 +1,80 @@ +import { Prisma } from '@prisma/client'; + +export interface ProductType { + id: number; + name: string; + description?: string; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + comments?: CommentType[]; + productLikes?: ProductLikeType[]; +} +export interface ArticleType { + id: number; + title: string; + content: string; + createdAt: Date; + comments: CommentType[]; + articleLikes: ArticleLikeType[]; +} +export interface CommentType { + id: number; + content: string; + createdAt: Date; + updatedAt: Date; + productId?: number; + articleId?: number; + product?: ProductType; + article?: ArticleType; +} +export interface UserType { + id: number; + email: string; + password: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + refreshToken?: string | null; + productLikes?: ProductLikeType[]; + articleLikes?: ArticleLikeType[]; +} + + +export interface UpdateUserPasswordType { + + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} + +export interface ProductLikeType { + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + productId: number; + user: UserType; + product: ProductType; +} +export interface ArticleLikeType { + id: number; + userId: number; + articleId: number; + user: UserType; + article: ArticleType; + createdAt: Date; +} + +export type ProductFindOptions = Prisma.ProductFindManyArgs; +export type ArticleFindOptions = Prisma.ArticleFindManyArgs; + +export interface ProductWithLikes extends ProductType { + productLikes: ProductLikeType[]; +} + +export interface ProductWithIsLiked extends ProductType { + isLiked: boolean; +} \ No newline at end of file diff --git a/src/main.js b/src/main.ts similarity index 66% rename from src/main.js rename to src/main.ts index 507288e4..fa97a1b6 100644 --- a/src/main.js +++ b/src/main.ts @@ -1,8 +1,8 @@ -import { PORT, EXPRESS } from './libs/constants.js'; +import { PORT, EXPRESS } from './libs/constants'; import cors from 'cors'; -import { RouterManager } from './Routers/routerManager.js'; -import { getCorsOrigin, corsOriginChecker } from './libs/corsSetUp.js'; -import errorHandler from './libs/Handler/errorHandler.js'; +import { RouterManager } from './Routers/routerManager'; +import { getCorsOrigin } from './libs/corsSetUp'; +import errorHandler from './libs/Handler/errorHandler'; const app = EXPRESS(); diff --git a/src/middlewares/auth.js b/src/middlewares/auth.ts similarity index 86% rename from src/middlewares/auth.js rename to src/middlewares/auth.ts index 4562519a..f0294bdd 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.ts @@ -4,8 +4,13 @@ import articleRepository from '../repositories/articleRepository.js'; import { CustomError } from '../libs/Handler/errorHandler.js'; import jwt from 'jsonwebtoken'; +const JWT_SECRET = process.env.JWT_SECRET; +if (!JWT_SECRET) { + throw new Error('JWT_SECRET is not defined in environment variables.'); +} + const verifyAccessToken = expressjwt({ - secret: process.env.JWT_SECRET, + secret: JWT_SECRET, algorithms: ["HS256"], requestProperty: 'user' }); @@ -13,7 +18,7 @@ const verifyAccessToken = expressjwt({ const softVerifyAccessToken = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (token) { - jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + jwt.verify(token, JWT_SECRET, (err, user) => { if (!err) { req.user = user; } @@ -25,7 +30,7 @@ const softVerifyAccessToken = (req, res, next) => { }; const verifyRefreshToken = expressjwt({ - secret: process.env.JWT_SECRET, + secret: JWT_SECRET, algorithms: ['HS256'], getToken: (req) => req.cookies.refreshToken, }); diff --git a/src/repositories/articleLikeRepository.js b/src/repositories/articleLikeRepository.ts similarity index 69% rename from src/repositories/articleLikeRepository.js rename to src/repositories/articleLikeRepository.ts index 825729f8..4a8c146b 100644 --- a/src/repositories/articleLikeRepository.js +++ b/src/repositories/articleLikeRepository.ts @@ -1,6 +1,6 @@ -import { prismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants'; -async function find(userId, articleId) { +async function find(userId: number, articleId: number) { return prismaClient.articleLike.findUnique({ where: { userId_articleId: { @@ -11,7 +11,7 @@ async function find(userId, articleId) { }); } -async function create(userId, articleId) { +async function create(userId: number, articleId: number) { return prismaClient.articleLike.create({ data: { userId, @@ -20,7 +20,7 @@ async function create(userId, articleId) { }); } -async function remove(id) { +async function remove(id: number) { return prismaClient.articleLike.delete({ where: { id, diff --git a/src/repositories/articleRepository.js b/src/repositories/articleRepository.ts similarity index 65% rename from src/repositories/articleRepository.js rename to src/repositories/articleRepository.ts index bd0dcbc4..76e71fc1 100644 --- a/src/repositories/articleRepository.js +++ b/src/repositories/articleRepository.ts @@ -1,6 +1,7 @@ import { prismaClient } from '../libs/constants.js'; +import { ArticleType, ArticleFindOptions } from '../libs/interfaces.js'; -async function findById(id, userId) { +async function findById(id: number, userId: number) { const include = { articleLikes: userId ? { where: { userId } } : false, }; @@ -12,25 +13,28 @@ async function findById(id, userId) { }); } -async function findAll(findOptions, userId) { +async function findAll(findOptions: ArticleFindOptions, userId: number) { const include = { articleLikes: userId ? { where: { userId } } : false, }; + return prismaClient.article.findMany({ ...findOptions, include, }); } -async function create(userFields) { +async function create(userFields: ArticleType) { + const { createdAt, comments, articleLikes, ...NewuserFields } = userFields; + return await prismaClient.article.create({ data: { - ...userFields, + ...NewuserFields, }, }); } -async function update(id, data) { +async function update(id: number, data: ArticleFindOptions) { return prismaClient.article.update({ where: { id, @@ -41,7 +45,7 @@ async function update(id, data) { }); } -async function ondelete(id) { +async function ondelete(id: number) { return await prismaClient.article.delete({ where: { id, diff --git a/src/repositories/productLikeRepository.js b/src/repositories/productLikeRepository.ts similarity index 77% rename from src/repositories/productLikeRepository.js rename to src/repositories/productLikeRepository.ts index ee5e8278..7e5a8d84 100644 --- a/src/repositories/productLikeRepository.js +++ b/src/repositories/productLikeRepository.ts @@ -1,6 +1,6 @@ import { prismaClient } from '../libs/constants.js'; -async function find(userId, productId) { +async function find(userId: number, productId: number) { return prismaClient.productLike.findUnique({ where: { userId_productId: { @@ -11,7 +11,7 @@ async function find(userId, productId) { }); } -async function create(userId, productId) { +async function create(userId: number, productId: number) { return prismaClient.productLike.create({ data: { userId, @@ -20,7 +20,7 @@ async function create(userId, productId) { }); } -async function remove(id) { +async function remove(id: number) { return prismaClient.productLike.delete({ where: { id, @@ -28,7 +28,7 @@ async function remove(id) { }); } -async function findLikedProductsByUserId(userId) { +async function findLikedProductsByUserId(userId: number) { return prismaClient.productLike.findMany({ where: { userId, diff --git a/src/repositories/productRepository.js b/src/repositories/productRepository.ts similarity index 54% rename from src/repositories/productRepository.js rename to src/repositories/productRepository.ts index 6f41fb62..8fa1c8f2 100644 --- a/src/repositories/productRepository.js +++ b/src/repositories/productRepository.ts @@ -1,6 +1,8 @@ -import { prismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants'; +import { ProductType, ProductFindOptions } from './../libs/interfaces'; -async function findByUserId(userId) { + +async function findByUserId(userId: number) { return prismaClient.product.findMany({ where: { id: userId, @@ -8,7 +10,7 @@ async function findByUserId(userId) { }); } -async function findById(id, userId) { +async function findById(id: number, userId: number) { const include = { productLikes: userId ? { where: { userId } } : false, }; @@ -20,7 +22,7 @@ async function findById(id, userId) { }); } -async function findAll(findOptions, userId) { +async function findAll(findOptions: ProductFindOptions, userId: number) { const include = { productLikes: userId ? { where: { userId } } : false, }; @@ -30,26 +32,27 @@ async function findAll(findOptions, userId) { }); } -async function update(id, data) { +async function update(id: number, data: ProductType) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id: _, createdAt, updatedAt, comments, productLikes, ...updateData } = data; return prismaClient.product.update({ where: { id, }, - data: { - ...data, - }, + data: updateData, }); } -async function create(userFields) { +async function create(userFields: ProductType) { + const { createdAt, updatedAt, comments, productLikes, ...NewuserFields } = userFields; return await prismaClient.product.create({ data: { - ...userFields, + ...NewuserFields, }, }); } -async function ondelete(id) { +async function ondelete(id: number) { return await prismaClient.product.delete({ where: { id, diff --git a/src/repositories/userRepository.js b/src/repositories/userRepository.js deleted file mode 100644 index 0761eb28..00000000 --- a/src/repositories/userRepository.js +++ /dev/null @@ -1,52 +0,0 @@ -import { prismaClient } from '../libs/constants.js'; - -async function findById(id) { - return prismaClient.user.findUnique({ - where: { - id, - }, - }); -} - -async function findByEmail(email) { - return await prismaClient.user.findUnique({ - where: { - email, - }, - }); -} - -async function save(user) { - return prismaClient.user.create({ - data: { - email: user.email, - nickname: user.nickname, - password: user.password, - }, - }); -} - -async function update(id, data) { - return prismaClient.user.update({ - where: { - id, - }, - data: data, - }); -} - -async function createOrUpdate(provider, providerId, email, name) { - return prismaClient.user.upsert({ - where: { provider, providerId }, - update: { email, name }, - create: { provider, providerId, email, name }, - }); -} - -export default { - findById, - findByEmail, - save, - update, - createOrUpdate, -}; diff --git a/src/repositories/userRepository.ts b/src/repositories/userRepository.ts new file mode 100644 index 00000000..9234c917 --- /dev/null +++ b/src/repositories/userRepository.ts @@ -0,0 +1,56 @@ +import { prismaClient } from '../libs/constants'; +import { UserType } from "./../libs/interfaces"; + +async function findById(id: number) { + return prismaClient.user.findUnique({ + where: { + id, + }, + }); +} + +async function findByEmail(email: string) { + return await prismaClient.user.findUnique({ + where: { + email, + }, + }); +}; + +async function save(user: UserType) { + return prismaClient.user.create({ + data: { + email: user.email, + nickname: user.nickname, + password: user.password, + }, + }); +} + +async function update(id: number, data: Partial) { + const { createdAt, updatedAt, articleLikes, productLikes, ...Newdata } = data; + + return prismaClient.user.update({ + where: { + id, + }, + data: { + ...Newdata + }, + }); +} + +// async function createOrUpdate(provider: string, providerId: string, email: string, name: string) { +// return prismaClient.user.upsert({ +// where: { provider, providerId }, +// update: { email, name }, +// create: { provider, providerId, email, name }, +// }); +// } + +export default { + findById, + findByEmail, + save, + update +}; diff --git a/src/services/articleService.js b/src/services/articleService.ts similarity index 71% rename from src/services/articleService.js rename to src/services/articleService.ts index 6704a4b0..a3a41c2e 100644 --- a/src/services/articleService.js +++ b/src/services/articleService.ts @@ -1,26 +1,27 @@ -import articleLikeRepository from '../repositories/articleLikeRepository.js'; -import articleRepository from '../repositories/articleRepository.js'; +import articleLikeRepository from '../repositories/articleLikeRepository'; +import articleRepository from '../repositories/articleRepository'; +import { ArticleFindOptions } from './../libs/interfaces'; class ArticleService { - async likeArticle(userId, articleId) { + async likeArticle(userId: number, articleId: number) { const existingLike = await articleLikeRepository.find(userId, articleId); if (existingLike) { await articleLikeRepository.remove(existingLike.id); return { liked: false }; - } else { - await articleLikeRepository.create(userId, articleId); - return { liked: true }; } + + await articleLikeRepository.create(userId, articleId); + return { liked: true }; } - async getArticleById(articleId, userId) { + async getArticleById(articleId: number, userId: number) { const article = await articleRepository.findById(articleId, userId); const { articleLikes, ...rest } = article; return { ...rest, isLiked: articleLikes.length > 0 }; } - async getArticles(findOptions, userId) { + async getArticles(findOptions: ArticleFindOptions, userId: number) { const articles = await articleRepository.findAll(findOptions, userId); return articles.map((article) => { const { articleLikes, ...rest } = article; diff --git a/src/services/productService.js b/src/services/productService.ts similarity index 79% rename from src/services/productService.js rename to src/services/productService.ts index 282d14a2..0a116d9e 100644 --- a/src/services/productService.js +++ b/src/services/productService.ts @@ -1,8 +1,10 @@ -import productRepository from '../repositories/productRepository.js'; -import productLikeRepository from '../repositories/productLikeRepository.js'; +import productRepository from '../repositories/productRepository'; +import productLikeRepository from '../repositories/productLikeRepository'; +import { ProductFindOptions } from '../libs/interfaces'; + class ProductService { - async likeProduct(userId, productId) { + async likeProduct(userId: number, productId: number) { const existingLike = await productLikeRepository.find(userId, productId); if (existingLike) { @@ -14,13 +16,13 @@ class ProductService { } } - async getProductById(productId, userId) { + async getProductById(productId: number, userId: number) { const product = await productRepository.findById(productId, userId); const { productLikes, ...rest } = product; return { ...rest, isLiked: productLikes.length > 0 }; } - async getProducts(findOptions, userId) { + async getProducts(findOptions: ProductFindOptions, userId: number) { const products = await productRepository.findAll(findOptions, userId); return products.map((product) => { const { productLikes, ...rest } = product; diff --git a/src/services/userService.js b/src/services/userService.ts similarity index 68% rename from src/services/userService.js rename to src/services/userService.ts index c9f03a77..f7a1ca5d 100644 --- a/src/services/userService.js +++ b/src/services/userService.ts @@ -1,19 +1,21 @@ import { assert } from 'superstruct'; -import { CustomError } from '../libs/Handler/errorHandler.js'; -import userRepository from '../repositories/userRepository.js'; -import productRepository from '../repositories/productRepository.js'; -import productLikeRepository from '../repositories/productLikeRepository.js'; +import { CustomError } from '../libs/Handler/errorHandler'; +import userRepository from '../repositories/userRepository'; +import productRepository from '../repositories/productRepository'; +import productLikeRepository from '../repositories/productLikeRepository'; import bcrypt from 'bcrypt'; -import jwt from 'jsonwebtoken'; -import { PatchUser, ChangePassword } from '../structs/userStructs.js'; +import jwt, { JwtPayload, SignOptions } from 'jsonwebtoken'; +import { PatchUser, ChangePassword } from '../structs/userStructs'; +import { UserType, UpdateUserPasswordType } from "./../libs/interfaces"; -async function hashingPassword(password) { + +async function hashingPassword(password: string) { // 함수 추가 return bcrypt.hash(password, 12); } class UserService { - async createUser(user) { + async createUser(user: UserType) { const existedUser = await userRepository.findByEmail(user.email); if (existedUser) { throw new CustomError(422, 'User already exists', { @@ -28,12 +30,12 @@ class UserService { return this.filterSensitivceUserData(createdUser); } - filterSensitivceUserData(user) { + filterSensitivceUserData(user: UserType) { const { password, refreshToken, ...rest } = user; return rest; } - async getUser(email, password) { + async getUser(email: string, password: string) { const user = await userRepository.findByEmail(email); if (!user) throw new CustomError(401, 'Unauthorized'); @@ -42,7 +44,7 @@ class UserService { } - async getUserById(id) { + async getUserById(id: number) { const user = await userRepository.findById(id); if (!user) { throw new CustomError(404, 'User not found'); @@ -50,19 +52,25 @@ class UserService { return this.filterSensitivceUserData(user); } - async updateProfile(id, data) { + async updateProfile(id: number, data: UserType) { assert(data, PatchUser); const user = await userRepository.update(id, data); + if (!user) { + throw new CustomError(404, 'User not found'); + } return this.filterSensitivceUserData(user); } - async updatePassword(id, data) { + async updatePassword(id: number, data: UpdateUserPasswordType) { assert(data, ChangePassword); const { currentPassword, newPassword, confirmNewPassword } = data; if (newPassword !== confirmNewPassword) { throw new CustomError(400, "Passwords don't match"); } const user = await userRepository.findById(id); + if (!user) { + throw new CustomError(404, 'User not found'); + } await this.verifyPassword(currentPassword, user.password); const hashedPassword = await hashingPassword(newPassword); @@ -70,32 +78,35 @@ class UserService { return this.filterSensitivceUserData(updatedUser); } - async getProductsByUserId(id) { + async getProductsByUserId(id: number) { const products = await productRepository.findByUserId(id); return products; } - async getLikedProductsByUserId(id) { + async getLikedProductsByUserId(id: number) { const products = await productLikeRepository.findLikedProductsByUserId(id); return products; } - async updateUser(id, data) { + async updateUser(id: number, data: UserType) { return await userRepository.update(id, data); } - async verifyPassword(inputPassword, savedPassword) { + async verifyPassword(inputPassword: string, savedPassword: string) { const isValid = await bcrypt.compare(inputPassword, savedPassword); if (!isValid) throw new CustomError(401, 'Unauthorized'); } - createToken(user, type) { - const payload = { userId: user.id }; - const options = { + createToken(user: UserType, type?: string) { + const JWTsecretKey = process.env.JWT_SECRET; + if (!JWTsecretKey) throw new CustomError(500, 'JWT_SECRET is not defined'); + const payload: JwtPayload = { userId: user.id }; + const options: SignOptions = { expiresIn: type === 'refresh' ? '1d' : '10m', }; - return jwt.sign(payload, process.env.JWT_SECRET, options); + + return jwt.sign(payload, JWTsecretKey, options); } - async refreshToken(userId, refreshToken) { + async refreshToken(userId: number, refreshToken: string) { const user = await userRepository.findById(userId); if (!user || user.refreshToken !== refreshToken) { throw new CustomError(401, 'Unauthorized'); diff --git a/src/structs/structs.js b/src/structs/structs.ts similarity index 100% rename from src/structs/structs.js rename to src/structs/structs.ts diff --git a/src/structs/userStructs.js b/src/structs/userStructs.ts similarity index 100% rename from src/structs/userStructs.js rename to src/structs/userStructs.ts diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..a96a5e08 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,39 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + "rootDir": "./src", + "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "commonjs", + "target": "es2016", + "typeRoots": ["./node_modules/@types"], + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "noImplicitAny": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "esModuleInterop": true, + "skipLibCheck": true + } +} From 573dbc022235ef29ff4eb3e45f815c6c27cf5aee Mon Sep 17 00:00:00 2001 From: YooInHak Date: Fri, 5 Dec 2025 10:15:42 +0900 Subject: [PATCH 23/53] =?UTF-8?q?auth.ts=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/constants.ts | 4 +-- src/middlewares/auth.ts | 34 +++++++++++++---------- src/repositories/articleRepository.ts | 6 ++-- src/repositories/productLikeRepository.ts | 2 +- src/repositories/productRepository.ts | 5 ++-- src/repositories/userRepository.ts | 8 ------ tsconfig.json | 2 +- types/express.d.ts | 10 +++++++ 8 files changed, 39 insertions(+), 32 deletions(-) create mode 100644 types/express.d.ts diff --git a/src/libs/constants.ts b/src/libs/constants.ts index d6796350..da16ec04 100644 --- a/src/libs/constants.ts +++ b/src/libs/constants.ts @@ -1,6 +1,6 @@ import * as dotenv from 'dotenv'; import { PrismaClient } from '@prisma/client'; -import express, { Request, Response, NextFunction } from 'express'; +import express, { RequestHandler, Request, Response, NextFunction } from 'express'; dotenv.config(); @@ -8,7 +8,7 @@ export const EXPRESS = express; export const PORT = process.env.PORT || 3000; export const prismaClient = new PrismaClient; - +export type ExpressHandler = RequestHandler; export type ExpressRequest = Request; export type ExpressResponse = Response; export type ExpressNextFunction = NextFunction; \ No newline at end of file diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts index f0294bdd..dac8f23d 100644 --- a/src/middlewares/auth.ts +++ b/src/middlewares/auth.ts @@ -1,9 +1,11 @@ import { expressjwt } from 'express-jwt'; -import productRepository from '../repositories/productRepository.js'; -import articleRepository from '../repositories/articleRepository.js'; -import { CustomError } from '../libs/Handler/errorHandler.js'; +import productRepository from '../repositories/productRepository'; +import articleRepository from '../repositories/articleRepository'; +import { CustomError } from '../libs/Handler/errorHandler'; +import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; import jwt from 'jsonwebtoken'; + const JWT_SECRET = process.env.JWT_SECRET; if (!JWT_SECRET) { throw new Error('JWT_SECRET is not defined in environment variables.'); @@ -15,11 +17,11 @@ const verifyAccessToken = expressjwt({ requestProperty: 'user' }); -const softVerifyAccessToken = (req, res, next) => { +const softVerifyAccessToken: ExpressHandler = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (token) { jwt.verify(token, JWT_SECRET, (err, user) => { - if (!err) { + if (!err && user) { req.user = user; } next(); @@ -34,27 +36,31 @@ const verifyRefreshToken = expressjwt({ algorithms: ['HS256'], getToken: (req) => req.cookies.refreshToken, }); -async function verifyProduectAuth(req, res, next) { - const id = req.params.id; - - const product = await productRepository.findById(id); +async function verifyProduectAuth + (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { + const id = req.params.id || ""; + if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); + const idtoNumber = parseInt(id); + const product = await productRepository.findById(idtoNumber); if (!product) { throw new CustomError(404, 'product not found'); } - if (product.authorId !== req.user.id) { + if (typeof req.user !== 'object' || req.user.id === undefined || product.id !== req.user.id) { throw new CustomError(403, 'Forbidden'); } return next(); }; -async function verifyArticleAuth(req, res, next) { - const id = req.params.id; +async function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { - const article = await articleRepository.findById(id); + const id = req.params.id; + if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); + const idtoNumber = parseInt(id); + const article = await articleRepository.findById(idtoNumber); if (!article) { throw new CustomError(404, 'article not found'); } - if (article.authorId !== req.user.id) { + if (typeof req.user !== 'object' || req.user.id === undefined || article.id !== req.user.id) { throw new CustomError(403, 'Forbidden'); } return next(); diff --git a/src/repositories/articleRepository.ts b/src/repositories/articleRepository.ts index 76e71fc1..e0958bbc 100644 --- a/src/repositories/articleRepository.ts +++ b/src/repositories/articleRepository.ts @@ -1,7 +1,7 @@ -import { prismaClient } from '../libs/constants.js'; -import { ArticleType, ArticleFindOptions } from '../libs/interfaces.js'; +import { prismaClient } from '../libs/constants'; +import { ArticleType, ArticleFindOptions } from '../libs/interfaces'; -async function findById(id: number, userId: number) { +async function findById(id: number, userId?: number) { const include = { articleLikes: userId ? { where: { userId } } : false, }; diff --git a/src/repositories/productLikeRepository.ts b/src/repositories/productLikeRepository.ts index 7e5a8d84..96e0889d 100644 --- a/src/repositories/productLikeRepository.ts +++ b/src/repositories/productLikeRepository.ts @@ -1,4 +1,4 @@ -import { prismaClient } from '../libs/constants.js'; +import { prismaClient } from '../libs/constants'; async function find(userId: number, productId: number) { return prismaClient.productLike.findUnique({ diff --git a/src/repositories/productRepository.ts b/src/repositories/productRepository.ts index 8fa1c8f2..5f7d2834 100644 --- a/src/repositories/productRepository.ts +++ b/src/repositories/productRepository.ts @@ -10,7 +10,7 @@ async function findByUserId(userId: number) { }); } -async function findById(id: number, userId: number) { +async function findById(id: number, userId?: number) { const include = { productLikes: userId ? { where: { userId } } : false, }; @@ -33,7 +33,6 @@ async function findAll(findOptions: ProductFindOptions, userId: number) { } async function update(id: number, data: ProductType) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id: _, createdAt, updatedAt, comments, productLikes, ...updateData } = data; return prismaClient.product.update({ where: { @@ -66,5 +65,5 @@ export default { update, create, ondelete, - findByUserId, + findByUserId }; diff --git a/src/repositories/userRepository.ts b/src/repositories/userRepository.ts index 9234c917..df0142d7 100644 --- a/src/repositories/userRepository.ts +++ b/src/repositories/userRepository.ts @@ -40,14 +40,6 @@ async function update(id: number, data: Partial) { }); } -// async function createOrUpdate(provider: string, providerId: string, email: string, name: string) { -// return prismaClient.user.upsert({ -// where: { provider, providerId }, -// update: { email, name }, -// create: { provider, providerId, email, name }, -// }); -// } - export default { findById, findByEmail, diff --git a/tsconfig.json b/tsconfig.json index a96a5e08..d598afe0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ // See also https://aka.ms/tsconfig/module "module": "commonjs", "target": "es2016", - "typeRoots": ["./node_modules/@types"], + "typeRoots": ["./types", "./node_modules/@types"], // Other Outputs "sourceMap": true, diff --git a/types/express.d.ts b/types/express.d.ts new file mode 100644 index 00000000..7617f952 --- /dev/null +++ b/types/express.d.ts @@ -0,0 +1,10 @@ +import express from 'express'; +import { JwtPayload } from 'jsonwebtoken'; + +declare global { + namespace Express { + interface Request { + user?: (JwtPayload & { id?: number; }) | string; + } + } +} \ No newline at end of file From c0b72d073d959c8e644262f8e523fe7b54facccc Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 9 Dec 2025 10:22:18 +0900 Subject: [PATCH 24/53] =?UTF-8?q?[FEAT]=20=EC=8A=A4=ED=94=84=EB=A6=B0?= =?UTF-8?q?=ED=8A=B8=20=EB=AF=B8=EC=85=98=205=EA=B0=9C=EB=B0=9C=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 ++--- dist/Routers/articleRouter.d.ts | 3 - dist/Routers/articleRouter.d.ts.map | 1 - dist/Routers/articleRouter.js | 21 --- dist/Routers/articleRouter.js.map | 1 - dist/Routers/commentRouter.d.ts | 3 - dist/Routers/commentRouter.d.ts.map | 1 - dist/Routers/commentRouter.js | 19 --- dist/Routers/commentRouter.js.map | 1 - dist/Routers/productRouter.d.ts | 3 - dist/Routers/productRouter.d.ts.map | 1 - dist/Routers/productRouter.js | 21 --- dist/Routers/productRouter.js.map | 1 - dist/Routers/routerManager.d.ts | 2 - dist/Routers/routerManager.d.ts.map | 1 - dist/Routers/routerManager.js | 19 --- dist/Routers/routerManager.js.map | 1 - dist/Routers/uploadRouter.d.ts | 3 - dist/Routers/uploadRouter.d.ts.map | 1 - dist/Routers/uploadRouter.js | 15 -- dist/Routers/uploadRouter.js.map | 1 - dist/Routers/userRouter.d.ts | 3 - dist/Routers/userRouter.d.ts.map | 1 - dist/Routers/userRouter.js | 20 --- dist/Routers/userRouter.js.map | 1 - dist/controller/articleController.d.ts | 9 -- dist/controller/articleController.d.ts.map | 1 - dist/controller/articleController.js | 110 ------------- dist/controller/articleController.js.map | 1 - dist/controller/commentController.d.ts | 6 - dist/controller/commentController.d.ts.map | 1 - dist/controller/commentController.js | 122 -------------- dist/controller/commentController.js.map | 1 - dist/controller/productController.d.ts | 9 -- dist/controller/productController.d.ts.map | 1 - dist/controller/productController.js | 109 ------------- dist/controller/productController.js.map | 1 - dist/controller/uploadController.d.ts | 2 - dist/controller/uploadController.d.ts.map | 1 - dist/controller/uploadController.js | 9 -- dist/controller/uploadController.js.map | 1 - dist/controller/userController.d.ts | 11 -- dist/controller/userController.d.ts.map | 1 - dist/controller/userController.js | 87 ---------- dist/controller/userController.js.map | 1 - dist/libs/Handler/errorHandler.d.ts | 11 -- dist/libs/Handler/errorHandler.d.ts.map | 1 - dist/libs/Handler/errorHandler.js | 80 ---------- dist/libs/Handler/errorHandler.js.map | 1 - dist/libs/catchAsync.d.ts | 3 - dist/libs/catchAsync.d.ts.map | 1 - dist/libs/catchAsync.js | 10 -- dist/libs/catchAsync.js.map | 1 - dist/libs/constants.d.ts | 9 -- dist/libs/constants.d.ts.map | 1 - dist/libs/constants.js | 47 ------ dist/libs/constants.js.map | 1 - dist/libs/corsSetUp.d.ts | 3 - dist/libs/corsSetUp.d.ts.map | 1 - dist/libs/corsSetUp.js | 15 -- dist/libs/corsSetUp.js.map | 1 - dist/libs/interfaces.d.ts | 73 --------- dist/libs/interfaces.d.ts.map | 1 - dist/libs/interfaces.js | 3 - dist/libs/interfaces.js.map | 1 - dist/main.d.ts | 2 - dist/main.d.ts.map | 1 - dist/main.js | 26 --- dist/main.js.map | 1 - dist/middlewares/auth.d.ts | 17 -- dist/middlewares/auth.d.ts.map | 1 - dist/middlewares/auth.js | 79 --------- dist/middlewares/auth.js.map | 1 - dist/repositories/articleLikeRepository.d.ts | 25 --- .../articleLikeRepository.d.ts.map | 1 - dist/repositories/articleLikeRepository.js | 49 ------ .../repositories/articleLikeRepository.js.map | 1 - dist/repositories/articleRepository.d.ts | 59 ------- dist/repositories/articleRepository.d.ts.map | 1 - dist/repositories/articleRepository.js | 79 --------- dist/repositories/articleRepository.js.map | 1 - dist/repositories/productLikeRepository.d.ts | 42 ----- .../productLikeRepository.d.ts.map | 1 - dist/repositories/productLikeRepository.js | 62 ------- .../repositories/productLikeRepository.js.map | 1 - dist/repositories/productRepository.d.ts | 79 --------- dist/repositories/productRepository.d.ts.map | 1 - dist/repositories/productRepository.js | 91 ----------- dist/repositories/productRepository.js.map | 1 - dist/repositories/userRepository.d.ts | 49 ------ dist/repositories/userRepository.d.ts.map | 1 - dist/repositories/userRepository.js | 78 --------- dist/repositories/userRepository.js.map | 1 - dist/services/articleService.d.ts | 25 --- dist/services/articleService.d.ts.map | 1 - dist/services/articleService.js | 61 ------- dist/services/articleService.js.map | 1 - dist/services/productService.d.ts | 29 ---- dist/services/productService.d.ts.map | 1 - dist/services/productService.js | 61 ------- dist/services/productService.js.map | 1 - dist/services/userService.d.ts | 107 ------------- dist/services/userService.d.ts.map | 1 - dist/services/userService.js | 151 ------------------ dist/services/userService.js.map | 1 - dist/structs/structs.d.ts | 52 ------ dist/structs/structs.d.ts.map | 1 - dist/structs/structs.js | 65 -------- dist/structs/structs.js.map | 1 - dist/structs/userStructs.d.ts | 31 ---- dist/structs/userStructs.d.ts.map | 1 - dist/structs/userStructs.js | 59 ------- dist/structs/userStructs.js.map | 1 - package.json | 2 +- sprint-mission-2/ElectronicProduct.js | 2 +- src/Routers/articleRouter.ts | 8 +- src/Routers/commentRouter.ts | 8 +- src/Routers/productRouter.ts | 8 +- src/Routers/routerManager.ts | 12 +- src/Routers/uploadRouter.ts | 6 +- src/Routers/userRouter.ts | 8 +- src/controller/articleController.ts | 58 ++++--- src/controller/commentController.ts | 52 +++--- src/controller/productController.ts | 69 ++++---- src/controller/uploadController.ts | 7 +- src/controller/userController.ts | 47 +++--- src/libs/Handler/errorHandler.ts | 60 ++++--- src/libs/catchAsync.ts | 13 +- src/libs/constants.ts | 4 +- src/libs/corsSetUp.ts | 2 +- src/libs/interfaces.ts | 32 +++- src/libs/removeTool.ts | 10 ++ src/middlewares/auth.ts | 17 +- src/repositories/articleRepository.ts | 17 +- src/repositories/productRepository.ts | 12 +- src/services/userService.ts | 18 +-- tsconfig.json | 2 +- types/express.d.ts | 3 +- 138 files changed, 311 insertions(+), 2496 deletions(-) delete mode 100644 dist/Routers/articleRouter.d.ts delete mode 100644 dist/Routers/articleRouter.d.ts.map delete mode 100644 dist/Routers/articleRouter.js delete mode 100644 dist/Routers/articleRouter.js.map delete mode 100644 dist/Routers/commentRouter.d.ts delete mode 100644 dist/Routers/commentRouter.d.ts.map delete mode 100644 dist/Routers/commentRouter.js delete mode 100644 dist/Routers/commentRouter.js.map delete mode 100644 dist/Routers/productRouter.d.ts delete mode 100644 dist/Routers/productRouter.d.ts.map delete mode 100644 dist/Routers/productRouter.js delete mode 100644 dist/Routers/productRouter.js.map delete mode 100644 dist/Routers/routerManager.d.ts delete mode 100644 dist/Routers/routerManager.d.ts.map delete mode 100644 dist/Routers/routerManager.js delete mode 100644 dist/Routers/routerManager.js.map delete mode 100644 dist/Routers/uploadRouter.d.ts delete mode 100644 dist/Routers/uploadRouter.d.ts.map delete mode 100644 dist/Routers/uploadRouter.js delete mode 100644 dist/Routers/uploadRouter.js.map delete mode 100644 dist/Routers/userRouter.d.ts delete mode 100644 dist/Routers/userRouter.d.ts.map delete mode 100644 dist/Routers/userRouter.js delete mode 100644 dist/Routers/userRouter.js.map delete mode 100644 dist/controller/articleController.d.ts delete mode 100644 dist/controller/articleController.d.ts.map delete mode 100644 dist/controller/articleController.js delete mode 100644 dist/controller/articleController.js.map delete mode 100644 dist/controller/commentController.d.ts delete mode 100644 dist/controller/commentController.d.ts.map delete mode 100644 dist/controller/commentController.js delete mode 100644 dist/controller/commentController.js.map delete mode 100644 dist/controller/productController.d.ts delete mode 100644 dist/controller/productController.d.ts.map delete mode 100644 dist/controller/productController.js delete mode 100644 dist/controller/productController.js.map delete mode 100644 dist/controller/uploadController.d.ts delete mode 100644 dist/controller/uploadController.d.ts.map delete mode 100644 dist/controller/uploadController.js delete mode 100644 dist/controller/uploadController.js.map delete mode 100644 dist/controller/userController.d.ts delete mode 100644 dist/controller/userController.d.ts.map delete mode 100644 dist/controller/userController.js delete mode 100644 dist/controller/userController.js.map delete mode 100644 dist/libs/Handler/errorHandler.d.ts delete mode 100644 dist/libs/Handler/errorHandler.d.ts.map delete mode 100644 dist/libs/Handler/errorHandler.js delete mode 100644 dist/libs/Handler/errorHandler.js.map delete mode 100644 dist/libs/catchAsync.d.ts delete mode 100644 dist/libs/catchAsync.d.ts.map delete mode 100644 dist/libs/catchAsync.js delete mode 100644 dist/libs/catchAsync.js.map delete mode 100644 dist/libs/constants.d.ts delete mode 100644 dist/libs/constants.d.ts.map delete mode 100644 dist/libs/constants.js delete mode 100644 dist/libs/constants.js.map delete mode 100644 dist/libs/corsSetUp.d.ts delete mode 100644 dist/libs/corsSetUp.d.ts.map delete mode 100644 dist/libs/corsSetUp.js delete mode 100644 dist/libs/corsSetUp.js.map delete mode 100644 dist/libs/interfaces.d.ts delete mode 100644 dist/libs/interfaces.d.ts.map delete mode 100644 dist/libs/interfaces.js delete mode 100644 dist/libs/interfaces.js.map delete mode 100644 dist/main.d.ts delete mode 100644 dist/main.d.ts.map delete mode 100644 dist/main.js delete mode 100644 dist/main.js.map delete mode 100644 dist/middlewares/auth.d.ts delete mode 100644 dist/middlewares/auth.d.ts.map delete mode 100644 dist/middlewares/auth.js delete mode 100644 dist/middlewares/auth.js.map delete mode 100644 dist/repositories/articleLikeRepository.d.ts delete mode 100644 dist/repositories/articleLikeRepository.d.ts.map delete mode 100644 dist/repositories/articleLikeRepository.js delete mode 100644 dist/repositories/articleLikeRepository.js.map delete mode 100644 dist/repositories/articleRepository.d.ts delete mode 100644 dist/repositories/articleRepository.d.ts.map delete mode 100644 dist/repositories/articleRepository.js delete mode 100644 dist/repositories/articleRepository.js.map delete mode 100644 dist/repositories/productLikeRepository.d.ts delete mode 100644 dist/repositories/productLikeRepository.d.ts.map delete mode 100644 dist/repositories/productLikeRepository.js delete mode 100644 dist/repositories/productLikeRepository.js.map delete mode 100644 dist/repositories/productRepository.d.ts delete mode 100644 dist/repositories/productRepository.d.ts.map delete mode 100644 dist/repositories/productRepository.js delete mode 100644 dist/repositories/productRepository.js.map delete mode 100644 dist/repositories/userRepository.d.ts delete mode 100644 dist/repositories/userRepository.d.ts.map delete mode 100644 dist/repositories/userRepository.js delete mode 100644 dist/repositories/userRepository.js.map delete mode 100644 dist/services/articleService.d.ts delete mode 100644 dist/services/articleService.d.ts.map delete mode 100644 dist/services/articleService.js delete mode 100644 dist/services/articleService.js.map delete mode 100644 dist/services/productService.d.ts delete mode 100644 dist/services/productService.d.ts.map delete mode 100644 dist/services/productService.js delete mode 100644 dist/services/productService.js.map delete mode 100644 dist/services/userService.d.ts delete mode 100644 dist/services/userService.d.ts.map delete mode 100644 dist/services/userService.js delete mode 100644 dist/services/userService.js.map delete mode 100644 dist/structs/structs.d.ts delete mode 100644 dist/structs/structs.d.ts.map delete mode 100644 dist/structs/structs.js delete mode 100644 dist/structs/structs.js.map delete mode 100644 dist/structs/userStructs.d.ts delete mode 100644 dist/structs/userStructs.d.ts.map delete mode 100644 dist/structs/userStructs.js delete mode 100644 dist/structs/userStructs.js.map create mode 100644 src/libs/removeTool.ts diff --git a/README.md b/README.md index 363882a0..42012d3b 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,35 @@ ## 미션 목표 -- [ ] 타입스크립트 마이그레이션하기 -- [ ] 타입스크립트 개발 환경 세팅하기 -- [ ] (심화) Layered Architecture 적용하기 +- [x] 타입스크립트 마이그레이션하기 +- [x] 타입스크립트 개발 환경 세팅하기 +- [x] (심화) Layered Architecture 적용하기 ## 기본 요구 사항 -- [ ] 스프린트 미션 4의 구현이 완료된 상태에서 진행을 권장합니다. -- [ ] 타입스크립트 마이그레이션을 먼저 진행해 보고, 이전 미션에서 구현하지 못한 부분이 있다면 추가로 구현해 보세요. +- [x] 스프린트 미션 4의 구현이 완료된 상태에서 진행을 권장합니다. +- [x] 타입스크립트 마이그레이션을 먼저 진행해 보고, 이전 미션에서 구현하지 못한 부분이 있다면 추가로 구현해 보세요. ## 프로젝트 세팅 -- [] tsconfig.json 파일을 생성하고, 필요한 옵션을 설정해 주세요. (예: outDir). -- [] 필요한 npm script를 설정해 주세요. (예: 빌드 및 개발 서버 실행 명령어) +- [x] tsconfig.json 파일을 생성하고, 필요한 옵션을 설정해 주세요. (예: outDir). +- [x] 필요한 npm script를 설정해 주세요. (예: 빌드 및 개발 서버 실행 명령어) ## 타입스크립트 마이그레이션 -- [ ] 기존 Express.js 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요. -- [ ] 필요한 타입 패키지를 설치해 주세요. -- [ ] any 타입의 사용은 최소화해주세요. -- [ ] 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요. -- [ ] 필요한 경우, 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요. -- [ ] 필요한 경우, declare를 사용하여 타입을 오버라이드하거나 확장합니다. (예: req.user) +- [x] 기존 Express.js 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요. +- [x] 필요한 타입 패키지를 설치해 주세요. +- [x] any 타입의 사용은 최소화해주세요. +- [x] 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요. +- [x] 필요한 경우, 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요. +- [x] 필요한 경우, declare를 사용하여 타입을 오버라이드하거나 확장합니다. (예: req.user) ## 개발 환경 설정 -- [ ] ts-node 를 사용해 .ts 코드를 바로 실행할 수 있는 npm script를 만들어 주세요. (예: npm run dev) -- [ ] nodemon을 사용해 .ts 코드가 변경될 때마다 서버가 다시 실행되는 npm script를 만들어 주세요. (예: npm run dev) +- [x] ts-node 를 사용해 .ts 코드를 바로 실행할 수 있는 npm script를 만들어 주세요. (예: npm run dev) +- [x] nodemon을 사용해 .ts 코드가 변경될 때마다 서버가 다시 실행되는 npm script를 만들어 주세요. (예: npm run dev) ## 심화 요구 사항 -- [ ] Layered Architecture 적용하기 -- [ ] Controller, Service, Repository로 나누어 코드를 리팩토링해 주세요. -- [ ] 필요하다면, 계층 사이에서 데이터를 주고 받을 때 DTO를 활용해 주세요. +- [x] Layered Architecture 적용하기 +- [x] Controller, Service, Repository로 나누어 코드를 리팩토링해 주세요. +- [x] 필요하다면, 계층 사이에서 데이터를 주고 받을 때 DTO를 활용해 주세요. diff --git a/dist/Routers/articleRouter.d.ts b/dist/Routers/articleRouter.d.ts deleted file mode 100644 index 8878a904..00000000 --- a/dist/Routers/articleRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const articleRouter: import("express-serve-static-core").Router; -export default articleRouter; -//# sourceMappingURL=articleRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.d.ts.map b/dist/Routers/articleRouter.d.ts.map deleted file mode 100644 index 8986ff63..00000000 --- a/dist/Routers/articleRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/articleRouter.js b/dist/Routers/articleRouter.js deleted file mode 100644 index 199ef703..00000000 --- a/dist/Routers/articleRouter.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -const catchAsync_js_1 = require("../libs/catchAsync.js"); -const auth_js_1 = __importDefault(require("../middlewares/auth.js")); -const articleController_js_1 = __importDefault(require("../controller/articleController.js")); -const articleRouter = constants_js_1.EXPRESS.Router(); -const articleController = new articleController_js_1.default(); -articleRouter.route('/') - .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.getArticles)) - .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.postArticle)); -articleRouter.route('/:id') - .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.getArticleById)) - .patch(auth_js_1.default.verifyAccessToken, auth_js_1.default.verifyArticleAuth, (0, catchAsync_js_1.catchAsync)(articleController.patchArticleById)) - .delete(auth_js_1.default.verifyAccessToken, auth_js_1.default.verifyArticleAuth, (0, catchAsync_js_1.catchAsync)(articleController.deleteArticleById)); -articleRouter.post('/:id/like', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(articleController.likeArticle)); -exports.default = articleRouter; -//# sourceMappingURL=articleRouter.js.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.js.map b/dist/Routers/articleRouter.js.map deleted file mode 100644 index 01a62ee5..00000000 --- a/dist/Routers/articleRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRouter.js","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,qEAA0C;AAC1C,8FAAmE;AAEnE,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,8BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;KAC1E,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KACrG,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAE7G,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts b/dist/Routers/commentRouter.d.ts deleted file mode 100644 index 06b1655f..00000000 --- a/dist/Routers/commentRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const commentRouter: import("express-serve-static-core").Router; -export default commentRouter; -//# sourceMappingURL=commentRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts.map b/dist/Routers/commentRouter.d.ts.map deleted file mode 100644 index 415ffed9..00000000 --- a/dist/Routers/commentRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAYvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.js b/dist/Routers/commentRouter.js deleted file mode 100644 index 4ded349f..00000000 --- a/dist/Routers/commentRouter.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -const catchAsync_js_1 = require("../libs/catchAsync.js"); -const auth_js_1 = __importDefault(require("../middlewares/auth.js")); -const commentController_js_1 = require("../controller/commentController.js"); -const commentRouter = constants_js_1.EXPRESS.Router(); -commentRouter.route('/') - .get((0, catchAsync_js_1.catchAsync)(commentController_js_1.GetComment)) - .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(commentController_js_1.PostComment)); -commentRouter.route('/:id') - .get((0, catchAsync_js_1.catchAsync)(commentController_js_1.GetCommentById)) - .patch(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsyncAll)(auth_js_1.default.verifyProduectAuth, commentController_js_1.PatchCommentById)) - .delete(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsyncAll)(auth_js_1.default.verifyProduectAuth, commentController_js_1.DeleteCommentById)); -exports.default = commentRouter; -//# sourceMappingURL=commentRouter.js.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.js.map b/dist/Routers/commentRouter.js.map deleted file mode 100644 index eb74a379..00000000 --- a/dist/Routers/commentRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRouter.js","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAkE;AAClE,qEAA0C;AAC1C,6EAM4C;AAE5C,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAEvC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,IAAA,0BAAU,EAAC,iCAAU,CAAC,CAAC;KAC3B,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,kCAAW,CAAC,CAAC,CAAC;AAE3D,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,IAAA,0BAAU,EAAC,qCAAc,CAAC,CAAC;KAC/B,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,6BAAa,EAAC,iBAAI,CAAC,kBAAkB,EAAE,uCAAgB,CAAC,CAAC;KACvF,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,6BAAa,EAAC,iBAAI,CAAC,kBAAkB,EAAE,wCAAiB,CAAC,CAAC,CAAC;AAG/F,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts b/dist/Routers/productRouter.d.ts deleted file mode 100644 index c3eadb5d..00000000 --- a/dist/Routers/productRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const productRouter: import("express-serve-static-core").Router; -export default productRouter; -//# sourceMappingURL=productRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts.map b/dist/Routers/productRouter.d.ts.map deleted file mode 100644 index 0c9ad952..00000000 --- a/dist/Routers/productRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.js b/dist/Routers/productRouter.js deleted file mode 100644 index 10a43338..00000000 --- a/dist/Routers/productRouter.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -const catchAsync_js_1 = require("../libs/catchAsync.js"); -const auth_js_1 = __importDefault(require("../middlewares/auth.js")); -const productController_js_1 = __importDefault(require("../controller/productController.js")); -const productRouter = constants_js_1.EXPRESS.Router(); -const productController = new productController_js_1.default(); -productRouter.route('/') - .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.GetProduct)) - .post(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.PostProduct)); -productRouter.route('/:id') - .get(auth_js_1.default.softVerifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.GetProductById)) - .patch(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.PatchProductById)) - .delete(auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.DeleteProductById)); -productRouter.post('/:id/like', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(productController.likeProduct)); -exports.default = productRouter; -//# sourceMappingURL=productRouter.js.map \ No newline at end of file diff --git a/dist/Routers/productRouter.js.map b/dist/Routers/productRouter.js.map deleted file mode 100644 index e73f56fa..00000000 --- a/dist/Routers/productRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRouter.js","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,qEAA0C;AAC1C,8FAAmE;AAEnE,MAAM,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,8BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;KACzE,IAAI,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,iBAAI,CAAC,qBAAqB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KAC7E,MAAM,CAAC,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErF,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts b/dist/Routers/routerManager.d.ts deleted file mode 100644 index c5d39a67..00000000 --- a/dist/Routers/routerManager.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare const RouterManager: import("express-serve-static-core").Router; -//# sourceMappingURL=routerManager.d.ts.map \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts.map b/dist/Routers/routerManager.d.ts.map deleted file mode 100644 index 92a9a935..00000000 --- a/dist/Routers/routerManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routerManager.d.ts","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,4CAAmB,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.js b/dist/Routers/routerManager.js deleted file mode 100644 index 43373e55..00000000 --- a/dist/Routers/routerManager.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RouterManager = void 0; -const constants_js_1 = require("../libs/constants.js"); -const productRouter_js_1 = __importDefault(require("./productRouter.js")); -const articleRouter_js_1 = __importDefault(require("./articleRouter.js")); -const commentRouter_js_1 = __importDefault(require("./commentRouter.js")); -const uploadRouter_js_1 = __importDefault(require("./uploadRouter.js")); -const userRouter_js_1 = __importDefault(require("./userRouter.js")); -exports.RouterManager = constants_js_1.EXPRESS.Router(); -exports.RouterManager.use('/products', productRouter_js_1.default); -exports.RouterManager.use('/articles', articleRouter_js_1.default); -exports.RouterManager.use('/comments', commentRouter_js_1.default); -exports.RouterManager.use('/files', uploadRouter_js_1.default); -exports.RouterManager.use('/user', userRouter_js_1.default); -//# sourceMappingURL=routerManager.js.map \ No newline at end of file diff --git a/dist/Routers/routerManager.js.map b/dist/Routers/routerManager.js.map deleted file mode 100644 index 6ccaa03b..00000000 --- a/dist/Routers/routerManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,uDAA+C;AAC/C,0EAA+C;AAC/C,0EAA+C;AAC/C,0EAA+C;AAC/C,wEAA6C;AAC7C,oEAAyC;AAG5B,QAAA,aAAa,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,0BAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,yBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,uBAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts b/dist/Routers/uploadRouter.d.ts deleted file mode 100644 index a036c514..00000000 --- a/dist/Routers/uploadRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const uploadRouter: import("express-serve-static-core").Router; -export default uploadRouter; -//# sourceMappingURL=uploadRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts.map b/dist/Routers/uploadRouter.d.ts.map deleted file mode 100644 index 732e2bf6..00000000 --- a/dist/Routers/uploadRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,YAAY,4CAAmB,CAAC;AAStC,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js b/dist/Routers/uploadRouter.js deleted file mode 100644 index b5877488..00000000 --- a/dist/Routers/uploadRouter.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -const catchAsync_js_1 = require("../libs/catchAsync.js"); -const multer_1 = __importDefault(require("multer")); -const uploadController_js_1 = require("../controller/uploadController.js"); -const uploadRouter = constants_js_1.EXPRESS.Router(); -const upload = (0, multer_1.default)({ dest: 'upload/' }); -uploadRouter.post('/', upload.single('attachment'), (0, catchAsync_js_1.catchAsync)(uploadController_js_1.UploadSingleImage)); -uploadRouter.use('/', constants_js_1.EXPRESS.static('upload')); -exports.default = uploadRouter; -//# sourceMappingURL=uploadRouter.js.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js.map b/dist/Routers/uploadRouter.js.map deleted file mode 100644 index b8ed5cc5..00000000 --- a/dist/Routers/uploadRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadRouter.js","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,oDAA4B;AAC5B,2EAE2C;AAE3C,MAAM,YAAY,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AAEtC,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;AAE3C,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAC9C,IAAA,0BAAU,EAAC,uCAAiB,CAAC,CAAC,CAAC;AAEnC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,sBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEhD,kBAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts b/dist/Routers/userRouter.d.ts deleted file mode 100644 index 23df20cc..00000000 --- a/dist/Routers/userRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const userRouter: import("express-serve-static-core").Router; -export default userRouter; -//# sourceMappingURL=userRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts.map b/dist/Routers/userRouter.d.ts.map deleted file mode 100644 index 6564ec89..00000000 --- a/dist/Routers/userRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,UAAU,4CAAmB,CAAC;AAgBpC,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.js b/dist/Routers/userRouter.js deleted file mode 100644 index e81cb3ca..00000000 --- a/dist/Routers/userRouter.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -const catchAsync_js_1 = require("../libs/catchAsync.js"); -const userController_js_1 = __importDefault(require("../controller/userController.js")); -const auth_js_1 = __importDefault(require("../middlewares/auth.js")); -const userRouter = constants_js_1.EXPRESS.Router(); -const userController = new userController_js_1.default(); -userRouter.post('/', (0, catchAsync_js_1.catchAsync)(userController.register)); -userRouter.post('/login', (0, catchAsync_js_1.catchAsync)(userController.login)); -userRouter.post('/token/refresh', auth_js_1.default.verifyRefreshToken, (0, catchAsync_js_1.catchAsync)(userController.refresh)); -userRouter.get('/me', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.GetMe)); -userRouter.patch('/me', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.updateMe)); -userRouter.patch('/me/password', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.updateMyPassword)); -userRouter.get('/me/products', auth_js_1.default.verifyAccessToken, (0, catchAsync_js_1.catchAsync)(userController.getMyProducts)); -exports.default = userRouter; -//# sourceMappingURL=userRouter.js.map \ No newline at end of file diff --git a/dist/Routers/userRouter.js.map b/dist/Routers/userRouter.js.map deleted file mode 100644 index 66dfdb18..00000000 --- a/dist/Routers/userRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,uDAA+C;AAC/C,yDAAmD;AACnD,wFAAoE;AACpE,qEAA0C;AAE1C,MAAM,UAAU,GAAG,sBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,2BAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1D,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,iBAAI,CAAC,kBAAkB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,iBAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/controller/articleController.d.ts b/dist/controller/articleController.d.ts deleted file mode 100644 index 0c4c8648..00000000 --- a/dist/controller/articleController.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default class ArticleController { - getArticles(req: any, res: any): Promise; - getArticleById(req: any, res: any): Promise; - postArticle(req: any, res: any): Promise; - patchArticleById(req: any, res: any): Promise; - deleteArticleById(req: any, res: any): Promise; - likeArticle(req: any, res: any): Promise; -} -//# sourceMappingURL=articleController.d.ts.map \ No newline at end of file diff --git a/dist/controller/articleController.d.ts.map b/dist/controller/articleController.d.ts.map deleted file mode 100644 index 262ac3b0..00000000 --- a/dist/controller/articleController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAC5B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAuCpB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOvB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOpB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAQzB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAM1B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAM7B"} \ No newline at end of file diff --git a/dist/controller/articleController.js b/dist/controller/articleController.js deleted file mode 100644 index 784588da..00000000 --- a/dist/controller/articleController.js +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const articleService_js_1 = require("../services/articleService.js"); -const superstruct_1 = require("superstruct"); -const structs_js_1 = require("../structs/structs.js"); -const articleRepository_js_1 = __importDefault(require("../repositories/articleRepository.js")); -class ArticleController { - getArticles(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - const findOptions = { - where: { - title: { - contains: title, - }, - content: { - contains: content, - }, - }, - orderBy, - skip: parsedOffset, - }; - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } - const userId = req.user ? req.user.userId : null; - const articles = yield articleService_js_1.articleService.getArticles(findOptions, userId); - res.send(articles); - }); - } - getArticleById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const userId = req.user ? req.user.userId : null; - const article = yield articleService_js_1.articleService.getArticleById(id, userId); - res.send(article); - }); - } - postArticle(req, res) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(req.body, structs_js_1.CreateArticle); - const userFields = __rest(req.body, []); - const article = yield articleRepository_js_1.default.create(userFields); - res.status(201).send(article); - }); - } - patchArticleById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - (0, superstruct_1.assert)(req.body, structs_js_1.PatchArticle); - const userFields = __rest(req.body, []); - const article = yield articleRepository_js_1.default.update(id, userFields); - res.send(article); - }); - } - deleteArticleById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const article = yield articleRepository_js_1.default.ondelete(id); - res.send(article); - }); - } - likeArticle(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const articleId = req.params.id; - const result = yield articleService_js_1.articleService.likeArticle(userId, articleId); - return res.status(200).json(result); - }); - } -} -exports.default = ArticleController; -//# sourceMappingURL=articleController.js.map \ No newline at end of file diff --git a/dist/controller/articleController.js.map b/dist/controller/articleController.js.map deleted file mode 100644 index 0e76b016..00000000 --- a/dist/controller/articleController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/commentController.d.ts b/dist/controller/commentController.d.ts deleted file mode 100644 index 280c7142..00000000 --- a/dist/controller/commentController.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export declare function GetComment(req: any, res: any): Promise; -export declare function GetCommentById(req: any, res: any): Promise; -export declare function PostComment(req: any, res: any): Promise; -export declare function PatchCommentById(req: any, res: any): Promise; -export declare function DeleteCommentById(req: any, res: any): Promise; -//# sourceMappingURL=commentController.d.ts.map \ No newline at end of file diff --git a/dist/controller/commentController.d.ts.map b/dist/controller/commentController.d.ts.map deleted file mode 100644 index 059331ca..00000000 --- a/dist/controller/commentController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAMA,wBAAsB,UAAU,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBA0CxC;AAGD,wBAAsB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,iBAQ5C;AAGD,wBAAsB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBAoBzC;AAED,wBAAsB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,gBAkB9C;AAED,wBAAsB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,iBAQ/C"} \ No newline at end of file diff --git a/dist/controller/commentController.js b/dist/controller/commentController.js deleted file mode 100644 index f0f5dba6..00000000 --- a/dist/controller/commentController.js +++ /dev/null @@ -1,122 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetComment = GetComment; -exports.GetCommentById = GetCommentById; -exports.PostComment = PostComment; -exports.PatchCommentById = PatchCommentById; -exports.DeleteCommentById = DeleteCommentById; -const constants_js_1 = require("../libs/constants.js"); -const superstruct_1 = require("superstruct"); -const structs_js_1 = require("../structs/structs.js"); -function GetComment(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { productId, articleId } = req.query; - const { take = '10', cursor } = req.query; - const parsedTake = parseInt(take, 10); - if (isNaN(parsedTake) || parsedTake <= 0) { - return res.status(400).send({ error: 'Invalid "take" parameter.' }); - } - const whereClause = {}; - if (productId) { - whereClause.productId = productId; // 상품 ID로 필터링 - } - else if (articleId) { - whereClause.articleId = articleId; // 게시글 ID로 필터링 - } - const findOptions = { - take: parsedTake, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, - }; - if (cursor) { - findOptions.skip = 1; - findOptions.cursor = { - id: cursor, - }; - } - const comments = yield constants_js_1.prismaClient.comment.findMany(findOptions); // - let nextCursor = null; - if (comments.length === parsedTake) { - nextCursor = comments[comments.length - 1].id; - } - res.status(200).json({ - comments, - nextCursor, - }); - }); -} -function GetCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const Comment = yield constants_js_1.prismaClient.comment.findUniqueOrThrow({ - where: { - id - }, - }); - res.send(Comment); - }); -} -function PostComment(req, res) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(req.body, structs_js_1.CreateComment); - const { content, productId, articleId } = req.body; - if ((productId && articleId) || (!productId && !articleId)) { - return res.status(400).json({ - error: 'Comment must belong to EITHER a Product OR an Article.', - }); - } - if (!content || content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - const comment = yield constants_js_1.prismaClient.comment.create({ - data: { - content: content, - productId: productId, - articleId: articleId, - }, - }); - res.status(201).send(comment); - }); -} -function PatchCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - (0, superstruct_1.assert)(req.body, structs_js_1.PatchComment); - const { content } = req.body; - if (content !== undefined && content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - const comment = yield constants_js_1.prismaClient.comment.update({ - where: { - id - }, - data: { - content: content, - }, - }); - res.send(comment); - }); -} -function DeleteCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const Comment = yield constants_js_1.prismaClient.comment.delete({ - where: { - id - }, - }); - res.send(Comment); - }); -} -//# sourceMappingURL=commentController.js.map \ No newline at end of file diff --git a/dist/controller/commentController.js.map b/dist/controller/commentController.js.map deleted file mode 100644 index d67221bc..00000000 --- a/dist/controller/commentController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/productController.d.ts b/dist/controller/productController.d.ts deleted file mode 100644 index 49d097ef..00000000 --- a/dist/controller/productController.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default class ProductController { - GetProduct(req: any, res: any): Promise; - GetProductById(req: any, res: any): Promise; - PostProduct(req: any, res: any): Promise; - PatchProductById(req: any, res: any): Promise; - DeleteProductById(req: any, res: any): Promise; - likeProduct(req: any, res: any): Promise; -} -//# sourceMappingURL=productController.d.ts.map \ No newline at end of file diff --git a/dist/controller/productController.d.ts.map b/dist/controller/productController.d.ts.map deleted file mode 100644 index fac19115..00000000 --- a/dist/controller/productController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAC5B,UAAU,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAsCnB,cAAc,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOvB,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAOpB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAQzB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAM1B,WAAW,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAM7B"} \ No newline at end of file diff --git a/dist/controller/productController.js b/dist/controller/productController.js deleted file mode 100644 index 47a58c0b..00000000 --- a/dist/controller/productController.js +++ /dev/null @@ -1,109 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const productService_js_1 = require("../services/productService.js"); -const superstruct_1 = require("superstruct"); -const structs_js_1 = require("../structs/structs.js"); -const productRepository_js_1 = __importDefault(require("../repositories/productRepository.js")); -class ProductController { - GetProduct(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); - const findOptions = { - where: { - name: { - contains: name, - }, - description: { - contains: description, - }, - }, - orderBy, - skip: parsedOffset, - }; - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } - const userId = req.user ? req.user.userId : null; - const products = yield productService_js_1.productService.getProducts(findOptions, userId); - res.send(products); - }); - } - GetProductById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - const userId = req.user ? req.user.userId : null; - const product = yield productService_js_1.productService.getProductById(id, userId); - res.send(product); - }); - } - PostProduct(req, res) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(req.body, structs_js_1.CreateProduct); - const userFields = __rest(req.body, []); - const product = yield productRepository_js_1.default.create(userFields); - res.status(201).send(product); - }); - } - PatchProductById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - (0, superstruct_1.assert)(req.body, structs_js_1.PatchProduct); - const userFields = __rest(req.body, []); - const Product = yield productRepository_js_1.default.update(id, userFields); - res.send(Product); - }); - } - DeleteProductById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - const Product = yield productRepository_js_1.default.ondelete(id); - res.send(Product); - }); - } - likeProduct(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const productId = req.params.id; - const result = yield productService_js_1.productService.likeProduct(userId, productId); - return res.status(200).json(result); - }); - } -} -exports.default = ProductController; -//# sourceMappingURL=productController.js.map \ No newline at end of file diff --git a/dist/controller/productController.js.map b/dist/controller/productController.js.map deleted file mode 100644 index eb085861..00000000 --- a/dist/controller/productController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts b/dist/controller/uploadController.d.ts deleted file mode 100644 index d6517695..00000000 --- a/dist/controller/uploadController.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare function UploadSingleImage(req: any, res: any): void; -//# sourceMappingURL=uploadController.d.ts.map \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts.map b/dist/controller/uploadController.d.ts.map deleted file mode 100644 index 38d4676c..00000000 --- a/dist/controller/uploadController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,QAIzC"} \ No newline at end of file diff --git a/dist/controller/uploadController.js b/dist/controller/uploadController.js deleted file mode 100644 index d249fedb..00000000 --- a/dist/controller/uploadController.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UploadSingleImage = UploadSingleImage; -function UploadSingleImage(req, res) { - const { filename } = req.file; - const path = `files/${filename}`; - res.json({ path }); -} -//# sourceMappingURL=uploadController.js.map \ No newline at end of file diff --git a/dist/controller/uploadController.js.map b/dist/controller/uploadController.js.map deleted file mode 100644 index 0301614f..00000000 --- a/dist/controller/uploadController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAEA,8CAIC;AAJD,SAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG;IACtC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/dist/controller/userController.d.ts b/dist/controller/userController.d.ts deleted file mode 100644 index f67785ac..00000000 --- a/dist/controller/userController.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default class UserServiceController { - register(req: any, res: any): Promise; - login(req: any, res: any): Promise; - refresh(req: any, res: any): Promise; - GetMe(req: any, res: any): Promise; - updateMe(req: any, res: any): Promise; - updateMyPassword(req: any, res: any): Promise; - getMyProducts(req: any, res: any): Promise; - getMyLikedProducts(req: any, res: any): Promise; -} -//# sourceMappingURL=userController.d.ts.map \ No newline at end of file diff --git a/dist/controller/userController.d.ts.map b/dist/controller/userController.d.ts.map deleted file mode 100644 index c6c4aee1..00000000 --- a/dist/controller/userController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userController.d.ts","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAChC,QAAQ,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAKjB,KAAK,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAad,OAAO,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAahB,KAAK,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAKd,QAAQ,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMjB,gBAAgB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMzB,aAAa,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;IAMtB,kBAAkB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA;CAKpC"} \ No newline at end of file diff --git a/dist/controller/userController.js b/dist/controller/userController.js deleted file mode 100644 index cf383018..00000000 --- a/dist/controller/userController.js +++ /dev/null @@ -1,87 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const userService_js_1 = require("../services/userService.js"); -class UserServiceController { - register(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userService_js_1.userService.createUser(req.body); - return res.status(201).json(user); - }); - } - login(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { email, password } = req.body; - const user = yield userService_js_1.userService.getUser(email, password); - const accessToken = userService_js_1.userService.createToken(user); - const refreshToken = userService_js_1.userService.createToken(user, 'refresh'); - yield userService_js_1.userService.updateUser(user.id, { refreshToken }); - res.cookie('refreshToken', refreshToken, { - httpOnly: true, - sameSite: 'none', - secure: true - }); - return res.status(200).json({ accessToken }); - }); - } - refresh(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { refreshToken } = req.cookies; - const { userId } = req.auth; - const { accessToken, newRefreshToken } = yield userService_js_1.userService.refreshToken(userId, refreshToken); // 변경 - yield userService_js_1.userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 - res.cookie('refreshToken', newRefreshToken, { - path: '/token/refresh', - httpOnly: true, - sameSite: 'none', - secure: true, - }); - return res.json({ accessToken }); - }); - } - GetMe(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const user = yield userService_js_1.userService.getUserById(userId); - return res.status(200).json(user); - }); - } - updateMe(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const updatedUser = yield userService_js_1.userService.updateProfile(userId, req.body); - return res.status(200).json(updatedUser); - }); - } - updateMyPassword(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const updatedUser = yield userService_js_1.userService.updatePassword(userId, req.body); - return res.status(200).json(updatedUser); - }); - } - getMyProducts(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const products = yield userService_js_1.userService.getProductsByUserId(userId); - return res.status(200).json(products); - }); - } - getMyLikedProducts(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const products = yield userService_js_1.userService.getLikedProductsByUserId(userId); - return res.status(200).json(products); - }); - } -} -exports.default = UserServiceController; -//# sourceMappingURL=userController.js.map \ No newline at end of file diff --git a/dist/controller/userController.js.map b/dist/controller/userController.js.map deleted file mode 100644 index 7504c7c5..00000000 --- a/dist/controller/userController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userController.js","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAyD;AAEzD,MAAqB,qBAAqB;IAChC,QAAQ,CAAC,GAAG,EAAE,GAAG;;YACnB,MAAM,IAAI,GAAG,MAAM,4BAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;KAAA;IAEK,KAAK,CAAC,GAAG,EAAE,GAAG;;YAChB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,4BAAW,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACxD,MAAM,WAAW,GAAG,4BAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,YAAY,GAAG,4BAAW,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC9D,MAAM,4BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;YACxD,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,YAAY,EAAE;gBACrC,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,MAAM;gBAChB,MAAM,EAAE,IAAI;aACf,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;QACjD,CAAC;KAAA;IACK,OAAO,CAAC,GAAG,EAAE,GAAG;;YAClB,MAAM,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;YACrC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5B,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,MAAM,4BAAW,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK;YACpG,MAAM,4BAAW,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,KAAK;YAC9E,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,eAAe,EAAE;gBACxC,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,MAAM;gBAChB,MAAM,EAAE,IAAI;aACf,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;QACrC,CAAC;KAAA;IACK,KAAK,CAAC,GAAG,EAAE,GAAG;;YAChB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,4BAAW,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACnD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;KAAA;IACK,QAAQ,CAAC,GAAG,EAAE,GAAG;;YACnB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,WAAW,GAAG,MAAM,4BAAW,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACtE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC;KAAA;IAEK,gBAAgB,CAAC,GAAG,EAAE,GAAG;;YAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,WAAW,GAAG,MAAM,4BAAW,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACvE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC;KAAA;IAEK,aAAa,CAAC,GAAG,EAAE,GAAG;;YACxB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,QAAQ,GAAG,MAAM,4BAAW,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC/D,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;KAAA;IAEK,kBAAkB,CAAC,GAAG,EAAE,GAAG;;YAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC/B,MAAM,QAAQ,GAAG,MAAM,4BAAW,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;YACpE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;KAAA;CACJ;AA5DD,wCA4DC"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts b/dist/libs/Handler/errorHandler.d.ts deleted file mode 100644 index fda3843a..00000000 --- a/dist/libs/Handler/errorHandler.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -declare const errorHandler: (err: any, req: any, res: any, next: any) => any; -export default errorHandler; -/** - * 모든 커스텀 에러의 기본이 되는 클래스 - * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. - */ -export declare class CustomError extends Error { - constructor(statusCode?: number, message?: string, data?: {}); -} -export declare const globalErrorHandler: (err: any, req: any, res: any, next: any) => void; -//# sourceMappingURL=errorHandler.d.ts.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts.map b/dist/libs/Handler/errorHandler.d.ts.map deleted file mode 100644 index e04e6e35..00000000 --- a/dist/libs/Handler/errorHandler.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,YAAY,GAAI,QAAG,EAAE,QAAG,EAAE,QAAG,EAAE,SAAI,QAyCxC,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;gBACtB,UAAU,SAAM,EAAE,OAAO,SAAK,EAAE,IAAI,KAAK;CASxD;AAED,eAAO,MAAM,kBAAkB,GAAI,QAAG,EAAE,QAAG,EAAE,QAAG,EAAE,SAAI,SAcrD,CAAC"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js b/dist/libs/Handler/errorHandler.js deleted file mode 100644 index 47aaf7bb..00000000 --- a/dist/libs/Handler/errorHandler.js +++ /dev/null @@ -1,80 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.globalErrorHandler = exports.CustomError = void 0; -const multer_1 = __importDefault(require("multer")); -const errorHandler = (err, req, res, next) => { - if (err.name === 'PrismaCluentInitializationError') - return res.status(500).json({ - success: false, - message: '데이터베이스 연결에 실패했씁니다. DATABASE_URL을 확인해주세요.' - }); - if (err.code === 'P2002') - return res.status(409).json({ - success: false, - message: '중복된 데이터가 존재합니다.' - }); - if (err.code === 'P2025') { - return res.status(404).json({ - success: false, - message: '요청한 데이터를 찾을 수 없습니다.' - }); - } - if (err instanceof multer_1.default.MulterError) { - if (err.code === 'LIMIT_FILE_SIZE') { - return res.status(400).json({ - success: false, - message: '파일 크기가 너무 큽니다.', - }); - } - if (err.code === 'LIMIT_FILE_COUNT') { - return res.status(400).json({ - success: false, - message: '파일 개수가 너무 많습니다.', - }); - } - } - const statusCode = err.statusCode || err.status || 500; - const message = err.message || '서버 오류가 발생했습니다.'; - const path = err.path || null; - const response = { - success: false, - message, - path - }; - return res.status(statusCode).json(response); -}; -exports.default = errorHandler; -/** - * 모든 커스텀 에러의 기본이 되는 클래스 - * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. - */ -class CustomError extends Error { - constructor(statusCode = 500, message = '', data = {}) { - super(message); - this.statusCode = statusCode; - // Error stack 추적을 위해 클래스 이름 설정 - this.name = this.constructor.name; - this.data = data; - // 생성자 함수를 호출 스택에서 제외 - Error.captureStackTrace(this, this.constructor); - } -} -exports.CustomError = CustomError; -const globalErrorHandler = (err, req, res, next) => { - // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. - const statusCode = err.statusCode || 500; - const message = err.message || 'Internal Server Error'; - // 에러를 콘솔에 기록 - console.error(`[${err.name} - ${statusCode}] ${err.message}`, err.stack); - res.status(statusCode).json({ - status: 'error', - message: message, - // 개발 환경에서만 스택 트레이스를 포함할 수 있습니다. - stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, - }); -}; -exports.globalErrorHandler = globalErrorHandler; -//# sourceMappingURL=errorHandler.js.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js.map b/dist/libs/Handler/errorHandler.js.map deleted file mode 100644 index 471e837e..00000000 --- a/dist/libs/Handler/errorHandler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts b/dist/libs/catchAsync.d.ts deleted file mode 100644 index eea73b6b..00000000 --- a/dist/libs/catchAsync.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export declare const catchAsync: (fn: any) => (req: any, res: any, next: any) => void; -export declare const catchAsyncAll: (...fns: any[]) => ((req: any, res: any, next: any) => void)[]; -//# sourceMappingURL=catchAsync.d.ts.map \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts.map b/dist/libs/catchAsync.d.ts.map deleted file mode 100644 index 4a1cfdcc..00000000 --- a/dist/libs/catchAsync.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"catchAsync.d.ts","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,OAAE,MAAM,QAAG,EAAE,QAAG,EAAE,SAAI,SAEhD,CAAC;AACF,eAAO,MAAM,aAAa,GAAI,GAAG,UAAG,gDAAwB,CAAC"} \ No newline at end of file diff --git a/dist/libs/catchAsync.js b/dist/libs/catchAsync.js deleted file mode 100644 index 4c9a11b1..00000000 --- a/dist/libs/catchAsync.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.catchAsyncAll = exports.catchAsync = void 0; -const catchAsync = (fn) => (req, res, next) => { - Promise.resolve(fn(req, res, next)).catch(next); -}; -exports.catchAsync = catchAsync; -const catchAsyncAll = (...fns) => fns.map(exports.catchAsync); -exports.catchAsyncAll = catchAsyncAll; -//# sourceMappingURL=catchAsync.js.map \ No newline at end of file diff --git a/dist/libs/catchAsync.js.map b/dist/libs/catchAsync.js.map deleted file mode 100644 index d3e83ed2..00000000 --- a/dist/libs/catchAsync.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"catchAsync.js","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":";;;AAAO,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACjD,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB;AACK,MAAM,aAAa,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAU,CAAC,CAAC;AAAhD,QAAA,aAAa,iBAAmC"} \ No newline at end of file diff --git a/dist/libs/constants.d.ts b/dist/libs/constants.d.ts deleted file mode 100644 index 58c966e4..00000000 --- a/dist/libs/constants.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import express, { Request, Response, NextFunction } from 'express'; -export declare const EXPRESS: typeof express; -export declare const PORT: string | number; -export declare const prismaClient: PrismaClient; -export type ExpressRequest = Request; -export type ExpressResponse = Response; -export type ExpressNextFunction = NextFunction; -//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/dist/libs/constants.d.ts.map b/dist/libs/constants.d.ts.map deleted file mode 100644 index 9f2dcff1..00000000 --- a/dist/libs/constants.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAInE,eAAO,MAAM,OAAO,gBAAU,CAAC;AAC/B,eAAO,MAAM,IAAI,iBAA2B,CAAC;AAC7C,eAAO,MAAM,YAAY,gIAAmB,CAAC;AAG7C,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AACrC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;AACvC,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/constants.js b/dist/libs/constants.js deleted file mode 100644 index 6d819d84..00000000 --- a/dist/libs/constants.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.prismaClient = exports.PORT = exports.EXPRESS = void 0; -const dotenv = __importStar(require("dotenv")); -const client_1 = require("@prisma/client"); -const express_1 = __importDefault(require("express")); -dotenv.config(); -exports.EXPRESS = express_1.default; -exports.PORT = process.env.PORT || 3000; -exports.prismaClient = new client_1.PrismaClient; -//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/dist/libs/constants.js.map b/dist/libs/constants.js.map deleted file mode 100644 index b55f83c1..00000000 --- a/dist/libs/constants.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,2CAA8C;AAC9C,sDAAmE;AAEnE,MAAM,CAAC,MAAM,EAAE,CAAC;AAEH,QAAA,OAAO,GAAG,iBAAO,CAAC;AAClB,QAAA,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAChC,QAAA,YAAY,GAAG,IAAI,qBAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts b/dist/libs/corsSetUp.d.ts deleted file mode 100644 index 898f7ad0..00000000 --- a/dist/libs/corsSetUp.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import 'dotenv/config'; -export declare const getCorsOrigin: () => string | string[]; -//# sourceMappingURL=corsSetUp.d.ts.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts.map b/dist/libs/corsSetUp.d.ts.map deleted file mode 100644 index a54081d8..00000000 --- a/dist/libs/corsSetUp.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"corsSetUp.d.ts","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,eAAO,MAAM,aAAa,yBASzB,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.js b/dist/libs/corsSetUp.js deleted file mode 100644 index 7563d23d..00000000 --- a/dist/libs/corsSetUp.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getCorsOrigin = void 0; -require("dotenv/config"); -const getCorsOrigin = () => { - const corsOrigin = process.env.CORS_ORIGIN; - if (!corsOrigin || corsOrigin === '*') - return '*'; - if (corsOrigin.includes(',')) - return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); - // 단일 origin (trailing slash 제거) - return corsOrigin.trim().replace(/\/$/, ''); -}; -exports.getCorsOrigin = getCorsOrigin; -//# sourceMappingURL=corsSetUp.js.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.js.map b/dist/libs/corsSetUp.js.map deleted file mode 100644 index e7570d29..00000000 --- a/dist/libs/corsSetUp.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"corsSetUp.js","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":";;;AAAA,yBAAuB;AAChB,MAAM,aAAa,GAAG,GAAG,EAAE;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC3C,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAEnF,gCAAgC;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC;AATW,QAAA,aAAa,iBASxB"} \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts b/dist/libs/interfaces.d.ts deleted file mode 100644 index 012e7ebf..00000000 --- a/dist/libs/interfaces.d.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Prisma } from '@prisma/client'; -export interface ProductType { - id: number; - name: string; - description?: string; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - comments?: CommentType[]; - productLikes?: ProductLikeType[]; -} -export interface ArticleType { - id: number; - title: string; - content: string; - createdAt: Date; - comments: CommentType[]; - articleLikes: ArticleLikeType[]; -} -export interface CommentType { - id: number; - content: string; - createdAt: Date; - updatedAt: Date; - productId?: number; - articleId?: number; - product?: ProductType; - article?: ArticleType; -} -export interface UserType { - id: number; - email: string; - password: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - refreshToken?: string | null; - productLikes?: ProductLikeType[]; - articleLikes?: ArticleLikeType[]; -} -export interface UpdateUserPasswordType { - currentPassword: string; - newPassword: string; - confirmNewPassword: string; -} -export interface ProductLikeType { - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - productId: number; - user: UserType; - product: ProductType; -} -export interface ArticleLikeType { - id: number; - userId: number; - articleId: number; - user: UserType; - article: ArticleType; - createdAt: Date; -} -export type ProductFindOptions = Prisma.ProductFindManyArgs; -export type ArticleFindOptions = Prisma.ArticleFindManyArgs; -export interface ProductWithLikes extends ProductType { - productLikes: ProductLikeType[]; -} -export interface ProductWithIsLiked extends ProductType { - isLiked: boolean; -} -//# sourceMappingURL=interfaces.d.ts.map \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts.map b/dist/libs/interfaces.d.ts.map deleted file mode 100644 index 230b1e29..00000000 --- a/dist/libs/interfaces.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC;AACD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,eAAe,EAAE,CAAC;CACnC;AACD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,CAAC;CACzB;AACD,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CACpC;AAGD,MAAM,WAAW,sBAAsB;IAEnC,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;CACxB;AACD,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAE5D,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACjD,YAAY,EAAE,eAAe,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACnD,OAAO,EAAE,OAAO,CAAC;CACpB"} \ No newline at end of file diff --git a/dist/libs/interfaces.js b/dist/libs/interfaces.js deleted file mode 100644 index db919115..00000000 --- a/dist/libs/interfaces.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=interfaces.js.map \ No newline at end of file diff --git a/dist/libs/interfaces.js.map b/dist/libs/interfaces.js.map deleted file mode 100644 index cbb18070..00000000 --- a/dist/libs/interfaces.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts deleted file mode 100644 index 371115b3..00000000 --- a/dist/main.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/main.d.ts.map b/dist/main.d.ts.map deleted file mode 100644 index 28b87d89..00000000 --- a/dist/main.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js deleted file mode 100644 index e5d4ec53..00000000 --- a/dist/main.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("./libs/constants"); -const cors_1 = __importDefault(require("cors")); -const routerManager_1 = require("./Routers/routerManager"); -const corsSetUp_1 = require("./libs/corsSetUp"); -const errorHandler_1 = __importDefault(require("./libs/Handler/errorHandler")); -const app = (0, constants_1.EXPRESS)(); -app.use((0, cors_1.default)({ - origin: (0, corsSetUp_1.getCorsOrigin)(), - credentials: true, - methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], - allowedHeaders: ['Content-Type', 'Authorization'] -})); -app.use(constants_1.EXPRESS.json()); -app.use(constants_1.EXPRESS.urlencoded({ extended: true })); -app.use('/upload', constants_1.EXPRESS.static('upload')); -app.use('/', routerManager_1.RouterManager); -app.use(errorHandler_1.default); -app.listen(constants_1.PORT, () => { - console.log(`Server is running`); -}); -//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map deleted file mode 100644 index 4a49c239..00000000 --- a/dist/main.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;AAAA,gDAAiD;AACjD,gDAAwB;AACxB,2DAAwD;AACxD,gDAAiD;AACjD,+EAAuD;AAEvD,MAAM,GAAG,GAAG,IAAA,mBAAO,GAAE,CAAC;AAGtB,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC;IACT,MAAM,EAAE,IAAA,yBAAa,GAAE;IACvB,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC;IAClD,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;CACpD,CAAC,CAAC,CAAC;AAIJ,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAI7C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,6BAAa,CAAC,CAAC;AAI5B,GAAG,CAAC,GAAG,CAAC,sBAAY,CAAC,CAAC;AAEtB,GAAG,CAAC,MAAM,CAAC,gBAAI,EAAE,GAAG,EAAE;IAClB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts b/dist/middlewares/auth.d.ts deleted file mode 100644 index 2c7f9c72..00000000 --- a/dist/middlewares/auth.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -declare function verifyProduectAuth(req: any, res: any, next: any): Promise; -declare function verifyArticleAuth(req: any, res: any, next: any): Promise; -declare const _default: { - verifyAccessToken: { - (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction): Promise; - unless: typeof import("express-unless").unless; - }; - softVerifyAccessToken: (req: any, res: any, next: any) => void; - verifyProduectAuth: typeof verifyProduectAuth; - verifyArticleAuth: typeof verifyArticleAuth; - verifyRefreshToken: { - (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction): Promise; - unless: typeof import("express-unless").unless; - }; -}; -export default _default; -//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts.map b/dist/middlewares/auth.d.ts.map deleted file mode 100644 index 0ca5bae9..00000000 --- a/dist/middlewares/auth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":"AA+BA,iBAAe,kBAAkB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,EAAE,IAAI,KAAA,gBAW/C;AAED,iBAAe,iBAAiB,CAAC,GAAG,KAAA,EAAE,GAAG,KAAA,EAAE,IAAI,KAAA,gBAW9C;;;;;;;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/middlewares/auth.js b/dist/middlewares/auth.js deleted file mode 100644 index 51fcbfa1..00000000 --- a/dist/middlewares/auth.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_jwt_1 = require("express-jwt"); -const productRepository_js_1 = __importDefault(require("../repositories/productRepository.js")); -const articleRepository_js_1 = __importDefault(require("../repositories/articleRepository.js")); -const errorHandler_js_1 = require("../libs/Handler/errorHandler.js"); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const verifyAccessToken = (0, express_jwt_1.expressjwt)({ - secret: process.env.JWT_SECRET, - algorithms: ["HS256"], - requestProperty: 'user' -}); -const softVerifyAccessToken = (req, res, next) => { - var _a; - const token = (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.split(' ')[1]; - if (token) { - jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET, (err, user) => { - if (!err) { - req.user = user; - } - next(); - }); - } - else { - next(); - } -}; -const verifyRefreshToken = (0, express_jwt_1.expressjwt)({ - secret: process.env.JWT_SECRET, - algorithms: ['HS256'], - getToken: (req) => req.cookies.refreshToken, -}); -function verifyProduectAuth(req, res, next) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - const product = yield productRepository_js_1.default.findById(id); - if (!product) { - throw new errorHandler_js_1.CustomError(404, 'product not found'); - } - if (product.authorId !== req.user.id) { - throw new errorHandler_js_1.CustomError(403, 'Forbidden'); - } - return next(); - }); -} -; -function verifyArticleAuth(req, res, next) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - const article = yield articleRepository_js_1.default.findById(id); - if (!article) { - throw new errorHandler_js_1.CustomError(404, 'article not found'); - } - if (article.authorId !== req.user.id) { - throw new errorHandler_js_1.CustomError(403, 'Forbidden'); - } - return next(); - }); -} -exports.default = { - verifyAccessToken, - softVerifyAccessToken, - verifyProduectAuth, - verifyArticleAuth, - verifyRefreshToken, -}; -//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/dist/middlewares/auth.js.map b/dist/middlewares/auth.js.map deleted file mode 100644 index feb3e7b3..00000000 --- a/dist/middlewares/auth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,6CAAyC;AACzC,gGAAqE;AACrE,gGAAqE;AACrE,qEAA8D;AAC9D,gEAA+B;AAE/B,MAAM,iBAAiB,GAAG,IAAA,wBAAU,EAAC;IACjC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAC9B,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM;CAC1B,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;;IAC7C,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,OAAO,CAAC,aAAa,0CAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACvD,IAAI,KAAK,EAAE,CAAC;QACR,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACpD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACP,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YACpB,CAAC;YACD,IAAI,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,IAAI,EAAE,CAAC;IACX,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,IAAA,wBAAU,EAAC;IAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAC9B,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY;CAC9C,CAAC,CAAC;AACH,SAAe,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI;;QAC5C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,MAAM,8BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAClB,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI;;QAC3C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,MAAM,8BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,6BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAClB,CAAC;CAAA;AAED,kBAAe;IACX,iBAAiB;IACjB,qBAAqB;IACrB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;CACrB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts b/dist/repositories/articleLikeRepository.d.ts deleted file mode 100644 index 9424a24e..00000000 --- a/dist/repositories/articleLikeRepository.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -declare function find(userId: any, articleId: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -} | null>; -declare function create(userId: any, articleId: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -}>; -declare function remove(id: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -}>; -declare const _default: { - find: typeof find; - create: typeof create; - remove: typeof remove; -}; -export default _default; -//# sourceMappingURL=articleLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts.map b/dist/repositories/articleLikeRepository.d.ts.map deleted file mode 100644 index fe0b5184..00000000 --- a/dist/repositories/articleLikeRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;UASpC;AAED,iBAAe,MAAM,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;GAOtC;AAED,iBAAe,MAAM,CAAC,EAAE,KAAA;;;;;GAMvB;;;;;;AAED,wBAIE"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js b/dist/repositories/articleLikeRepository.js deleted file mode 100644 index 37052fc7..00000000 --- a/dist/repositories/articleLikeRepository.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function find(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.findUnique({ - where: { - userId_articleId: { - userId, - articleId, - }, - }, - }); - }); -} -function create(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.create({ - data: { - userId, - articleId, - }, - }); - }); -} -function remove(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - find, - create, - remove, -}; -//# sourceMappingURL=articleLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js.map b/dist/repositories/articleLikeRepository.js.map deleted file mode 100644 index f24384c6..00000000 --- a/dist/repositories/articleLikeRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAM,EAAE,SAAS;;QACjC,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAM,EAAE,SAAS;;QACnC,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAE;;QACpB,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts b/dist/repositories/articleRepository.d.ts deleted file mode 100644 index 96470f6a..00000000 --- a/dist/repositories/articleRepository.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ArticleType, ArticleFindOptions } from '../libs/interfaces.js'; -declare function findById(id: number, userId: number): Promise<{ - articleLikes: { - id: number; - createdAt: Date; - userId: number; - articleId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; -}>; -declare function findAll(findOptions: ArticleFindOptions, userId: number): Promise<({ - articleLikes: { - id: number; - createdAt: Date; - userId: number; - articleId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; -})[]>; -declare function create(userFields: ArticleType): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; -}>; -declare function update(id: number, data: ArticleFindOptions): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; -}>; -declare function ondelete(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; -}>; -declare const _default: { - findById: typeof findById; - findAll: typeof findAll; - create: typeof create; - update: typeof update; - ondelete: typeof ondelete; -}; -export default _default; -//# sourceMappingURL=articleRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts.map b/dist/repositories/articleRepository.d.ts.map deleted file mode 100644 index 7ece1f57..00000000 --- a/dist/repositories/articleRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAExE,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;GAUjD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,WAAW;;;;;;GAQ5C;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;;;;;;GASzD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.js b/dist/repositories/articleRepository.js deleted file mode 100644 index f072c370..00000000 --- a/dist/repositories/articleRepository.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -function findById(id, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - articleLikes: userId ? { where: { userId } } : false, - }; - return constants_js_1.prismaClient.article.findUniqueOrThrow({ - where: { - id, - }, - include, - }); - }); -} -function findAll(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - articleLikes: userId ? { where: { userId } } : false, - }; - return constants_js_1.prismaClient.article.findMany(Object.assign(Object.assign({}, findOptions), { include })); - }); -} -function create(userFields) { - return __awaiter(this, void 0, void 0, function* () { - const { createdAt, comments, articleLikes } = userFields, NewuserFields = __rest(userFields, ["createdAt", "comments", "articleLikes"]); - return yield constants_js_1.prismaClient.article.create({ - data: Object.assign({}, NewuserFields), - }); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - return constants_js_1.prismaClient.article.update({ - where: { - id, - }, - data: Object.assign({}, data), - }); - }); -} -function ondelete(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_js_1.prismaClient.article.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - findById, - findAll, - create, - update, - ondelete, -}; -//# sourceMappingURL=articleRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.js.map b/dist/repositories/articleRepository.js.map deleted file mode 100644 index 2ec0985c..00000000 --- a/dist/repositories/articleRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRepository.js","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,uDAAoD;AAGpD,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAc;;QAC9C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,2BAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QAEF,OAAO,2BAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAAuB;;QACzC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAApE,yCAAuD,CAAa,CAAC;QAE3E,OAAO,MAAM,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAwB;;QACtD,OAAO,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,IAAI,CACV;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,2BAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;CACX,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts b/dist/repositories/productLikeRepository.d.ts deleted file mode 100644 index cff87d62..00000000 --- a/dist/repositories/productLikeRepository.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -declare function find(userId: any, productId: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -} | null>; -declare function create(userId: any, productId: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -}>; -declare function remove(id: any): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -}>; -declare function findLikedProductsByUserId(userId: any): Promise<({ - product: { - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - }; -} & { - id: number; - createdAt: Date; - userId: number; - productId: number; -})[]>; -declare const _default: { - find: typeof find; - create: typeof create; - remove: typeof remove; - findLikedProductsByUserId: typeof findLikedProductsByUserId; -}; -export default _default; -//# sourceMappingURL=productLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts.map b/dist/repositories/productLikeRepository.d.ts.map deleted file mode 100644 index e362aad5..00000000 --- a/dist/repositories/productLikeRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;UASpC;AAED,iBAAe,MAAM,CAAC,MAAM,KAAA,EAAE,SAAS,KAAA;;;;;GAOtC;AAED,iBAAe,MAAM,CAAC,EAAE,KAAA;;;;;GAMvB;AAED,iBAAe,yBAAyB,CAAC,MAAM,KAAA;;;;;;;;;;;;;;;MAS9C;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js b/dist/repositories/productLikeRepository.js deleted file mode 100644 index a32f5cae..00000000 --- a/dist/repositories/productLikeRepository.js +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_js_1 = require("../libs/constants.js"); -function find(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_js_1.prismaClient.productLike.findUnique({ - where: { - userId_productId: { - userId, - productId, - }, - }, - }); - }); -} -function create(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_js_1.prismaClient.productLike.create({ - data: { - userId, - productId, - }, - }); - }); -} -function remove(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_js_1.prismaClient.productLike.delete({ - where: { - id, - }, - }); - }); -} -function findLikedProductsByUserId(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_js_1.prismaClient.productLike.findMany({ - where: { - userId, - }, - include: { - product: true, - }, - }); - }); -} -exports.default = { - find, - create, - remove, - findLikedProductsByUserId, -}; -//# sourceMappingURL=productLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js.map b/dist/repositories/productLikeRepository.js.map deleted file mode 100644 index d57fdece..00000000 --- a/dist/repositories/productLikeRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,uDAAoD;AAEpD,SAAe,IAAI,CAAC,MAAM,EAAE,SAAS;;QACjC,OAAO,2BAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAM,EAAE,SAAS;;QACnC,OAAO,2BAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAE;;QACpB,OAAO,2BAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,yBAAyB,CAAC,MAAM;;QAC3C,OAAO,2BAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE;gBACH,MAAM;aACT;YACD,OAAO,EAAE;gBACL,OAAO,EAAE,IAAI;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;IACN,yBAAyB;CAC5B,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts b/dist/repositories/productRepository.d.ts deleted file mode 100644 index ebcb3b55..00000000 --- a/dist/repositories/productRepository.d.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ProductType, ProductFindOptions } from './../libs/interfaces'; -declare function findByUserId(userId: number): Promise<{ - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -}[]>; -declare function findById(id: number, userId: number): Promise<{ - productLikes: { - id: number; - createdAt: Date; - userId: number; - productId: number; - }[]; -} & { - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -}>; -declare function findAll(findOptions: ProductFindOptions, userId: number): Promise<({ - productLikes: { - id: number; - createdAt: Date; - userId: number; - productId: number; - }[]; -} & { - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -})[]>; -declare function update(id: number, data: ProductType): Promise<{ - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -}>; -declare function create(userFields: ProductType): Promise<{ - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -}>; -declare function ondelete(id: number): Promise<{ - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; -}>; -declare const _default: { - findById: typeof findById; - findAll: typeof findAll; - update: typeof update; - create: typeof create; - ondelete: typeof ondelete; - findByUserId: typeof findByUserId; -}; -export default _default; -//# sourceMappingURL=productRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts.map b/dist/repositories/productRepository.d.ts.map deleted file mode 100644 index b39fee46..00000000 --- a/dist/repositories/productRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGvE,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;GAUjD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW;;;;;;;;GASlD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,WAAW;;;;;;;;GAO5C;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;GAMjC;;;;;;;;;AAED,wBAOE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.js b/dist/repositories/productRepository.js deleted file mode 100644 index 6e21821b..00000000 --- a/dist/repositories/productRepository.js +++ /dev/null @@ -1,91 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findByUserId(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.product.findMany({ - where: { - id: userId, - }, - }); - }); -} -function findById(id, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - productLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.product.findUniqueOrThrow({ - where: { - id, - }, - include, - }); - }); -} -function findAll(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - productLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.product.findMany(Object.assign(Object.assign({}, findOptions), { include })); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { id: _, createdAt, updatedAt, comments, productLikes } = data, updateData = __rest(data, ["id", "createdAt", "updatedAt", "comments", "productLikes"]); - return constants_1.prismaClient.product.update({ - where: { - id, - }, - data: updateData, - }); - }); -} -function create(userFields) { - return __awaiter(this, void 0, void 0, function* () { - const { createdAt, updatedAt, comments, productLikes } = userFields, NewuserFields = __rest(userFields, ["createdAt", "updatedAt", "comments", "productLikes"]); - return yield constants_1.prismaClient.product.create({ - data: Object.assign({}, NewuserFields), - }); - }); -} -function ondelete(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_1.prismaClient.product.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - findById, - findAll, - update, - create, - ondelete, - findByUserId, -}; -//# sourceMappingURL=productRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productRepository.js.map b/dist/repositories/productRepository.js.map deleted file mode 100644 index d0ad132f..00000000 --- a/dist/repositories/productRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAIjD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAc;;QAC9C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAiB;;QAC/C,6DAA6D;QAC7D,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAA7E,4DAAsE,CAAO,CAAC;QACpF,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAAuB;;QACzC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAA/E,sDAAkE,CAAa,CAAC;QACtF,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;CACf,CAAC"} \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts b/dist/repositories/userRepository.d.ts deleted file mode 100644 index 3d839f5f..00000000 --- a/dist/repositories/userRepository.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { UserType } from "./../libs/interfaces"; -declare function findById(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - email: string; - nickname: string; - image: string | null; - password: string; - refreshToken: string | null; -} | null>; -declare function findByEmail(email: string): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - email: string; - nickname: string; - image: string | null; - password: string; - refreshToken: string | null; -} | null>; -declare function save(user: UserType): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - email: string; - nickname: string; - image: string | null; - password: string; - refreshToken: string | null; -}>; -declare function update(id: number, data: Partial): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - email: string; - nickname: string; - image: string | null; - password: string; - refreshToken: string | null; -}>; -declare const _default: { - findById: typeof findById; - findByEmail: typeof findByEmail; - save: typeof save; - update: typeof update; -}; -export default _default; -//# sourceMappingURL=userRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts.map b/dist/repositories/userRepository.d.ts.map deleted file mode 100644 index 6789714b..00000000 --- a/dist/repositories/userRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAMjC;AAED,iBAAe,WAAW,CAAC,KAAK,EAAE,MAAM;;;;;;;;;UAMvC;AAED,iBAAe,IAAI,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;GAQjC;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;GAWxD;;;;;;;AAUD,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/userRepository.js b/dist/repositories/userRepository.js deleted file mode 100644 index 04deef26..00000000 --- a/dist/repositories/userRepository.js +++ /dev/null @@ -1,78 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findById(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.user.findUnique({ - where: { - id, - }, - }); - }); -} -function findByEmail(email) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_1.prismaClient.user.findUnique({ - where: { - email, - }, - }); - }); -} -; -function save(user) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.user.create({ - data: { - email: user.email, - nickname: user.nickname, - password: user.password, - }, - }); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - const { createdAt, updatedAt, articleLikes, productLikes } = data, Newdata = __rest(data, ["createdAt", "updatedAt", "articleLikes", "productLikes"]); - return constants_1.prismaClient.user.update({ - where: { - id, - }, - data: Object.assign({}, Newdata), - }); - }); -} -// async function createOrUpdate(provider: string, providerId: string, email: string, name: string) { -// return prismaClient.user.upsert({ -// where: { provider, providerId }, -// update: { email, name }, -// create: { provider, providerId, email, name }, -// }); -// } -exports.default = { - findById, - findByEmail, - save, - update -}; -//# sourceMappingURL=userRepository.js.map \ No newline at end of file diff --git a/dist/repositories/userRepository.js.map b/dist/repositories/userRepository.js.map deleted file mode 100644 index a4c7fa52..00000000 --- a/dist/repositories/userRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRepository.js","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAGjD,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,KAAa;;QACpC,OAAO,MAAM,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE;gBACH,KAAK;aACR;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,IAAI,CAAC,IAAc;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,IAAI,EAAE;gBACF,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aAC1B;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,KAAiB,IAAI,EAAhB,OAAO,UAAK,IAAI,EAAvE,0DAAgE,CAAO,CAAC;QAE9E,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,OAAO,CACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,qGAAqG;AACrG,wCAAwC;AACxC,2CAA2C;AAC3C,mCAAmC;AACnC,yDAAyD;AACzD,UAAU;AACV,IAAI;AAEJ,kBAAe;IACX,QAAQ;IACR,WAAW;IACX,IAAI;IACJ,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.d.ts b/dist/services/articleService.d.ts deleted file mode 100644 index b2f37a7b..00000000 --- a/dist/services/articleService.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ArticleFindOptions } from './../libs/interfaces'; -declare class ArticleService { - likeArticle(userId: number, articleId: number): Promise<{ - liked: boolean; - }>; - getArticleById(articleId: number, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; - }>; - getArticles(findOptions: ArticleFindOptions, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - title: string; - content: string; - }[]>; -} -export declare const articleService: ArticleService; -export {}; -//# sourceMappingURL=articleService.d.ts.map \ No newline at end of file diff --git a/dist/services/articleService.d.ts.map b/dist/services/articleService.d.ts.map deleted file mode 100644 index 6f1f8383..00000000 --- a/dist/services/articleService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.js b/dist/services/articleService.js deleted file mode 100644 index 69373fa4..00000000 --- a/dist/services/articleService.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.articleService = void 0; -const articleLikeRepository_1 = __importDefault(require("../repositories/articleLikeRepository")); -const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); -class ArticleService { - likeArticle(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - const existingLike = yield articleLikeRepository_1.default.find(userId, articleId); - if (existingLike) { - yield articleLikeRepository_1.default.remove(existingLike.id); - return { liked: false }; - } - else { - yield articleLikeRepository_1.default.create(userId, articleId); - return { liked: true }; - } - }); - } - getArticleById(articleId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const article = yield articleRepository_1.default.findById(articleId, userId); - const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); - }); - } - getArticles(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const articles = yield articleRepository_1.default.findAll(findOptions, userId); - return articles.map((article) => { - const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); - }); - }); - } -} -exports.articleService = new ArticleService(); -//# sourceMappingURL=articleService.js.map \ No newline at end of file diff --git a/dist/services/articleService.js.map b/dist/services/articleService.js.map deleted file mode 100644 index bb8c3b10..00000000 --- a/dist/services/articleService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kGAA0E;AAC1E,0FAAkE;AAGlE,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.d.ts b/dist/services/productService.d.ts deleted file mode 100644 index 3ba1eccb..00000000 --- a/dist/services/productService.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ProductFindOptions } from '../libs/interfaces'; -declare class ProductService { - likeProduct(userId: number, productId: number): Promise<{ - liked: boolean; - }>; - getProductById(productId: number, userId: number): Promise<{ - isLiked: boolean; - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - }>; - getProducts(findOptions: ProductFindOptions, userId: number): Promise<{ - isLiked: boolean; - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - }[]>; -} -export declare const productService: ProductService; -export {}; -//# sourceMappingURL=productService.d.ts.map \ No newline at end of file diff --git a/dist/services/productService.d.ts.map b/dist/services/productService.d.ts.map deleted file mode 100644 index 49eab074..00000000 --- a/dist/services/productService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.js b/dist/services/productService.js deleted file mode 100644 index 045d62ec..00000000 --- a/dist/services/productService.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.productService = void 0; -const productRepository_1 = __importDefault(require("../repositories/productRepository")); -const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); -class ProductService { - likeProduct(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - const existingLike = yield productLikeRepository_1.default.find(userId, productId); - if (existingLike) { - yield productLikeRepository_1.default.remove(existingLike.id); - return { liked: false }; - } - else { - yield productLikeRepository_1.default.create(userId, productId); - return { liked: true }; - } - }); - } - getProductById(productId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const product = yield productRepository_1.default.findById(productId, userId); - const { productLikes } = product, rest = __rest(product, ["productLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); - }); - } - getProducts(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productRepository_1.default.findAll(findOptions, userId); - return products.map((product) => { - const { productLikes } = product, rest = __rest(product, ["productLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); - }); - }); - } -} -exports.productService = new ProductService(); -//# sourceMappingURL=productService.js.map \ No newline at end of file diff --git a/dist/services/productService.js.map b/dist/services/productService.js.map deleted file mode 100644 index 865751b4..00000000 --- a/dist/services/productService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,kGAA0E;AAI1E,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.d.ts b/dist/services/userService.d.ts deleted file mode 100644 index 00217b83..00000000 --- a/dist/services/userService.d.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { UserType, UpdateUserPasswordType } from "./../libs/interfaces"; -declare class UserService { - createUser(user: UserType): Promise<{ - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }>; - filterSensitivceUserData(user: UserType): { - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }; - getUser(email: string, password: string): Promise<{ - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }>; - getUserById(id: number): Promise<{ - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }>; - updateProfile(id: number, data: UserType): Promise<{ - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }>; - updatePassword(id: number, data: UpdateUserPasswordType): Promise<{ - id: number; - email: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - productLikes?: import("./../libs/interfaces").ProductLikeType[]; - articleLikes?: import("./../libs/interfaces").ArticleLikeType[]; - }>; - getProductsByUserId(id: number): Promise<{ - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - }[]>; - getLikedProductsByUserId(id: number): Promise<({ - product: { - id: number; - name: string; - description: string | null; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - }; - } & { - id: number; - createdAt: Date; - userId: number; - productId: number; - })[]>; - updateUser(id: number, data: UserType): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - email: string; - nickname: string; - image: string | null; - password: string; - refreshToken: string | null; - }>; - verifyPassword(inputPassword: string, savedPassword: string): Promise; - createToken(user: UserType, type?: string): string; - refreshToken(userId: number, refreshToken: string): Promise<{ - accessToken: string; - newRefreshToken: string; - }>; -} -export declare const userService: UserService; -export {}; -//# sourceMappingURL=userService.d.ts.map \ No newline at end of file diff --git a/dist/services/userService.d.ts.map b/dist/services/userService.d.ts.map deleted file mode 100644 index 07e47c02..00000000 --- a/dist/services/userService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAQxE,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;IAe/B,wBAAwB,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;;IAKjC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;IASvC,WAAW,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;IAQtB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;;;;;;;;;;IASxC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB;;;;;;;;;;IAiBvD,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;;;;;;;;;;IAGrC,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUnC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.js b/dist/services/userService.js deleted file mode 100644 index f9f6bf0c..00000000 --- a/dist/services/userService.js +++ /dev/null @@ -1,151 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.userService = void 0; -const superstruct_1 = require("superstruct"); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -const userRepository_1 = __importDefault(require("../repositories/userRepository")); -const productRepository_1 = __importDefault(require("../repositories/productRepository")); -const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); -const bcrypt_1 = __importDefault(require("bcrypt")); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const userStructs_1 = require("../structs/userStructs"); -function hashingPassword(password) { - return __awaiter(this, void 0, void 0, function* () { - // 함수 추가 - return bcrypt_1.default.hash(password, 12); - }); -} -class UserService { - createUser(user) { - return __awaiter(this, void 0, void 0, function* () { - const existedUser = yield userRepository_1.default.findByEmail(user.email); - if (existedUser) { - throw new errorHandler_1.CustomError(422, 'User already exists', { - email: user.email, - }); - } - const hashedPassword = yield hashingPassword(user.password); - const createdUser = yield userRepository_1.default.save(Object.assign(Object.assign({}, user), { password: hashedPassword })); - return this.filterSensitivceUserData(createdUser); - }); - } - filterSensitivceUserData(user) { - const { password, refreshToken } = user, rest = __rest(user, ["password", "refreshToken"]); - return rest; - } - getUser(email, password) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findByEmail(email); - if (!user) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - yield this.verifyPassword(password, user.password); - return this.filterSensitivceUserData(user); - }); - } - getUserById(id) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findById(id); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - return this.filterSensitivceUserData(user); - }); - } - updateProfile(id, data) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(data, userStructs_1.PatchUser); - const user = yield userRepository_1.default.update(id, data); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - return this.filterSensitivceUserData(user); - }); - } - updatePassword(id, data) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(data, userStructs_1.ChangePassword); - const { currentPassword, newPassword, confirmNewPassword } = data; - if (newPassword !== confirmNewPassword) { - throw new errorHandler_1.CustomError(400, "Passwords don't match"); - } - const user = yield userRepository_1.default.findById(id); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - yield this.verifyPassword(currentPassword, user.password); - const hashedPassword = yield hashingPassword(newPassword); - const updatedUser = yield userRepository_1.default.update(id, { password: hashedPassword }); - return this.filterSensitivceUserData(updatedUser); - }); - } - getProductsByUserId(id) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productRepository_1.default.findByUserId(id); - return products; - }); - } - getLikedProductsByUserId(id) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productLikeRepository_1.default.findLikedProductsByUserId(id); - return products; - }); - } - updateUser(id, data) { - return __awaiter(this, void 0, void 0, function* () { - return yield userRepository_1.default.update(id, data); - }); - } - verifyPassword(inputPassword, savedPassword) { - return __awaiter(this, void 0, void 0, function* () { - const isValid = yield bcrypt_1.default.compare(inputPassword, savedPassword); - if (!isValid) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - }); - } - createToken(user, type) { - const JWTsecretKey = process.env.JWT_SECRET; - if (!JWTsecretKey) - throw new errorHandler_1.CustomError(500, 'JWT_SECRET is not defined'); - const payload = { userId: user.id }; - const options = { - expiresIn: type === 'refresh' ? '1d' : '10m', - }; - return jsonwebtoken_1.default.sign(payload, JWTsecretKey, options); - } - refreshToken(userId, refreshToken) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findById(userId); - if (!user || user.refreshToken !== refreshToken) { - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - } - const accessToken = this.createToken(user); // 변경 - const newRefreshToken = this.createToken(user, 'refresh'); // 추가 - return { accessToken, newRefreshToken }; // 변경 - }); - } -} -exports.userService = new UserService(); -//# sourceMappingURL=userService.js.map \ No newline at end of file diff --git a/dist/services/userService.js.map b/dist/services/userService.js.map deleted file mode 100644 index 417aeef6..00000000 --- a/dist/services/userService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userService.js","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/structs/structs.d.ts b/dist/structs/structs.d.ts deleted file mode 100644 index abb29fad..00000000 --- a/dist/structs/structs.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as s from "superstruct"; -export declare const CreateProduct: s.Struct<{ - name: string; - description: string; - price: number; - tags: string[]; -}, { - name: s.Struct; - description: s.Struct; - price: s.Struct; - tags: s.Struct>; -}>; -export declare const CreateArticle: s.Struct<{ - title: string; - content: string; -}, { - title: s.Struct; - content: s.Struct; -}>; -export declare const CreateComment: s.Struct<{ - content: string; - productId?: number | undefined; - articleId?: number | undefined; -}, { - content: s.Struct; - productId: s.Struct; - articleId: s.Struct; -}>; -export declare const PatchProduct: s.Struct<{ - name?: string | undefined; - description?: string | undefined; - price?: number | undefined; - tags?: string[] | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - name: s.Struct; - description: s.Struct; - price: s.Struct; - tags: s.Struct>; -}>>; -export declare const PatchArticle: s.Struct<{ - title?: string | undefined; - content?: string | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - title: s.Struct; - content: s.Struct; -}>>; -export declare const PatchComment: s.Struct<{ - content?: string | undefined; -}, { - content: s.Struct; -}>; -//# sourceMappingURL=structs.d.ts.map \ No newline at end of file diff --git a/dist/structs/structs.d.ts.map b/dist/structs/structs.d.ts.map deleted file mode 100644 index 40033255..00000000 --- a/dist/structs/structs.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAGjC,eAAO,MAAM,aAAa;;;;;;;;;;EAKxB,CAAC;AAGH,eAAO,MAAM,aAAa;;;;;;EAGxB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;EAepB,CAAC;AAEP,eAAO,MAAM,YAAY;;;;;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;EAAiD,CAAC"} \ No newline at end of file diff --git a/dist/structs/structs.js b/dist/structs/structs.js deleted file mode 100644 index 2f15113f..00000000 --- a/dist/structs/structs.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PatchComment = exports.PatchArticle = exports.PatchProduct = exports.CreateComment = exports.CreateArticle = exports.CreateProduct = void 0; -const s = __importStar(require("superstruct")); -exports.CreateProduct = s.object({ - name: s.size(s.string(), 1, 100), - description: s.string(), - price: s.min(s.number(), 0), - tags: s.array(s.string()), -}); -exports.CreateArticle = s.object({ - title: s.size(s.string(), 1, 100), - content: s.string(), -}); -exports.CreateComment = s.refine(s.object({ - content: s.string(), - productId: s.optional(s.number()), - articleId: s.optional(s.number()), -}), 'EitherProductIdOrArticleId', //검증 규칙 이름 -(value) => { - const { productId, articleId } = value; - // 실패 케이스: (둘 다 있거나) OR (둘 다 없거나) - if ((productId && articleId) || (!productId && !articleId)) { - return 'Comment must have *either* a productId *or* an articleId, but not both.'; - } - // 성공 케이스: 둘 중 하나만 존재함 - return true; -}); -exports.PatchProduct = s.partial(exports.CreateProduct); -exports.PatchArticle = s.partial(exports.CreateArticle); -exports.PatchComment = s.object({ content: s.optional(s.string()), }); -//# sourceMappingURL=structs.js.map \ No newline at end of file diff --git a/dist/structs/structs.js.map b/dist/structs/structs.js.map deleted file mode 100644 index ad9a7c5d..00000000 --- a/dist/structs/structs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"structs.js","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGpB,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAGU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAEU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACpC,CAAC,EACE,4BAA4B,EAAC,UAAU;AACvC,CAAC,KAAK,EAAE,EAAE;IACN,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACvC,iCAAiC;IACjC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,yEAAyE,CAAC;IACrF,CAAC;IAED,sBAAsB;IACtB,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEM,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts b/dist/structs/userStructs.d.ts deleted file mode 100644 index 99320c96..00000000 --- a/dist/structs/userStructs.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as s from "superstruct"; -export declare const CreateUser: s.Struct<{ - email: string; - nickname: string; - password: string; - image?: string | undefined; -}, { - email: s.Struct; - nickname: s.Struct; - image: s.Struct; - password: s.Struct; -}>; -export declare const PatchUser: s.Struct<{ - email?: string | undefined; - nickname?: string | undefined; - image?: string | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - email: s.Struct; - nickname: s.Struct; - image: s.Struct; -}>>; -export declare const ChangePassword: s.Struct<{ - currentPassword: string; - newPassword: string; - confirmNewPassword: string; -}, { - currentPassword: s.Struct; - newPassword: s.Struct; - confirmNewPassword: s.Struct; -}>; -//# sourceMappingURL=userStructs.d.ts.map \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts.map b/dist/structs/userStructs.d.ts.map deleted file mode 100644 index c3a5c9b7..00000000 --- a/dist/structs/userStructs.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userStructs.d.ts","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAKjC,eAAO,MAAM,UAAU;;;;;;;;;;EAKrB,CAAC;AAGH,eAAO,MAAM,SAAS;;;;;;;;GAInB,CAAC;AAEJ,eAAO,MAAM,cAAc;;;;;;;;EAIzB,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.js b/dist/structs/userStructs.js deleted file mode 100644 index bde1f424..00000000 --- a/dist/structs/userStructs.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ChangePassword = exports.PatchUser = exports.CreateUser = void 0; -const s = __importStar(require("superstruct")); -const is_email_1 = __importDefault(require("is-email")); -const email = s.refine(s.string(), 'is_email', (v) => (0, is_email_1.default)(v)); -exports.CreateUser = s.object({ - email: email, - nickname: s.string(), - image: s.optional(s.string()), - password: s.string(), -}); -exports.PatchUser = s.partial(s.object({ - email: email, - nickname: s.string(), - image: s.optional(s.string()), -})); -exports.ChangePassword = s.object({ - currentPassword: s.string(), - newPassword: s.string(), - confirmNewPassword: s.string(), -}); -//# sourceMappingURL=userStructs.js.map \ No newline at end of file diff --git a/dist/structs/userStructs.js.map b/dist/structs/userStructs.js.map deleted file mode 100644 index 662473fb..00000000 --- a/dist/structs/userStructs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userStructs.js","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,wDAA+B;AAE/B,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,kBAAO,EAAC,CAAC,CAAC,CAAC,CAAC;AAErD,QAAA,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGU,QAAA,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC,CAAC;AAES,QAAA,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;IAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC"} \ No newline at end of file diff --git a/package.json b/package.json index 1852d756..903b1c81 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "main": "./dist/main.js", "scripts": { "build": "tsc", - "dev": "nodemon --watch src --exec ts-node./src/main.ts", + "dev": "nodemon --watch src --exec ts-node ./src/main.ts", "start": "node ./dist/main.js" }, "dependencies": { diff --git a/sprint-mission-2/ElectronicProduct.js b/sprint-mission-2/ElectronicProduct.js index bf1e149b..e9b051ab 100644 --- a/sprint-mission-2/ElectronicProduct.js +++ b/sprint-mission-2/ElectronicProduct.js @@ -1,4 +1,4 @@ -import { Product } from './product.js'; +import { Product } from './product'; export class ElectronicProduct extends Product { constructor(name, decription, prcie, tags, images, manufacturerr) { diff --git a/src/Routers/articleRouter.ts b/src/Routers/articleRouter.ts index e294ced4..77376f97 100644 --- a/src/Routers/articleRouter.ts +++ b/src/Routers/articleRouter.ts @@ -1,7 +1,7 @@ -import { EXPRESS } from '../libs/constants.js'; -import { catchAsync } from '../libs/catchAsync.js'; -import auth from '../middlewares/auth.js'; -import ArticleController from '../controller/articleController.js'; +import { EXPRESS } from '../libs/constants'; +import { catchAsync } from '../libs/catchAsync'; +import auth from '../middlewares/auth'; +import ArticleController from '../controller/articleController'; const articleRouter = EXPRESS.Router(); const articleController = new ArticleController(); diff --git a/src/Routers/commentRouter.ts b/src/Routers/commentRouter.ts index e02479d3..faafd7dd 100644 --- a/src/Routers/commentRouter.ts +++ b/src/Routers/commentRouter.ts @@ -1,13 +1,13 @@ -import { EXPRESS } from '../libs/constants.js'; -import { catchAsync, catchAsyncAll } from '../libs/catchAsync.js'; -import auth from '../middlewares/auth.js'; +import { EXPRESS } from '../libs/constants'; +import { catchAsync, catchAsyncAll } from '../libs/catchAsync'; +import auth from '../middlewares/auth'; import { GetComment, PostComment, GetCommentById, PatchCommentById, DeleteCommentById -} from '../controller/commentController.js'; +} from '../controller/commentController'; const commentRouter = EXPRESS.Router(); diff --git a/src/Routers/productRouter.ts b/src/Routers/productRouter.ts index 9b94e89e..00686896 100644 --- a/src/Routers/productRouter.ts +++ b/src/Routers/productRouter.ts @@ -1,7 +1,7 @@ -import { EXPRESS } from '../libs/constants.js'; -import { catchAsync } from '../libs/catchAsync.js'; -import auth from '../middlewares/auth.js'; -import ProductController from '../controller/productController.js'; +import { EXPRESS } from '../libs/constants'; +import { catchAsync } from '../libs/catchAsync'; +import auth from '../middlewares/auth'; +import ProductController from '../controller/productController'; const productRouter = EXPRESS.Router(); const productController = new ProductController(); diff --git a/src/Routers/routerManager.ts b/src/Routers/routerManager.ts index 332d0cf2..79aab70c 100644 --- a/src/Routers/routerManager.ts +++ b/src/Routers/routerManager.ts @@ -1,9 +1,9 @@ -import { EXPRESS } from '../libs/constants.js'; -import productRouter from './productRouter.js'; -import articleRouter from './articleRouter.js'; -import commentRouter from './commentRouter.js'; -import uploadRouter from './uploadRouter.js'; -import userRouter from './userRouter.js'; +import { EXPRESS } from '../libs/constants'; +import productRouter from './productRouter'; +import articleRouter from './articleRouter'; +import commentRouter from './commentRouter'; +import uploadRouter from './uploadRouter'; +import userRouter from './userRouter'; export const RouterManager = EXPRESS.Router(); diff --git a/src/Routers/uploadRouter.ts b/src/Routers/uploadRouter.ts index 4a2ea19a..960e8455 100644 --- a/src/Routers/uploadRouter.ts +++ b/src/Routers/uploadRouter.ts @@ -1,9 +1,9 @@ -import { EXPRESS } from '../libs/constants.js'; -import { catchAsync } from '../libs/catchAsync.js'; +import { EXPRESS } from '../libs/constants'; +import { catchAsync } from '../libs/catchAsync'; import multer from 'multer'; import { UploadSingleImage -} from '../controller/uploadController.js'; +} from '../controller/uploadController'; const uploadRouter = EXPRESS.Router(); diff --git a/src/Routers/userRouter.ts b/src/Routers/userRouter.ts index 1160c9bb..320351dd 100644 --- a/src/Routers/userRouter.ts +++ b/src/Routers/userRouter.ts @@ -1,7 +1,7 @@ -import { EXPRESS } from '../libs/constants.js'; -import { catchAsync } from '../libs/catchAsync.js'; -import UserServiceController from '../controller/userController.js'; -import auth from '../middlewares/auth.js'; +import { EXPRESS } from '../libs/constants'; +import { catchAsync } from '../libs/catchAsync'; +import UserServiceController from '../controller/userController'; +import auth from '../middlewares/auth'; const userRouter = EXPRESS.Router(); const userController = new UserServiceController(); diff --git a/src/controller/articleController.ts b/src/controller/articleController.ts index 1fffde4a..e4e04b8a 100644 --- a/src/controller/articleController.ts +++ b/src/controller/articleController.ts @@ -1,10 +1,12 @@ -import { articleService } from '../services/articleService.js'; +import { articleService } from '../services/articleService'; import { assert } from 'superstruct'; -import { CreateArticle, PatchArticle } from '../structs/structs.js'; -import articleRepository from '../repositories/articleRepository.js'; +import { CreateArticle, PatchArticle } from '../structs/structs'; +import articleRepository from '../repositories/articleRepository'; +import { ExpressHandler } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; export default class ArticleController { - async getArticles(req, res) { + getArticles: ExpressHandler = async (req, res) => { const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; let orderBy; switch (order) { @@ -17,11 +19,10 @@ export default class ArticleController { default: orderBy = { createdAt: 'desc' }; } - // parse offset/limit and only include `take` when a positive integer is provided - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); + const parsedOffset = parseInt(offset as string, 10) || 0; + const parsedLimit = parseInt(limit as string, 10) || 0; - const findOptions = { + const findOptions: any = { where: { title: { contains: title, @@ -39,43 +40,54 @@ export default class ArticleController { } const userId = req.user ? req.user.userId : null; + if (!userId) return new CustomError(404, "UserId Not Found"); const articles = await articleService.getArticles(findOptions, userId); res.send(articles); - } + }; - async getArticleById(req, res) { + getArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); const userId = req.user ? req.user.userId : null; - const article = await articleService.getArticleById(id, userId); + if (!userId) return new CustomError(404, "userId Not Found"); + const article = await articleService.getArticleById(_id, userId); res.send(article); - } + }; - async postArticle(req, res) { + postArticle: ExpressHandler = async (req, res) => { assert(req.body, CreateArticle); const { ...userFields } = req.body; const article = await articleRepository.create(userFields); res.status(201).send(article); - } + }; - async patchArticleById(req, res) { + patchArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); assert(req.body, PatchArticle); const { ...userFields } = req.body; - const article = await articleRepository.update(id, userFields); + const article = await articleRepository.update(_id, userFields); res.send(article); - } + }; - async deleteArticleById(req, res) { + deleteArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; - const article = await articleRepository.ondelete(id); + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); + const article = await articleRepository.ondelete(_id); res.send(article); - } + }; - async likeArticle(req, res) { + likeArticle: ExpressHandler = async (req, res) => { + if (!req.user) return new CustomError(404, "user Not Found"); const userId = req.user.userId; const articleId = req.params.id; - const result = await articleService.likeArticle(userId, articleId); + if (!articleId) return new CustomError(404, "articleId Not Found"); + const _articleId = parseInt(articleId); + const result = await articleService.likeArticle(userId, _articleId); return res.status(200).json(result); - } + }; } diff --git a/src/controller/commentController.ts b/src/controller/commentController.ts index 8cce4be4..678616d6 100644 --- a/src/controller/commentController.ts +++ b/src/controller/commentController.ts @@ -1,27 +1,28 @@ -import { prismaClient } from '../libs/constants.js'; +import { prismaClient, Prisma } from '../libs/constants'; import { assert } from 'superstruct'; -import { CreateComment, PatchComment } from '../structs/structs.js'; +import { CreateComment, PatchComment } from '../structs/structs'; +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; -export async function GetComment(req, res) { - const { productId, articleId } = req.query; - const { take = '10', cursor } = req.query; - const parsedTake = parseInt(take, 10); +export async function GetComment(req: ExpressRequest, res: ExpressResponse) { + const { take = '10', cursor, productId, articleId } = req.query; + const parsedTake = parseInt(take as string, 10) || 0; if (isNaN(parsedTake) || parsedTake <= 0) { return res.status(400).send({ error: 'Invalid "take" parameter.' }); } - const whereClause = {}; + const whereClause: Prisma.CommentWhereInput = {}; if (productId) { - whereClause.productId = productId; // 상품 ID로 필터링 + whereClause.productId = Number(productId); // 상품 ID로 필터링 } else if (articleId) { - whereClause.articleId = articleId; // 게시글 ID로 필터링 + whereClause.articleId = Number(articleId); // 게시글 ID로 필터링 } - const findOptions = { + const findOptions: Prisma.CommentFindManyArgs = { take: parsedTake, where: whereClause, orderBy: { @@ -30,16 +31,18 @@ export async function GetComment(req, res) { }; if (cursor) { + const parsedCursor = parseInt(cursor as string, 10); + findOptions.skip = 1; findOptions.cursor = { - id: cursor, + id: parsedCursor, }; } const comments = await prismaClient.comment.findMany(findOptions); // let nextCursor = null; - if (comments.length === parsedTake) { - nextCursor = comments[comments.length - 1].id; + if (comments.length > 0 && comments.length === parsedTake) { + nextCursor = comments[comments.length - 1]!.id; } res.status(200).json({ comments, @@ -49,18 +52,20 @@ export async function GetComment(req, res) { } -export async function GetCommentById(req, res) { +export async function GetCommentById(req: ExpressRequest, res: ExpressResponse) { const { id } = req.params; + const paesedId = parseInt(id as string, 10) || null; + if (!paesedId) return new CustomError(404, "id Not Found"); const Comment = await prismaClient.comment.findUniqueOrThrow({ where: { - id + id: paesedId }, }); res.send(Comment); } -export async function PostComment(req, res) { +export async function PostComment(req: ExpressRequest, res: ExpressResponse) { assert(req.body, CreateComment); const { content, productId, articleId } = req.body; if ((productId && articleId) || (!productId && !articleId)) { @@ -82,18 +87,19 @@ export async function PostComment(req, res) { res.status(201).send(comment); } -export async function PatchCommentById(req, res) { - const { id } = req.params; +export async function PatchCommentById(req: ExpressRequest, res: ExpressResponse) { assert(req.body, PatchComment); const { content } = req.body; - + const { id } = req.params; + const paesedId = parseInt(id as string, 10) || null; + if (!paesedId) return new CustomError(404, "id Not Found"); if (content !== undefined && content.trim() === '') { return res.status(400).json({ error: 'Content cannot be empty.' }); } const comment = await prismaClient.comment.update({ where: { - id + id: paesedId }, data: { content: content, @@ -102,11 +108,13 @@ export async function PatchCommentById(req, res) { res.send(comment); } -export async function DeleteCommentById(req, res) { +export async function DeleteCommentById(req: ExpressRequest, res: ExpressResponse) { const { id } = req.params; + const paesedId = parseInt(id as string, 10) || null; + if (!paesedId) return new CustomError(404, "id Not Found"); const Comment = await prismaClient.comment.delete({ where: { - id + id: paesedId }, }); res.send(Comment); diff --git a/src/controller/productController.ts b/src/controller/productController.ts index 7bb8162b..2baf0022 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -1,11 +1,15 @@ -import { productService } from '../services/productService.js'; +import { productService } from '../services/productService'; import { assert } from 'superstruct'; -import { CreateProduct, PatchProduct } from '../structs/structs.js'; -import productRepository from '../repositories/productRepository.js'; +import { CreateProduct, PatchProduct } from '../structs/structs'; +import productRepository from '../repositories/productRepository'; +import { ExpressHandler } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; +import { removeUndefined } from './../libs/removeTool'; + export default class ProductController { - async GetProduct(req, res) { - const { offset = 0, limit = 0, order = 'newset', name = "", description = "" } = req.query; + GetProduct: ExpressHandler = async (req, res) => { + const { offset = '0', limit = '0', order = 'newest', name = "", description = "" } = req.query; let orderBy; switch (order) { case 'oldest': @@ -17,63 +21,74 @@ export default class ProductController { default: orderBy = { createdAt: 'desc' }; } - const parsedOffset = Number.isNaN(parseInt(offset)) ? 0 : parseInt(offset); - const parsedLimit = parseInt(limit); + const parsedOffset = parseInt(offset as string, 10) || 0; + const parsedLimit = parseInt(limit as string, 10) || 0; - const findOptions = { + const findOptions: any = { where: { name: { - contains: name, + contains: name as string, }, description: { - contains: description, + contains: description as string, }, }, orderBy, skip: parsedOffset, }; - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + if (parsedLimit > 0) { findOptions.take = parsedLimit; } const userId = req.user ? req.user.userId : null; + if (!userId) return new CustomError(404, "UserId Not Found"); const products = await productService.getProducts(findOptions, userId); res.send(products); - } + }; - async GetProductById(req, res) { + GetProductById: ExpressHandler = async (req, res) => { const id = req.params.id; + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); const userId = req.user ? req.user.userId : null; - const product = await productService.getProductById(id, userId); + if (!userId) return new CustomError(404, "userId Not Found"); + const product = await productService.getProductById(_id, userId); res.send(product); - } + }; - async PostProduct(req, res) { + PostProduct: ExpressHandler = async (req, res) => { assert(req.body, CreateProduct); const { ...userFields } = req.body; const product = await productRepository.create(userFields); res.status(201).send(product); - } + }; - async PatchProductById(req, res) { + PatchProductById: ExpressHandler = async (req, res) => { const id = req.params.id; + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); assert(req.body, PatchProduct); - const { ...userFields } = req.body; - const Product = await productRepository.update(id, userFields); + const userFields = removeUndefined(req.body); + const Product = await productRepository.update(_id, userFields); res.send(Product); - } + }; - async DeleteProductById(req, res) { + DeleteProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - const Product = await productRepository.ondelete(id); + if (!id) return new CustomError(404, "id Not Found"); + const _id = parseInt(id); + const Product = await productRepository.ondelete(_id); res.send(Product); - } + }; - async likeProduct(req, res) { + likeProduct: ExpressHandler = async (req, res) => { + if (!req.user) return new CustomError(404, "user Not Found"); const userId = req.user.userId; const productId = req.params.id; - const result = await productService.likeProduct(userId, productId); + if (!productId) return new CustomError(404, "productId Not Found"); + const _productId = parseInt(productId); + const result = await productService.likeProduct(userId, _productId); return res.status(200).json(result); - } + }; } \ No newline at end of file diff --git a/src/controller/uploadController.ts b/src/controller/uploadController.ts index 815bcccf..46dde273 100644 --- a/src/controller/uploadController.ts +++ b/src/controller/uploadController.ts @@ -1,6 +1,9 @@ - -export function UploadSingleImage(req, res) { +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; +export function UploadSingleImage(req: ExpressRequest, res: ExpressResponse) { + if (!req.file) + return new CustomError(404, "file not found"); const { filename } = req.file; const path = `files/${filename}`; res.json({ path }); diff --git a/src/controller/userController.ts b/src/controller/userController.ts index 1c2feef8..e66d3ba8 100644 --- a/src/controller/userController.ts +++ b/src/controller/userController.ts @@ -1,12 +1,13 @@ -import { userService } from '../services/userService.js'; +import { userService } from '../services/userService'; +import { ExpressHandler } from '../libs/constants'; export default class UserServiceController { - async register(req, res) { + register: ExpressHandler = async (req, res) => { const user = await userService.createUser(req.body); return res.status(201).json(user); - } + }; - async login(req, res) { + login: ExpressHandler = async (req, res) => { const { email, password } = req.body; const user = await userService.getUser(email, password); const accessToken = userService.createToken(user); @@ -18,10 +19,10 @@ export default class UserServiceController { secure: true }); return res.status(200).json({ accessToken }); - } - async refresh(req, res) { + }; + refresh: ExpressHandler = async (req, res) => { const { refreshToken } = req.cookies; - const { userId } = req.auth; + const { userId } = req.auth!; const { accessToken, newRefreshToken } = await userService.refreshToken(userId, refreshToken); // 변경 await userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 res.cookie('refreshToken', newRefreshToken, { // 추가 @@ -31,33 +32,33 @@ export default class UserServiceController { secure: true, }); return res.json({ accessToken }); - } - async GetMe(req, res) { - const userId = req.user.userId; + }; + GetMe: ExpressHandler = async (req, res) => { + const userId = req.user!.userId; const user = await userService.getUserById(userId); return res.status(200).json(user); - } - async updateMe(req, res) { - const userId = req.user.userId; + }; + updateMe: ExpressHandler = async (req, res) => { + const userId = req.user!.userId; const updatedUser = await userService.updateProfile(userId, req.body); return res.status(200).json(updatedUser); - } + }; - async updateMyPassword(req, res) { - const userId = req.user.userId; + updateMyPassword: ExpressHandler = async (req, res) => { + const userId = req.user!.userId; const updatedUser = await userService.updatePassword(userId, req.body); return res.status(200).json(updatedUser); - } + }; - async getMyProducts(req, res) { - const userId = req.user.userId; + getMyProducts: ExpressHandler = async (req, res) => { + const userId = req.user!.userId; const products = await userService.getProductsByUserId(userId); return res.status(200).json(products); - } + }; - async getMyLikedProducts(req, res) { - const userId = req.user.userId; + getMyLikedProducts: ExpressHandler = async (req, res) => { + const userId = req.user!.userId; const products = await userService.getLikedProductsByUserId(userId); return res.status(200).json(products); - } + }; } diff --git a/src/libs/Handler/errorHandler.ts b/src/libs/Handler/errorHandler.ts index 6b9e1cd6..d7383627 100644 --- a/src/libs/Handler/errorHandler.ts +++ b/src/libs/Handler/errorHandler.ts @@ -1,23 +1,33 @@ import multer from 'multer'; +import { Prisma, ExpressRequest, ExpressResponse, ExpressNextFunction } from './../constants'; -const errorHandler = (err, req, res, next) => { - if (err.name === 'PrismaCluentInitializationError') +const errorHandler = (err: any + , req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => { + // 1. Prisma 초기화 에러 (DB 연결 실패 등) + if (err instanceof Prisma.PrismaClientInitializationError) { return res.status(500).json({ success: false, - message: '데이터베이스 연결에 실패했씁니다. DATABASE_URL을 확인해주세요.' + message: '데이터베이스 연결에 실패했습니다. DATABASE_URL을 확인해주세요.' }); + } - if (err.code === 'P2002') - return res.status(409).json({ - success: false, - message: '중복된 데이터가 존재합니다.' - }); - if (err.code === 'P2025') { - return res.status(404).json({ - success: false, - message: '요청한 데이터를 찾을 수 없습니다.' - }); + // 2. Prisma 요청 에러 (중복 키, 데이터 없음 등) + if (err instanceof Prisma.PrismaClientKnownRequestError) { + if (err.code === 'P2002') { + return res.status(409).json({ + success: false, + message: '중복된 데이터가 존재합니다.' + }); + } + if (err.code === 'P2025') { + return res.status(404).json({ + success: false, + message: '요청한 데이터를 찾을 수 없습니다.' + }); + } } + + // 3. Multer(파일 업로드) 에러 if (err instanceof multer.MulterError) { if (err.code === 'LIMIT_FILE_SIZE') { return res.status(400).json({ @@ -32,15 +42,22 @@ const errorHandler = (err, req, res, next) => { }); } } - const statusCode = err.statusCode || err.status || 500; + + // 4. CustomError 및 기타 에러 처리 + // any 타입이므로 안전하게 접근하기 위해 || 연산자 활용 + const statusCode = err.statusCode || 500; const message = err.message || '서버 오류가 발생했습니다.'; - const path = err.path || null; - const response = { + + // path가 없는 에러 객체도 많으므로 안전하게 처리 + const path = err.data || null; + + console.error(`[Error] ${statusCode} - ${message}`, err.stack); + + return res.status(statusCode).json({ success: false, message, path - }; - return res.status(statusCode).json(response); + }); }; export default errorHandler; @@ -50,6 +67,10 @@ export default errorHandler; * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. */ export class CustomError extends Error { + + public statusCode: number; + public data: Record; + constructor(statusCode = 500, message = '', data = {}) { super(message); this.statusCode = statusCode; @@ -61,7 +82,8 @@ export class CustomError extends Error { } } -export const globalErrorHandler = (err, req, res, next) => { +export const globalErrorHandler = (err: any + , req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => { // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. const statusCode = err.statusCode || 500; const message = err.message || 'Internal Server Error'; diff --git a/src/libs/catchAsync.ts b/src/libs/catchAsync.ts index 0409bd01..9205a082 100644 --- a/src/libs/catchAsync.ts +++ b/src/libs/catchAsync.ts @@ -1,4 +1,11 @@ -export const catchAsync = (fn) => (req, res, next) => { - Promise.resolve(fn(req, res, next)).catch(next); +import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from './constants'; + +export const catchAsync = (fn: ExpressHandler): ExpressHandler => { + return (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => { + Promise.resolve(fn(req, res, next)).catch(next); + }; }; -export const catchAsyncAll = (...fns) => fns.map(catchAsync); \ No newline at end of file + +export const catchAsyncAll = (...fns: ExpressHandler[]): ExpressHandler[] => { + return fns.map(catchAsync); +}; \ No newline at end of file diff --git a/src/libs/constants.ts b/src/libs/constants.ts index da16ec04..9d93765c 100644 --- a/src/libs/constants.ts +++ b/src/libs/constants.ts @@ -1,5 +1,5 @@ import * as dotenv from 'dotenv'; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient, Prisma } from '@prisma/client'; import express, { RequestHandler, Request, Response, NextFunction } from 'express'; dotenv.config(); @@ -7,7 +7,7 @@ dotenv.config(); export const EXPRESS = express; export const PORT = process.env.PORT || 3000; export const prismaClient = new PrismaClient; - +export { Prisma }; export type ExpressHandler = RequestHandler; export type ExpressRequest = Request; export type ExpressResponse = Response; diff --git a/src/libs/corsSetUp.ts b/src/libs/corsSetUp.ts index 0a6a6bc4..47de0dcc 100644 --- a/src/libs/corsSetUp.ts +++ b/src/libs/corsSetUp.ts @@ -1,5 +1,5 @@ import 'dotenv/config'; -export const getCorsOrigin = () => { +export const getCorsOrigin = (): string | string[] => { const corsOrigin = process.env.CORS_ORIGIN; if (!corsOrigin || corsOrigin === '*') return '*'; diff --git a/src/libs/interfaces.ts b/src/libs/interfaces.ts index 78333d03..6ef9ffe2 100644 --- a/src/libs/interfaces.ts +++ b/src/libs/interfaces.ts @@ -1,4 +1,5 @@ -import { Prisma } from '@prisma/client'; + +import { Prisma } from './../libs/constants'; export interface ProductType { id: number; @@ -16,8 +17,8 @@ export interface ArticleType { title: string; content: string; createdAt: Date; - comments: CommentType[]; - articleLikes: ArticleLikeType[]; + comments?: CommentType[]; + articleLikes?: ArticleLikeType[]; } export interface CommentType { id: number; @@ -75,6 +76,29 @@ export interface ProductWithLikes extends ProductType { productLikes: ProductLikeType[]; } + + export interface ProductWithIsLiked extends ProductType { + isLiked: boolean; -} \ No newline at end of file + +} + +export interface ArticleWithLikes extends ArticleType { + articleLikes: ArticleLikeType[]; +} + +export interface ArticleWithIsLiked extends ArticleType { + isLiked: boolean; +} + +export type UserPublicData = Omit; + +export type ProductPublicData = Omit; + +export type UpdateProductData = Partial; + + +export type ArticlePublicData = Omit; + +export type UpdateArticleData = Partial; diff --git a/src/libs/removeTool.ts b/src/libs/removeTool.ts new file mode 100644 index 00000000..489789d3 --- /dev/null +++ b/src/libs/removeTool.ts @@ -0,0 +1,10 @@ +// undefined인 키를 싹 지워주는 함수 +export const removeUndefined = (obj: any) => { + const newObj: any = {}; + Object.keys(obj).forEach((key) => { + if (obj[key] !== undefined) { + newObj[key] = obj[key]; + } + }); + return newObj; +}; diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts index dac8f23d..84318b05 100644 --- a/src/middlewares/auth.ts +++ b/src/middlewares/auth.ts @@ -3,7 +3,7 @@ import productRepository from '../repositories/productRepository'; import articleRepository from '../repositories/articleRepository'; import { CustomError } from '../libs/Handler/errorHandler'; import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; -import jwt from 'jsonwebtoken'; +import jwt, { JwtPayload } from 'jsonwebtoken'; const JWT_SECRET = process.env.JWT_SECRET; @@ -11,7 +11,7 @@ if (!JWT_SECRET) { throw new Error('JWT_SECRET is not defined in environment variables.'); } -const verifyAccessToken = expressjwt({ +const verifyAccessToken: ExpressHandler = expressjwt({ secret: JWT_SECRET, algorithms: ["HS256"], requestProperty: 'user' @@ -20,9 +20,9 @@ const verifyAccessToken = expressjwt({ const softVerifyAccessToken: ExpressHandler = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (token) { - jwt.verify(token, JWT_SECRET, (err, user) => { - if (!err && user) { - req.user = user; + jwt.verify(token, JWT_SECRET, (err, decoded) => { + if (!err && decoded && typeof decoded === 'object') { + req.user = decoded as (JwtPayload & { userId: number; }); } next(); }); @@ -31,11 +31,12 @@ const softVerifyAccessToken: ExpressHandler = (req, res, next) => { } }; -const verifyRefreshToken = expressjwt({ +const verifyRefreshToken: ExpressHandler = expressjwt({ secret: JWT_SECRET, algorithms: ['HS256'], getToken: (req) => req.cookies.refreshToken, }); + async function verifyProduectAuth (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { const id = req.params.id || ""; @@ -45,7 +46,7 @@ async function verifyProduectAuth if (!product) { throw new CustomError(404, 'product not found'); } - if (typeof req.user !== 'object' || req.user.id === undefined || product.id !== req.user.id) { + if (typeof req.user !== 'object' || req.user.userId === undefined || product.id !== req.user.userId) { throw new CustomError(403, 'Forbidden'); } return next(); @@ -60,7 +61,7 @@ async function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next if (!article) { throw new CustomError(404, 'article not found'); } - if (typeof req.user !== 'object' || req.user.id === undefined || article.id !== req.user.id) { + if (typeof req.user !== 'object' || req.user.userId === undefined || article.id !== req.user.userId) { throw new CustomError(403, 'Forbidden'); } return next(); diff --git a/src/repositories/articleRepository.ts b/src/repositories/articleRepository.ts index e0958bbc..cd0fa023 100644 --- a/src/repositories/articleRepository.ts +++ b/src/repositories/articleRepository.ts @@ -1,5 +1,9 @@ import { prismaClient } from '../libs/constants'; -import { ArticleType, ArticleFindOptions } from '../libs/interfaces'; +import { + ArticlePublicData, + ArticleFindOptions, + UpdateArticleData, +} from '../libs/interfaces'; async function findById(id: number, userId?: number) { const include = { @@ -24,8 +28,8 @@ async function findAll(findOptions: ArticleFindOptions, userId: number) { }); } -async function create(userFields: ArticleType) { - const { createdAt, comments, articleLikes, ...NewuserFields } = userFields; +async function create(userFields: ArticlePublicData) { + const { comments, articleLikes, ...NewuserFields } = userFields; return await prismaClient.article.create({ data: { @@ -34,14 +38,13 @@ async function create(userFields: ArticleType) { }); } -async function update(id: number, data: ArticleFindOptions) { +async function update(id: number, data: UpdateArticleData) { + const { comments, articleLikes, ...updateData } = data; return prismaClient.article.update({ where: { id, }, - data: { - ...data, - }, + data: updateData, }); } diff --git a/src/repositories/productRepository.ts b/src/repositories/productRepository.ts index 5f7d2834..b83cb099 100644 --- a/src/repositories/productRepository.ts +++ b/src/repositories/productRepository.ts @@ -1,5 +1,7 @@ import { prismaClient } from '../libs/constants'; -import { ProductType, ProductFindOptions } from './../libs/interfaces'; +import { ProductPublicData, ProductFindOptions, UpdateProductData } from './../libs/interfaces'; + + async function findByUserId(userId: number) { @@ -32,8 +34,8 @@ async function findAll(findOptions: ProductFindOptions, userId: number) { }); } -async function update(id: number, data: ProductType) { - const { id: _, createdAt, updatedAt, comments, productLikes, ...updateData } = data; +async function update(id: number, data: UpdateProductData) { + const { comments, productLikes, ...updateData } = data; return prismaClient.product.update({ where: { id, @@ -42,8 +44,8 @@ async function update(id: number, data: ProductType) { }); } -async function create(userFields: ProductType) { - const { createdAt, updatedAt, comments, productLikes, ...NewuserFields } = userFields; +async function create(userFields: ProductPublicData) { + const { comments, productLikes, ...NewuserFields } = userFields; return await prismaClient.product.create({ data: { ...NewuserFields, diff --git a/src/services/userService.ts b/src/services/userService.ts index f7a1ca5d..0270d6c6 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -6,7 +6,7 @@ import productLikeRepository from '../repositories/productLikeRepository'; import bcrypt from 'bcrypt'; import jwt, { JwtPayload, SignOptions } from 'jsonwebtoken'; import { PatchUser, ChangePassword } from '../structs/userStructs'; -import { UserType, UpdateUserPasswordType } from "./../libs/interfaces"; +import { UserType, UpdateUserPasswordType, UserPublicData } from "./../libs/interfaces"; async function hashingPassword(password: string) { @@ -15,7 +15,7 @@ async function hashingPassword(password: string) { } class UserService { - async createUser(user: UserType) { + async createUser(user: UserType): Promise { const existedUser = await userRepository.findByEmail(user.email); if (existedUser) { throw new CustomError(422, 'User already exists', { @@ -30,12 +30,12 @@ class UserService { return this.filterSensitivceUserData(createdUser); } - filterSensitivceUserData(user: UserType) { + filterSensitivceUserData(user: UserType): UserPublicData { const { password, refreshToken, ...rest } = user; return rest; } - async getUser(email: string, password: string) { + async getUser(email: string, password: string): Promise { const user = await userRepository.findByEmail(email); if (!user) throw new CustomError(401, 'Unauthorized'); @@ -44,7 +44,7 @@ class UserService { } - async getUserById(id: number) { + async getUserById(id: number): Promise { const user = await userRepository.findById(id); if (!user) { throw new CustomError(404, 'User not found'); @@ -52,7 +52,7 @@ class UserService { return this.filterSensitivceUserData(user); } - async updateProfile(id: number, data: UserType) { + async updateProfile(id: number, data: Partial): Promise { assert(data, PatchUser); const user = await userRepository.update(id, data); if (!user) { @@ -61,7 +61,7 @@ class UserService { return this.filterSensitivceUserData(user); } - async updatePassword(id: number, data: UpdateUserPasswordType) { + async updatePassword(id: number, data: UpdateUserPasswordType): Promise { assert(data, ChangePassword); const { currentPassword, newPassword, confirmNewPassword } = data; if (newPassword !== confirmNewPassword) { @@ -89,14 +89,14 @@ class UserService { } - async updateUser(id: number, data: UserType) { + async updateUser(id: number, data: Partial) { return await userRepository.update(id, data); } async verifyPassword(inputPassword: string, savedPassword: string) { const isValid = await bcrypt.compare(inputPassword, savedPassword); if (!isValid) throw new CustomError(401, 'Unauthorized'); } - createToken(user: UserType, type?: string) { + createToken(user: UserPublicData | UserType, type?: string) { const JWTsecretKey = process.env.JWT_SECRET; if (!JWTsecretKey) throw new CustomError(500, 'JWT_SECRET is not defined'); const payload: JwtPayload = { userId: user.id }; diff --git a/tsconfig.json b/tsconfig.json index d598afe0..b38385a6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,7 @@ // Stricter Typechecking Options "noUncheckedIndexedAccess": true, - "exactOptionalPropertyTypes": true, + // "exactOptionalPropertyTypes": true, // Style Options // "noImplicitReturns": true, diff --git a/types/express.d.ts b/types/express.d.ts index 7617f952..7472869c 100644 --- a/types/express.d.ts +++ b/types/express.d.ts @@ -4,7 +4,8 @@ import { JwtPayload } from 'jsonwebtoken'; declare global { namespace Express { interface Request { - user?: (JwtPayload & { id?: number; }) | string; + user?: JwtPayload & { userId: number; }; + auth?: JwtPayload & { userId: number; }; } } } \ No newline at end of file From 91de85fddc98df6f48bdaa6aa86cc17bd972f5a3 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Mon, 19 Jan 2026 10:05:48 +0900 Subject: [PATCH 25/53] =?UTF-8?q?[=EC=8A=A4=ED=94=84=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EB=AF=B8=EC=85=988]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 30 ++ dist/Routers/articleRouter.d.ts | 3 + dist/Routers/articleRouter.d.ts.map | 1 + dist/Routers/articleRouter.js | 21 ++ dist/Routers/articleRouter.js.map | 1 + dist/Routers/commentRouter.d.ts | 3 + dist/Routers/commentRouter.d.ts.map | 1 + dist/Routers/commentRouter.js | 19 ++ dist/Routers/commentRouter.js.map | 1 + dist/Routers/productRouter.d.ts | 3 + dist/Routers/productRouter.d.ts.map | 1 + dist/Routers/productRouter.js | 21 ++ dist/Routers/productRouter.js.map | 1 + dist/Routers/routerManager.d.ts | 2 + dist/Routers/routerManager.d.ts.map | 1 + dist/Routers/routerManager.js | 19 ++ dist/Routers/routerManager.js.map | 1 + dist/Routers/uploadRouter.d.ts | 3 + dist/Routers/uploadRouter.d.ts.map | 1 + dist/Routers/uploadRouter.js | 15 + dist/Routers/uploadRouter.js.map | 1 + dist/Routers/userRouter.d.ts | 3 + dist/Routers/userRouter.d.ts.map | 1 + dist/Routers/userRouter.js | 20 ++ dist/Routers/userRouter.js.map | 1 + dist/controller/articleController.d.ts | 10 + dist/controller/articleController.d.ts.map | 1 + dist/controller/articleController.js | 118 ++++++++ dist/controller/articleController.js.map | 1 + dist/controller/commentController.d.ts | 8 + dist/controller/commentController.d.ts.map | 1 + dist/controller/commentController.js | 132 +++++++++ dist/controller/commentController.js.map | 1 + dist/controller/productController.d.ts | 10 + dist/controller/productController.d.ts.map | 1 + dist/controller/productController.js | 119 ++++++++ dist/controller/productController.js.map | 1 + dist/controller/uploadController.d.ts | 4 + dist/controller/uploadController.d.ts.map | 1 + dist/controller/uploadController.js | 12 + dist/controller/uploadController.js.map | 1 + dist/controller/userController.d.ts | 12 + dist/controller/userController.d.ts.map | 1 + dist/controller/userController.js | 73 +++++ dist/controller/userController.js.map | 1 + dist/libs/Handler/errorHandler.d.ts | 14 + dist/libs/Handler/errorHandler.d.ts.map | 1 + dist/libs/Handler/errorHandler.js | 91 ++++++ dist/libs/Handler/errorHandler.js.map | 1 + dist/libs/catchAsync.d.ts | 4 + dist/libs/catchAsync.d.ts.map | 1 + dist/libs/catchAsync.js | 14 + dist/libs/catchAsync.js.map | 1 + dist/libs/constants.d.ts | 11 + dist/libs/constants.d.ts.map | 1 + dist/libs/constants.js | 48 ++++ dist/libs/constants.js.map | 1 + dist/libs/corsSetUp.d.ts | 3 + dist/libs/corsSetUp.d.ts.map | 1 + dist/libs/corsSetUp.js | 15 + dist/libs/corsSetUp.js.map | 1 + dist/libs/interfaces.d.ts | 84 ++++++ dist/libs/interfaces.d.ts.map | 1 + dist/libs/interfaces.js | 3 + dist/libs/interfaces.js.map | 1 + dist/libs/removeTool.d.ts | 2 + dist/libs/removeTool.d.ts.map | 1 + dist/libs/removeTool.js | 15 + dist/libs/removeTool.js.map | 1 + dist/main.d.ts | 2 + dist/main.d.ts.map | 1 + dist/main.js | 26 ++ dist/main.js.map | 1 + dist/middlewares/auth.d.ts | 12 + dist/middlewares/auth.d.ts.map | 1 + dist/middlewares/auth.js | 92 ++++++ dist/middlewares/auth.js.map | 1 + dist/repositories/articleLikeRepository.d.ts | 25 ++ .../articleLikeRepository.d.ts.map | 1 + dist/repositories/articleLikeRepository.js | 49 ++++ .../repositories/articleLikeRepository.js.map | 1 + dist/repositories/articleRepository.d.ts | 59 ++++ dist/repositories/articleRepository.d.ts.map | 1 + dist/repositories/articleRepository.js | 80 ++++++ dist/repositories/articleRepository.js.map | 1 + dist/repositories/productLikeRepository.d.ts | 42 +++ .../productLikeRepository.d.ts.map | 1 + dist/repositories/productLikeRepository.js | 62 +++++ .../repositories/productLikeRepository.js.map | 1 + dist/repositories/productRepository.d.ts | 79 ++++++ dist/repositories/productRepository.d.ts.map | 1 + dist/repositories/productRepository.js | 90 ++++++ dist/repositories/productRepository.js.map | 1 + dist/repositories/userRepository.d.ts | 49 ++++ dist/repositories/userRepository.d.ts.map | 1 + dist/repositories/userRepository.js | 71 +++++ dist/repositories/userRepository.js.map | 1 + dist/services/articleService.d.ts | 25 ++ dist/services/articleService.d.ts.map | 1 + dist/services/articleService.js | 59 ++++ dist/services/articleService.js.map | 1 + dist/services/productService.d.ts | 29 ++ dist/services/productService.d.ts.map | 1 + dist/services/productService.js | 61 ++++ dist/services/productService.js.map | 1 + dist/services/userService.d.ts | 53 ++++ dist/services/userService.d.ts.map | 1 + dist/services/userService.js | 151 ++++++++++ dist/services/userService.js.map | 1 + dist/structs/structs.d.ts | 52 ++++ dist/structs/structs.d.ts.map | 1 + dist/structs/structs.js | 65 +++++ dist/structs/structs.js.map | 1 + dist/structs/userStructs.d.ts | 31 +++ dist/structs/userStructs.d.ts.map | 1 + dist/structs/userStructs.js | 59 ++++ dist/structs/userStructs.js.map | 1 + launch.json | 31 +++ package-lock.json | 261 +++++++++++++++++- package.json | 6 +- public/index.html | 37 +++ src/main.ts | 3 +- src/middlewares/auth.ts | 120 ++++---- 123 files changed, 2739 insertions(+), 64 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 dist/Routers/articleRouter.d.ts create mode 100644 dist/Routers/articleRouter.d.ts.map create mode 100644 dist/Routers/articleRouter.js create mode 100644 dist/Routers/articleRouter.js.map create mode 100644 dist/Routers/commentRouter.d.ts create mode 100644 dist/Routers/commentRouter.d.ts.map create mode 100644 dist/Routers/commentRouter.js create mode 100644 dist/Routers/commentRouter.js.map create mode 100644 dist/Routers/productRouter.d.ts create mode 100644 dist/Routers/productRouter.d.ts.map create mode 100644 dist/Routers/productRouter.js create mode 100644 dist/Routers/productRouter.js.map create mode 100644 dist/Routers/routerManager.d.ts create mode 100644 dist/Routers/routerManager.d.ts.map create mode 100644 dist/Routers/routerManager.js create mode 100644 dist/Routers/routerManager.js.map create mode 100644 dist/Routers/uploadRouter.d.ts create mode 100644 dist/Routers/uploadRouter.d.ts.map create mode 100644 dist/Routers/uploadRouter.js create mode 100644 dist/Routers/uploadRouter.js.map create mode 100644 dist/Routers/userRouter.d.ts create mode 100644 dist/Routers/userRouter.d.ts.map create mode 100644 dist/Routers/userRouter.js create mode 100644 dist/Routers/userRouter.js.map create mode 100644 dist/controller/articleController.d.ts create mode 100644 dist/controller/articleController.d.ts.map create mode 100644 dist/controller/articleController.js create mode 100644 dist/controller/articleController.js.map create mode 100644 dist/controller/commentController.d.ts create mode 100644 dist/controller/commentController.d.ts.map create mode 100644 dist/controller/commentController.js create mode 100644 dist/controller/commentController.js.map create mode 100644 dist/controller/productController.d.ts create mode 100644 dist/controller/productController.d.ts.map create mode 100644 dist/controller/productController.js create mode 100644 dist/controller/productController.js.map create mode 100644 dist/controller/uploadController.d.ts create mode 100644 dist/controller/uploadController.d.ts.map create mode 100644 dist/controller/uploadController.js create mode 100644 dist/controller/uploadController.js.map create mode 100644 dist/controller/userController.d.ts create mode 100644 dist/controller/userController.d.ts.map create mode 100644 dist/controller/userController.js create mode 100644 dist/controller/userController.js.map create mode 100644 dist/libs/Handler/errorHandler.d.ts create mode 100644 dist/libs/Handler/errorHandler.d.ts.map create mode 100644 dist/libs/Handler/errorHandler.js create mode 100644 dist/libs/Handler/errorHandler.js.map create mode 100644 dist/libs/catchAsync.d.ts create mode 100644 dist/libs/catchAsync.d.ts.map create mode 100644 dist/libs/catchAsync.js create mode 100644 dist/libs/catchAsync.js.map create mode 100644 dist/libs/constants.d.ts create mode 100644 dist/libs/constants.d.ts.map create mode 100644 dist/libs/constants.js create mode 100644 dist/libs/constants.js.map create mode 100644 dist/libs/corsSetUp.d.ts create mode 100644 dist/libs/corsSetUp.d.ts.map create mode 100644 dist/libs/corsSetUp.js create mode 100644 dist/libs/corsSetUp.js.map create mode 100644 dist/libs/interfaces.d.ts create mode 100644 dist/libs/interfaces.d.ts.map create mode 100644 dist/libs/interfaces.js create mode 100644 dist/libs/interfaces.js.map create mode 100644 dist/libs/removeTool.d.ts create mode 100644 dist/libs/removeTool.d.ts.map create mode 100644 dist/libs/removeTool.js create mode 100644 dist/libs/removeTool.js.map create mode 100644 dist/main.d.ts create mode 100644 dist/main.d.ts.map create mode 100644 dist/main.js create mode 100644 dist/main.js.map create mode 100644 dist/middlewares/auth.d.ts create mode 100644 dist/middlewares/auth.d.ts.map create mode 100644 dist/middlewares/auth.js create mode 100644 dist/middlewares/auth.js.map create mode 100644 dist/repositories/articleLikeRepository.d.ts create mode 100644 dist/repositories/articleLikeRepository.d.ts.map create mode 100644 dist/repositories/articleLikeRepository.js create mode 100644 dist/repositories/articleLikeRepository.js.map create mode 100644 dist/repositories/articleRepository.d.ts create mode 100644 dist/repositories/articleRepository.d.ts.map create mode 100644 dist/repositories/articleRepository.js create mode 100644 dist/repositories/articleRepository.js.map create mode 100644 dist/repositories/productLikeRepository.d.ts create mode 100644 dist/repositories/productLikeRepository.d.ts.map create mode 100644 dist/repositories/productLikeRepository.js create mode 100644 dist/repositories/productLikeRepository.js.map create mode 100644 dist/repositories/productRepository.d.ts create mode 100644 dist/repositories/productRepository.d.ts.map create mode 100644 dist/repositories/productRepository.js create mode 100644 dist/repositories/productRepository.js.map create mode 100644 dist/repositories/userRepository.d.ts create mode 100644 dist/repositories/userRepository.d.ts.map create mode 100644 dist/repositories/userRepository.js create mode 100644 dist/repositories/userRepository.js.map create mode 100644 dist/services/articleService.d.ts create mode 100644 dist/services/articleService.d.ts.map create mode 100644 dist/services/articleService.js create mode 100644 dist/services/articleService.js.map create mode 100644 dist/services/productService.d.ts create mode 100644 dist/services/productService.d.ts.map create mode 100644 dist/services/productService.js create mode 100644 dist/services/productService.js.map create mode 100644 dist/services/userService.d.ts create mode 100644 dist/services/userService.d.ts.map create mode 100644 dist/services/userService.js create mode 100644 dist/services/userService.js.map create mode 100644 dist/structs/structs.d.ts create mode 100644 dist/structs/structs.d.ts.map create mode 100644 dist/structs/structs.js create mode 100644 dist/structs/structs.js.map create mode 100644 dist/structs/userStructs.d.ts create mode 100644 dist/structs/userStructs.d.ts.map create mode 100644 dist/structs/userStructs.js create mode 100644 dist/structs/userStructs.js.map create mode 100644 launch.json create mode 100644 public/index.html diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..b759bd50 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. + // 기존 특성에 대한 설명을 보려면 가리킵니다. + // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug TypeScript", + "runtimeArgs": ["-r", "ts-node/register"], + "args": ["${workspaceFolder}/src/main.ts"] + }, + { + "command": "npm start", + "name": "Run npm start", + "request": "launch", + "type": "node-terminal" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/dist/main.js" + } + ] +} \ No newline at end of file diff --git a/dist/Routers/articleRouter.d.ts b/dist/Routers/articleRouter.d.ts new file mode 100644 index 00000000..8878a904 --- /dev/null +++ b/dist/Routers/articleRouter.d.ts @@ -0,0 +1,3 @@ +declare const articleRouter: import("express-serve-static-core").Router; +export default articleRouter; +//# sourceMappingURL=articleRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.d.ts.map b/dist/Routers/articleRouter.d.ts.map new file mode 100644 index 00000000..8986ff63 --- /dev/null +++ b/dist/Routers/articleRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/articleRouter.js b/dist/Routers/articleRouter.js new file mode 100644 index 00000000..ca3ed39c --- /dev/null +++ b/dist/Routers/articleRouter.js @@ -0,0 +1,21 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +const catchAsync_1 = require("../libs/catchAsync"); +const auth_1 = __importDefault(require("../middlewares/auth")); +const articleController_1 = __importDefault(require("../controller/articleController")); +const articleRouter = constants_1.EXPRESS.Router(); +const articleController = new articleController_1.default(); +articleRouter.route('/') + .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.getArticles)) + .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.postArticle)); +articleRouter.route('/:id') + .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.getArticleById)) + .patch(auth_1.default.verifyAccessToken, auth_1.default.verifyArticleAuth, (0, catchAsync_1.catchAsync)(articleController.patchArticleById)) + .delete(auth_1.default.verifyAccessToken, auth_1.default.verifyArticleAuth, (0, catchAsync_1.catchAsync)(articleController.deleteArticleById)); +articleRouter.post('/:id/like', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.likeArticle)); +exports.default = articleRouter; +//# sourceMappingURL=articleRouter.js.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.js.map b/dist/Routers/articleRouter.js.map new file mode 100644 index 00000000..ab48ba28 --- /dev/null +++ b/dist/Routers/articleRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRouter.js","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,+DAAuC;AACvC,wFAAgE;AAEhE,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,2BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;KAC1E,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KACrG,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAE7G,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts b/dist/Routers/commentRouter.d.ts new file mode 100644 index 00000000..06b1655f --- /dev/null +++ b/dist/Routers/commentRouter.d.ts @@ -0,0 +1,3 @@ +declare const commentRouter: import("express-serve-static-core").Router; +export default commentRouter; +//# sourceMappingURL=commentRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts.map b/dist/Routers/commentRouter.d.ts.map new file mode 100644 index 00000000..415ffed9 --- /dev/null +++ b/dist/Routers/commentRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAYvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.js b/dist/Routers/commentRouter.js new file mode 100644 index 00000000..bd24cc3b --- /dev/null +++ b/dist/Routers/commentRouter.js @@ -0,0 +1,19 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +const catchAsync_1 = require("../libs/catchAsync"); +const auth_1 = __importDefault(require("../middlewares/auth")); +const commentController_1 = require("../controller/commentController"); +const commentRouter = constants_1.EXPRESS.Router(); +commentRouter.route('/') + .get((0, catchAsync_1.catchAsync)(commentController_1.GetComment)) + .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(commentController_1.PostComment)); +commentRouter.route('/:id') + .get((0, catchAsync_1.catchAsync)(commentController_1.GetCommentById)) + .patch(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsyncAll)(auth_1.default.verifyProduectAuth, commentController_1.PatchCommentById)) + .delete(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsyncAll)(auth_1.default.verifyProduectAuth, commentController_1.DeleteCommentById)); +exports.default = commentRouter; +//# sourceMappingURL=commentRouter.js.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.js.map b/dist/Routers/commentRouter.js.map new file mode 100644 index 00000000..4d0d08f3 --- /dev/null +++ b/dist/Routers/commentRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRouter.js","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAA+D;AAC/D,+DAAuC;AACvC,uEAMyC;AAEzC,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAEvC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,IAAA,uBAAU,EAAC,8BAAU,CAAC,CAAC;KAC3B,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,+BAAW,CAAC,CAAC,CAAC;AAE3D,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,IAAA,uBAAU,EAAC,kCAAc,CAAC,CAAC;KAC/B,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAa,EAAC,cAAI,CAAC,kBAAkB,EAAE,oCAAgB,CAAC,CAAC;KACvF,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAa,EAAC,cAAI,CAAC,kBAAkB,EAAE,qCAAiB,CAAC,CAAC,CAAC;AAG/F,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts b/dist/Routers/productRouter.d.ts new file mode 100644 index 00000000..c3eadb5d --- /dev/null +++ b/dist/Routers/productRouter.d.ts @@ -0,0 +1,3 @@ +declare const productRouter: import("express-serve-static-core").Router; +export default productRouter; +//# sourceMappingURL=productRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts.map b/dist/Routers/productRouter.d.ts.map new file mode 100644 index 00000000..0c9ad952 --- /dev/null +++ b/dist/Routers/productRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.js b/dist/Routers/productRouter.js new file mode 100644 index 00000000..3d5681ad --- /dev/null +++ b/dist/Routers/productRouter.js @@ -0,0 +1,21 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +const catchAsync_1 = require("../libs/catchAsync"); +const auth_1 = __importDefault(require("../middlewares/auth")); +const productController_1 = __importDefault(require("../controller/productController")); +const productRouter = constants_1.EXPRESS.Router(); +const productController = new productController_1.default(); +productRouter.route('/') + .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(productController.GetProduct)) + .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.PostProduct)); +productRouter.route('/:id') + .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(productController.GetProductById)) + .patch(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.PatchProductById)) + .delete(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.DeleteProductById)); +productRouter.post('/:id/like', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.likeProduct)); +exports.default = productRouter; +//# sourceMappingURL=productRouter.js.map \ No newline at end of file diff --git a/dist/Routers/productRouter.js.map b/dist/Routers/productRouter.js.map new file mode 100644 index 00000000..645f04d1 --- /dev/null +++ b/dist/Routers/productRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productRouter.js","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,+DAAuC;AACvC,wFAAgE;AAEhE,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,2BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;KACzE,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KAC7E,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErF,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts b/dist/Routers/routerManager.d.ts new file mode 100644 index 00000000..c5d39a67 --- /dev/null +++ b/dist/Routers/routerManager.d.ts @@ -0,0 +1,2 @@ +export declare const RouterManager: import("express-serve-static-core").Router; +//# sourceMappingURL=routerManager.d.ts.map \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts.map b/dist/Routers/routerManager.d.ts.map new file mode 100644 index 00000000..92a9a935 --- /dev/null +++ b/dist/Routers/routerManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"routerManager.d.ts","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,4CAAmB,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.js b/dist/Routers/routerManager.js new file mode 100644 index 00000000..7d4eb65f --- /dev/null +++ b/dist/Routers/routerManager.js @@ -0,0 +1,19 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RouterManager = void 0; +const constants_1 = require("../libs/constants"); +const productRouter_1 = __importDefault(require("./productRouter")); +const articleRouter_1 = __importDefault(require("./articleRouter")); +const commentRouter_1 = __importDefault(require("./commentRouter")); +const uploadRouter_1 = __importDefault(require("./uploadRouter")); +const userRouter_1 = __importDefault(require("./userRouter")); +exports.RouterManager = constants_1.EXPRESS.Router(); +exports.RouterManager.use('/products', productRouter_1.default); +exports.RouterManager.use('/articles', articleRouter_1.default); +exports.RouterManager.use('/comments', commentRouter_1.default); +exports.RouterManager.use('/files', uploadRouter_1.default); +exports.RouterManager.use('/user', userRouter_1.default); +//# sourceMappingURL=routerManager.js.map \ No newline at end of file diff --git a/dist/Routers/routerManager.js.map b/dist/Routers/routerManager.js.map new file mode 100644 index 00000000..e21219bc --- /dev/null +++ b/dist/Routers/routerManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,iDAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,kEAA0C;AAC1C,8DAAsC;AAGzB,QAAA,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts b/dist/Routers/uploadRouter.d.ts new file mode 100644 index 00000000..a036c514 --- /dev/null +++ b/dist/Routers/uploadRouter.d.ts @@ -0,0 +1,3 @@ +declare const uploadRouter: import("express-serve-static-core").Router; +export default uploadRouter; +//# sourceMappingURL=uploadRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts.map b/dist/Routers/uploadRouter.d.ts.map new file mode 100644 index 00000000..732e2bf6 --- /dev/null +++ b/dist/Routers/uploadRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,YAAY,4CAAmB,CAAC;AAStC,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js b/dist/Routers/uploadRouter.js new file mode 100644 index 00000000..1788fffb --- /dev/null +++ b/dist/Routers/uploadRouter.js @@ -0,0 +1,15 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +const catchAsync_1 = require("../libs/catchAsync"); +const multer_1 = __importDefault(require("multer")); +const uploadController_1 = require("../controller/uploadController"); +const uploadRouter = constants_1.EXPRESS.Router(); +const upload = (0, multer_1.default)({ dest: 'upload/' }); +uploadRouter.post('/', upload.single('attachment'), (0, catchAsync_1.catchAsync)(uploadController_1.UploadSingleImage)); +uploadRouter.use('/', constants_1.EXPRESS.static('upload')); +exports.default = uploadRouter; +//# sourceMappingURL=uploadRouter.js.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js.map b/dist/Routers/uploadRouter.js.map new file mode 100644 index 00000000..d94fc42d --- /dev/null +++ b/dist/Routers/uploadRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadRouter.js","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,oDAA4B;AAC5B,qEAEwC;AAExC,MAAM,YAAY,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAEtC,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;AAE3C,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAC9C,IAAA,uBAAU,EAAC,oCAAiB,CAAC,CAAC,CAAC;AAEnC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEhD,kBAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts b/dist/Routers/userRouter.d.ts new file mode 100644 index 00000000..23df20cc --- /dev/null +++ b/dist/Routers/userRouter.d.ts @@ -0,0 +1,3 @@ +declare const userRouter: import("express-serve-static-core").Router; +export default userRouter; +//# sourceMappingURL=userRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts.map b/dist/Routers/userRouter.d.ts.map new file mode 100644 index 00000000..6564ec89 --- /dev/null +++ b/dist/Routers/userRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,UAAU,4CAAmB,CAAC;AAgBpC,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.js b/dist/Routers/userRouter.js new file mode 100644 index 00000000..980a7423 --- /dev/null +++ b/dist/Routers/userRouter.js @@ -0,0 +1,20 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +const catchAsync_1 = require("../libs/catchAsync"); +const userController_1 = __importDefault(require("../controller/userController")); +const auth_1 = __importDefault(require("../middlewares/auth")); +const userRouter = constants_1.EXPRESS.Router(); +const userController = new userController_1.default(); +userRouter.post('/', (0, catchAsync_1.catchAsync)(userController.register)); +userRouter.post('/login', (0, catchAsync_1.catchAsync)(userController.login)); +userRouter.post('/token/refresh', auth_1.default.verifyRefreshToken, (0, catchAsync_1.catchAsync)(userController.refresh)); +userRouter.get('/me', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.GetMe)); +userRouter.patch('/me', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.updateMe)); +userRouter.patch('/me/password', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.updateMyPassword)); +userRouter.get('/me/products', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.getMyProducts)); +exports.default = userRouter; +//# sourceMappingURL=userRouter.js.map \ No newline at end of file diff --git a/dist/Routers/userRouter.js.map b/dist/Routers/userRouter.js.map new file mode 100644 index 00000000..9b027a1a --- /dev/null +++ b/dist/Routers/userRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,kFAAiE;AACjE,+DAAuC;AAEvC,MAAM,UAAU,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,wBAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1D,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,cAAI,CAAC,kBAAkB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/controller/articleController.d.ts b/dist/controller/articleController.d.ts new file mode 100644 index 00000000..dbe15c54 --- /dev/null +++ b/dist/controller/articleController.d.ts @@ -0,0 +1,10 @@ +import { ExpressHandler } from '../libs/constants'; +export default class ArticleController { + getArticles: ExpressHandler; + getArticleById: ExpressHandler; + postArticle: ExpressHandler; + patchArticleById: ExpressHandler; + deleteArticleById: ExpressHandler; + likeArticle: ExpressHandler; +} +//# sourceMappingURL=articleController.d.ts.map \ No newline at end of file diff --git a/dist/controller/articleController.d.ts.map b/dist/controller/articleController.d.ts.map new file mode 100644 index 00000000..75097cd4 --- /dev/null +++ b/dist/controller/articleController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,WAAW,EAAE,cAAc,CAqCzB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAKzB;IAEF,gBAAgB,EAAE,cAAc,CAQ9B;IAEF,iBAAiB,EAAE,cAAc,CAM/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/articleController.js b/dist/controller/articleController.js new file mode 100644 index 00000000..1bd6ce98 --- /dev/null +++ b/dist/controller/articleController.js @@ -0,0 +1,118 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const articleService_1 = require("../services/articleService"); +const superstruct_1 = require("superstruct"); +const structs_1 = require("../structs/structs"); +const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +class ArticleController { + constructor() { + this.getArticles = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + const parsedOffset = parseInt(offset, 10) || 0; + const parsedLimit = parseInt(limit, 10) || 0; + const findOptions = { + where: { + title: { + contains: title, + }, + content: { + contains: content, + }, + }, + orderBy, + skip: parsedOffset, + }; + if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { + findOptions.take = parsedLimit; + } + const userId = req.user ? req.user.userId : null; + if (!userId) + return new errorHandler_1.CustomError(404, "UserId Not Found"); + const articles = yield articleService_1.articleService.getArticles(findOptions, userId); + res.send(articles); + }); + this.getArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + const userId = req.user ? req.user.userId : null; + if (!userId) + return new errorHandler_1.CustomError(404, "userId Not Found"); + const article = yield articleService_1.articleService.getArticleById(_id, userId); + res.send(article); + }); + this.postArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_1.CreateArticle); + const userFields = __rest(req.body, []); + const article = yield articleRepository_1.default.create(userFields); + res.status(201).send(article); + }); + this.patchArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + (0, superstruct_1.assert)(req.body, structs_1.PatchArticle); + const userFields = __rest(req.body, []); + const article = yield articleRepository_1.default.update(_id, userFields); + res.send(article); + }); + this.deleteArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + const article = yield articleRepository_1.default.ondelete(_id); + res.send(article); + }); + this.likeArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { + if (!req.user) + return new errorHandler_1.CustomError(404, "user Not Found"); + const userId = req.user.userId; + const articleId = req.params.id; + if (!articleId) + return new errorHandler_1.CustomError(404, "articleId Not Found"); + const _articleId = parseInt(articleId); + const result = yield articleService_1.articleService.likeArticle(userId, _articleId); + return res.status(200).json(result); + }); + } +} +exports.default = ArticleController; +//# sourceMappingURL=articleController.js.map \ No newline at end of file diff --git a/dist/controller/articleController.js.map b/dist/controller/articleController.js.map new file mode 100644 index 00000000..18705063 --- /dev/null +++ b/dist/controller/articleController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/commentController.d.ts b/dist/controller/commentController.d.ts new file mode 100644 index 00000000..4865520f --- /dev/null +++ b/dist/controller/commentController.d.ts @@ -0,0 +1,8 @@ +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; +export declare function GetComment(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function GetCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function PostComment(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function PatchCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function DeleteCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +//# sourceMappingURL=commentController.d.ts.map \ No newline at end of file diff --git a/dist/controller/commentController.d.ts.map b/dist/controller/commentController.d.ts.map new file mode 100644 index 00000000..5e354b93 --- /dev/null +++ b/dist/controller/commentController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,wCA2CzE;AAGD,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,oCAU7E;AAGD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,wCAoB1E;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,sDAmB/E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,oCAUhF"} \ No newline at end of file diff --git a/dist/controller/commentController.js b/dist/controller/commentController.js new file mode 100644 index 00000000..ef9e30ee --- /dev/null +++ b/dist/controller/commentController.js @@ -0,0 +1,132 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetComment = GetComment; +exports.GetCommentById = GetCommentById; +exports.PostComment = PostComment; +exports.PatchCommentById = PatchCommentById; +exports.DeleteCommentById = DeleteCommentById; +const constants_1 = require("../libs/constants"); +const superstruct_1 = require("superstruct"); +const structs_1 = require("../structs/structs"); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +function GetComment(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { take = '10', cursor, productId, articleId } = req.query; + const parsedTake = parseInt(take, 10) || 0; + if (isNaN(parsedTake) || parsedTake <= 0) { + return res.status(400).send({ error: 'Invalid "take" parameter.' }); + } + const whereClause = {}; + if (productId) { + whereClause.productId = Number(productId); // 상품 ID로 필터링 + } + else if (articleId) { + whereClause.articleId = Number(articleId); // 게시글 ID로 필터링 + } + const findOptions = { + take: parsedTake, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + if (cursor) { + const parsedCursor = parseInt(cursor, 10); + findOptions.skip = 1; + findOptions.cursor = { + id: parsedCursor, + }; + } + const comments = yield constants_1.prismaClient.comment.findMany(findOptions); // + let nextCursor = null; + if (comments.length > 0 && comments.length === parsedTake) { + nextCursor = comments[comments.length - 1].id; + } + res.status(200).json({ + comments, + nextCursor, + }); + }); +} +function GetCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const paesedId = parseInt(id, 10) || null; + if (!paesedId) + return new errorHandler_1.CustomError(404, "id Not Found"); + const Comment = yield constants_1.prismaClient.comment.findUniqueOrThrow({ + where: { + id: paesedId + }, + }); + res.send(Comment); + }); +} +function PostComment(req, res) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_1.CreateComment); + const { content, productId, articleId } = req.body; + if ((productId && articleId) || (!productId && !articleId)) { + return res.status(400).json({ + error: 'Comment must belong to EITHER a Product OR an Article.', + }); + } + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + const comment = yield constants_1.prismaClient.comment.create({ + data: { + content: content, + productId: productId, + articleId: articleId, + }, + }); + res.status(201).send(comment); + }); +} +function PatchCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_1.PatchComment); + const { content } = req.body; + const { id } = req.params; + const paesedId = parseInt(id, 10) || null; + if (!paesedId) + return new errorHandler_1.CustomError(404, "id Not Found"); + if (content !== undefined && content.trim() === '') { + return res.status(400).json({ error: 'Content cannot be empty.' }); + } + const comment = yield constants_1.prismaClient.comment.update({ + where: { + id: paesedId + }, + data: { + content: content, + }, + }); + res.send(comment); + }); +} +function DeleteCommentById(req, res) { + return __awaiter(this, void 0, void 0, function* () { + const { id } = req.params; + const paesedId = parseInt(id, 10) || null; + if (!paesedId) + return new errorHandler_1.CustomError(404, "id Not Found"); + const Comment = yield constants_1.prismaClient.comment.delete({ + where: { + id: paesedId + }, + }); + res.send(Comment); + }); +} +//# sourceMappingURL=commentController.js.map \ No newline at end of file diff --git a/dist/controller/commentController.js.map b/dist/controller/commentController.js.map new file mode 100644 index 00000000..6a765c54 --- /dev/null +++ b/dist/controller/commentController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/productController.d.ts b/dist/controller/productController.d.ts new file mode 100644 index 00000000..e907b86d --- /dev/null +++ b/dist/controller/productController.d.ts @@ -0,0 +1,10 @@ +import { ExpressHandler } from '../libs/constants'; +export default class ProductController { + GetProduct: ExpressHandler; + GetProductById: ExpressHandler; + PostProduct: ExpressHandler; + PatchProductById: ExpressHandler; + DeleteProductById: ExpressHandler; + likeProduct: ExpressHandler; +} +//# sourceMappingURL=productController.d.ts.map \ No newline at end of file diff --git a/dist/controller/productController.d.ts.map b/dist/controller/productController.d.ts.map new file mode 100644 index 00000000..573bd7c2 --- /dev/null +++ b/dist/controller/productController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,UAAU,EAAE,cAAc,CAqCxB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAKzB;IAEF,gBAAgB,EAAE,cAAc,CAQ9B;IAEF,iBAAiB,EAAE,cAAc,CAM/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/productController.js b/dist/controller/productController.js new file mode 100644 index 00000000..8af6264f --- /dev/null +++ b/dist/controller/productController.js @@ -0,0 +1,119 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const productService_1 = require("../services/productService"); +const superstruct_1 = require("superstruct"); +const structs_1 = require("../structs/structs"); +const productRepository_1 = __importDefault(require("../repositories/productRepository")); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +const removeTool_1 = require("./../libs/removeTool"); +class ProductController { + constructor() { + this.GetProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { offset = '0', limit = '0', order = 'newest', name = "", description = "" } = req.query; + let orderBy; + switch (order) { + case 'oldest': + orderBy = { createdAt: 'asc' }; + break; + case 'newest': + orderBy = { createdAt: 'desc' }; + break; + default: + orderBy = { createdAt: 'desc' }; + } + const parsedOffset = parseInt(offset, 10) || 0; + const parsedLimit = parseInt(limit, 10) || 0; + const findOptions = { + where: { + name: { + contains: name, + }, + description: { + contains: description, + }, + }, + orderBy, + skip: parsedOffset, + }; + if (parsedLimit > 0) { + findOptions.take = parsedLimit; + } + const userId = req.user ? req.user.userId : null; + if (!userId) + return new errorHandler_1.CustomError(404, "UserId Not Found"); + const products = yield productService_1.productService.getProducts(findOptions, userId); + res.send(products); + }); + this.GetProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + const userId = req.user ? req.user.userId : null; + if (!userId) + return new errorHandler_1.CustomError(404, "userId Not Found"); + const product = yield productService_1.productService.getProductById(_id, userId); + res.send(product); + }); + this.PostProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(req.body, structs_1.CreateProduct); + const userFields = __rest(req.body, []); + const product = yield productRepository_1.default.create(userFields); + res.status(201).send(product); + }); + this.PatchProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + (0, superstruct_1.assert)(req.body, structs_1.PatchProduct); + const userFields = (0, removeTool_1.removeUndefined)(req.body); + const Product = yield productRepository_1.default.update(_id, userFields); + res.send(Product); + }); + this.DeleteProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; + if (!id) + return new errorHandler_1.CustomError(404, "id Not Found"); + const _id = parseInt(id); + const Product = yield productRepository_1.default.ondelete(_id); + res.send(Product); + }); + this.likeProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { + if (!req.user) + return new errorHandler_1.CustomError(404, "user Not Found"); + const userId = req.user.userId; + const productId = req.params.id; + if (!productId) + return new errorHandler_1.CustomError(404, "productId Not Found"); + const _productId = parseInt(productId); + const result = yield productService_1.productService.likeProduct(userId, _productId); + return res.status(200).json(result); + }); + } +} +exports.default = ProductController; +//# sourceMappingURL=productController.js.map \ No newline at end of file diff --git a/dist/controller/productController.js.map b/dist/controller/productController.js.map new file mode 100644 index 00000000..66c6c2c5 --- /dev/null +++ b/dist/controller/productController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts b/dist/controller/uploadController.d.ts new file mode 100644 index 00000000..2f0cef25 --- /dev/null +++ b/dist/controller/uploadController.d.ts @@ -0,0 +1,4 @@ +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; +export declare function UploadSingleImage(req: ExpressRequest, res: ExpressResponse): CustomError | undefined; +//# sourceMappingURL=uploadController.d.ts.map \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts.map b/dist/controller/uploadController.d.ts.map new file mode 100644 index 00000000..cada72c6 --- /dev/null +++ b/dist/controller/uploadController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,2BAM1E"} \ No newline at end of file diff --git a/dist/controller/uploadController.js b/dist/controller/uploadController.js new file mode 100644 index 00000000..41ac5899 --- /dev/null +++ b/dist/controller/uploadController.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UploadSingleImage = UploadSingleImage; +const errorHandler_1 = require("../libs/Handler/errorHandler"); +function UploadSingleImage(req, res) { + if (!req.file) + return new errorHandler_1.CustomError(404, "file not found"); + const { filename } = req.file; + const path = `files/${filename}`; + res.json({ path }); +} +//# sourceMappingURL=uploadController.js.map \ No newline at end of file diff --git a/dist/controller/uploadController.js.map b/dist/controller/uploadController.js.map new file mode 100644 index 00000000..a68037dd --- /dev/null +++ b/dist/controller/uploadController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAGA,8CAMC;AAPD,+DAA2D;AAC3D,SAAgB,iBAAiB,CAAC,GAAmB,EAAE,GAAoB;IACvE,IAAI,CAAC,GAAG,CAAC,IAAI;QACT,OAAO,IAAI,0BAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAClD,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/dist/controller/userController.d.ts b/dist/controller/userController.d.ts new file mode 100644 index 00000000..0f491de0 --- /dev/null +++ b/dist/controller/userController.d.ts @@ -0,0 +1,12 @@ +import { ExpressHandler } from '../libs/constants'; +export default class UserServiceController { + register: ExpressHandler; + login: ExpressHandler; + refresh: ExpressHandler; + GetMe: ExpressHandler; + updateMe: ExpressHandler; + updateMyPassword: ExpressHandler; + getMyProducts: ExpressHandler; + getMyLikedProducts: ExpressHandler; +} +//# sourceMappingURL=userController.d.ts.map \ No newline at end of file diff --git a/dist/controller/userController.d.ts.map b/dist/controller/userController.d.ts.map new file mode 100644 index 00000000..a183bca4 --- /dev/null +++ b/dist/controller/userController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userController.d.ts","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACtC,QAAQ,EAAE,cAAc,CAGtB;IAEF,KAAK,EAAE,cAAc,CAYnB;IACF,OAAO,EAAE,cAAc,CAYrB;IACF,KAAK,EAAE,cAAc,CAInB;IACF,QAAQ,EAAE,cAAc,CAItB;IAEF,gBAAgB,EAAE,cAAc,CAI9B;IAEF,aAAa,EAAE,cAAc,CAI3B;IAEF,kBAAkB,EAAE,cAAc,CAIhC;CACL"} \ No newline at end of file diff --git a/dist/controller/userController.js b/dist/controller/userController.js new file mode 100644 index 00000000..b1992cbb --- /dev/null +++ b/dist/controller/userController.js @@ -0,0 +1,73 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const userService_1 = require("../services/userService"); +class UserServiceController { + constructor() { + this.register = (req, res) => __awaiter(this, void 0, void 0, function* () { + const user = yield userService_1.userService.createUser(req.body); + return res.status(201).json(user); + }); + this.login = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { email, password } = req.body; + const user = yield userService_1.userService.getUser(email, password); + const accessToken = userService_1.userService.createToken(user); + const refreshToken = userService_1.userService.createToken(user, 'refresh'); + yield userService_1.userService.updateUser(user.id, { refreshToken }); + res.cookie('refreshToken', refreshToken, { + httpOnly: true, + sameSite: 'none', + secure: true + }); + return res.status(200).json({ accessToken }); + }); + this.refresh = (req, res) => __awaiter(this, void 0, void 0, function* () { + const { refreshToken } = req.cookies; + const { userId } = req.auth; + const { accessToken, newRefreshToken } = yield userService_1.userService.refreshToken(userId, refreshToken); // 변경 + yield userService_1.userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 + res.cookie('refreshToken', newRefreshToken, { + path: '/token/refresh', + httpOnly: true, + sameSite: 'none', + secure: true, + }); + return res.json({ accessToken }); + }); + this.GetMe = (req, res) => __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const user = yield userService_1.userService.getUserById(userId); + return res.status(200).json(user); + }); + this.updateMe = (req, res) => __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const updatedUser = yield userService_1.userService.updateProfile(userId, req.body); + return res.status(200).json(updatedUser); + }); + this.updateMyPassword = (req, res) => __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const updatedUser = yield userService_1.userService.updatePassword(userId, req.body); + return res.status(200).json(updatedUser); + }); + this.getMyProducts = (req, res) => __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const products = yield userService_1.userService.getProductsByUserId(userId); + return res.status(200).json(products); + }); + this.getMyLikedProducts = (req, res) => __awaiter(this, void 0, void 0, function* () { + const userId = req.user.userId; + const products = yield userService_1.userService.getLikedProductsByUserId(userId); + return res.status(200).json(products); + }); + } +} +exports.default = UserServiceController; +//# sourceMappingURL=userController.js.map \ No newline at end of file diff --git a/dist/controller/userController.js.map b/dist/controller/userController.js.map new file mode 100644 index 00000000..03cdd328 --- /dev/null +++ b/dist/controller/userController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userController.js","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts b/dist/libs/Handler/errorHandler.d.ts new file mode 100644 index 00000000..c1e8a83b --- /dev/null +++ b/dist/libs/Handler/errorHandler.d.ts @@ -0,0 +1,14 @@ +import { ExpressRequest, ExpressResponse, ExpressNextFunction } from './../constants'; +declare const errorHandler: (err: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => ExpressResponse; +export default errorHandler; +/** + * 모든 커스텀 에러의 기본이 되는 클래스 + * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. + */ +export declare class CustomError extends Error { + statusCode: number; + data: Record; + constructor(statusCode?: number, message?: string, data?: {}); +} +export declare const globalErrorHandler: (err: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void; +//# sourceMappingURL=errorHandler.d.ts.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts.map b/dist/libs/Handler/errorHandler.d.ts.map new file mode 100644 index 00000000..fca1f698 --- /dev/null +++ b/dist/libs/Handler/errorHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE9F,QAAA,MAAM,YAAY,GAAI,KAAK,GAAG,EACxB,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,oBAwDzE,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAE3B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAExB,UAAU,SAAM,EAAE,OAAO,SAAK,EAAE,IAAI,KAAK;CASxD;AAED,eAAO,MAAM,kBAAkB,GAAI,KAAK,GAAG,EACrC,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,SAczE,CAAC"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js b/dist/libs/Handler/errorHandler.js new file mode 100644 index 00000000..4c0fdca8 --- /dev/null +++ b/dist/libs/Handler/errorHandler.js @@ -0,0 +1,91 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.globalErrorHandler = exports.CustomError = void 0; +const multer_1 = __importDefault(require("multer")); +const constants_1 = require("./../constants"); +const errorHandler = (err, req, res, next) => { + // 1. Prisma 초기화 에러 (DB 연결 실패 등) + if (err instanceof constants_1.Prisma.PrismaClientInitializationError) { + return res.status(500).json({ + success: false, + message: '데이터베이스 연결에 실패했습니다. DATABASE_URL을 확인해주세요.' + }); + } + // 2. Prisma 요청 에러 (중복 키, 데이터 없음 등) + if (err instanceof constants_1.Prisma.PrismaClientKnownRequestError) { + if (err.code === 'P2002') { + return res.status(409).json({ + success: false, + message: '중복된 데이터가 존재합니다.' + }); + } + if (err.code === 'P2025') { + return res.status(404).json({ + success: false, + message: '요청한 데이터를 찾을 수 없습니다.' + }); + } + } + // 3. Multer(파일 업로드) 에러 + if (err instanceof multer_1.default.MulterError) { + if (err.code === 'LIMIT_FILE_SIZE') { + return res.status(400).json({ + success: false, + message: '파일 크기가 너무 큽니다.', + }); + } + if (err.code === 'LIMIT_FILE_COUNT') { + return res.status(400).json({ + success: false, + message: '파일 개수가 너무 많습니다.', + }); + } + } + // 4. CustomError 및 기타 에러 처리 + // any 타입이므로 안전하게 접근하기 위해 || 연산자 활용 + const statusCode = err.statusCode || 500; + const message = err.message || '서버 오류가 발생했습니다.'; + // path가 없는 에러 객체도 많으므로 안전하게 처리 + const path = err.data || null; + console.error(`[Error] ${statusCode} - ${message}`, err.stack); + return res.status(statusCode).json({ + success: false, + message, + path + }); +}; +exports.default = errorHandler; +/** + * 모든 커스텀 에러의 기본이 되는 클래스 + * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. + */ +class CustomError extends Error { + constructor(statusCode = 500, message = '', data = {}) { + super(message); + this.statusCode = statusCode; + // Error stack 추적을 위해 클래스 이름 설정 + this.name = this.constructor.name; + this.data = data; + // 생성자 함수를 호출 스택에서 제외 + Error.captureStackTrace(this, this.constructor); + } +} +exports.CustomError = CustomError; +const globalErrorHandler = (err, req, res, next) => { + // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. + const statusCode = err.statusCode || 500; + const message = err.message || 'Internal Server Error'; + // 에러를 콘솔에 기록 + console.error(`[${err.name} - ${statusCode}] ${err.message}`, err.stack); + res.status(statusCode).json({ + status: 'error', + message: message, + // 개발 환경에서만 스택 트레이스를 포함할 수 있습니다. + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, + }); +}; +exports.globalErrorHandler = globalErrorHandler; +//# sourceMappingURL=errorHandler.js.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js.map b/dist/libs/Handler/errorHandler.js.map new file mode 100644 index 00000000..c97e8752 --- /dev/null +++ b/dist/libs/Handler/errorHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts b/dist/libs/catchAsync.d.ts new file mode 100644 index 00000000..6dea8364 --- /dev/null +++ b/dist/libs/catchAsync.d.ts @@ -0,0 +1,4 @@ +import { ExpressHandler } from './constants'; +export declare const catchAsync: (fn: ExpressHandler) => ExpressHandler; +export declare const catchAsyncAll: (...fns: ExpressHandler[]) => ExpressHandler[]; +//# sourceMappingURL=catchAsync.d.ts.map \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts.map b/dist/libs/catchAsync.d.ts.map new file mode 100644 index 00000000..3a40ccf6 --- /dev/null +++ b/dist/libs/catchAsync.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"catchAsync.d.ts","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwD,MAAM,aAAa,CAAC;AAEnG,eAAO,MAAM,UAAU,GAAI,IAAI,cAAc,KAAG,cAI/C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,KAAK,cAAc,EAAE,KAAG,cAAc,EAEtE,CAAC"} \ No newline at end of file diff --git a/dist/libs/catchAsync.js b/dist/libs/catchAsync.js new file mode 100644 index 00000000..a7d3b311 --- /dev/null +++ b/dist/libs/catchAsync.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.catchAsyncAll = exports.catchAsync = void 0; +const catchAsync = (fn) => { + return (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); + }; +}; +exports.catchAsync = catchAsync; +const catchAsyncAll = (...fns) => { + return fns.map(exports.catchAsync); +}; +exports.catchAsyncAll = catchAsyncAll; +//# sourceMappingURL=catchAsync.js.map \ No newline at end of file diff --git a/dist/libs/catchAsync.js.map b/dist/libs/catchAsync.js.map new file mode 100644 index 00000000..19d36a3f --- /dev/null +++ b/dist/libs/catchAsync.js.map @@ -0,0 +1 @@ +{"version":3,"file":"catchAsync.js","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":";;;AAEO,MAAM,UAAU,GAAG,CAAC,EAAkB,EAAkB,EAAE;IAC7D,OAAO,CAAC,GAAmB,EAAE,GAAoB,EAAE,IAAyB,EAAE,EAAE;QAC5E,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC;AACN,CAAC,CAAC;AAJW,QAAA,UAAU,cAIrB;AAEK,MAAM,aAAa,GAAG,CAAC,GAAG,GAAqB,EAAoB,EAAE;IACxE,OAAO,GAAG,CAAC,GAAG,CAAC,kBAAU,CAAC,CAAC;AAC/B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB"} \ No newline at end of file diff --git a/dist/libs/constants.d.ts b/dist/libs/constants.d.ts new file mode 100644 index 00000000..71e53a24 --- /dev/null +++ b/dist/libs/constants.d.ts @@ -0,0 +1,11 @@ +import { PrismaClient, Prisma } from '@prisma/client'; +import express, { RequestHandler, Request, Response, NextFunction } from 'express'; +export declare const EXPRESS: typeof express; +export declare const PORT: string | number; +export declare const prismaClient: PrismaClient; +export { Prisma }; +export type ExpressHandler = RequestHandler; +export type ExpressRequest = Request; +export type ExpressResponse = Response; +export type ExpressNextFunction = NextFunction; +//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/dist/libs/constants.d.ts.map b/dist/libs/constants.d.ts.map new file mode 100644 index 00000000..870f744a --- /dev/null +++ b/dist/libs/constants.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,OAAO,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAInF,eAAO,MAAM,OAAO,gBAAU,CAAC;AAC/B,eAAO,MAAM,IAAI,iBAA2B,CAAC;AAC7C,eAAO,MAAM,YAAY,uGAAmB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC;AAC5C,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AACrC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;AACvC,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/constants.js b/dist/libs/constants.js new file mode 100644 index 00000000..53a74ed4 --- /dev/null +++ b/dist/libs/constants.js @@ -0,0 +1,48 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Prisma = exports.prismaClient = exports.PORT = exports.EXPRESS = void 0; +const dotenv = __importStar(require("dotenv")); +const client_1 = require("@prisma/client"); +Object.defineProperty(exports, "Prisma", { enumerable: true, get: function () { return client_1.Prisma; } }); +const express_1 = __importDefault(require("express")); +dotenv.config(); +exports.EXPRESS = express_1.default; +exports.PORT = process.env.PORT || 3000; +exports.prismaClient = new client_1.PrismaClient; +//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/dist/libs/constants.js.map b/dist/libs/constants.js.map new file mode 100644 index 00000000..b28f5b50 --- /dev/null +++ b/dist/libs/constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,2CAAsD;AAQ7C,uFARc,eAAM,OAQd;AAPf,sDAAmF;AAEnF,MAAM,CAAC,MAAM,EAAE,CAAC;AAEH,QAAA,OAAO,GAAG,iBAAO,CAAC;AAClB,QAAA,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAChC,QAAA,YAAY,GAAG,IAAI,qBAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts b/dist/libs/corsSetUp.d.ts new file mode 100644 index 00000000..898f7ad0 --- /dev/null +++ b/dist/libs/corsSetUp.d.ts @@ -0,0 +1,3 @@ +import 'dotenv/config'; +export declare const getCorsOrigin: () => string | string[]; +//# sourceMappingURL=corsSetUp.d.ts.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts.map b/dist/libs/corsSetUp.d.ts.map new file mode 100644 index 00000000..f9c29914 --- /dev/null +++ b/dist/libs/corsSetUp.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"corsSetUp.d.ts","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,eAAO,MAAM,aAAa,QAAO,MAAM,GAAG,MAAM,EAS/C,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.js b/dist/libs/corsSetUp.js new file mode 100644 index 00000000..7563d23d --- /dev/null +++ b/dist/libs/corsSetUp.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getCorsOrigin = void 0; +require("dotenv/config"); +const getCorsOrigin = () => { + const corsOrigin = process.env.CORS_ORIGIN; + if (!corsOrigin || corsOrigin === '*') + return '*'; + if (corsOrigin.includes(',')) + return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); + // 단일 origin (trailing slash 제거) + return corsOrigin.trim().replace(/\/$/, ''); +}; +exports.getCorsOrigin = getCorsOrigin; +//# sourceMappingURL=corsSetUp.js.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.js.map b/dist/libs/corsSetUp.js.map new file mode 100644 index 00000000..2be0f830 --- /dev/null +++ b/dist/libs/corsSetUp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"corsSetUp.js","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":";;;AAAA,yBAAuB;AAChB,MAAM,aAAa,GAAG,GAAsB,EAAE;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC3C,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAEnF,gCAAgC;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC;AATW,QAAA,aAAa,iBASxB"} \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts b/dist/libs/interfaces.d.ts new file mode 100644 index 00000000..ee927452 --- /dev/null +++ b/dist/libs/interfaces.d.ts @@ -0,0 +1,84 @@ +import { Prisma } from './../libs/constants'; +export interface ProductType { + id: number; + name: string; + description?: string; + price: number; + tags: string[]; + createdAt: Date; + updatedAt: Date; + comments?: CommentType[]; + productLikes?: ProductLikeType[]; +} +export interface ArticleType { + id: number; + title: string; + content: string; + createdAt: Date; + comments?: CommentType[]; + articleLikes?: ArticleLikeType[]; +} +export interface CommentType { + id: number; + content: string; + createdAt: Date; + updatedAt: Date; + productId?: number; + articleId?: number; + product?: ProductType; + article?: ArticleType; +} +export interface UserType { + id: number; + email: string; + password: string; + nickname: string; + image?: string | null; + createdAt: Date; + updatedAt: Date; + refreshToken?: string | null; + productLikes?: ProductLikeType[]; + articleLikes?: ArticleLikeType[]; +} +export interface UpdateUserPasswordType { + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} +export interface ProductLikeType { + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + productId: number; + user: UserType; + product: ProductType; +} +export interface ArticleLikeType { + id: number; + userId: number; + articleId: number; + user: UserType; + article: ArticleType; + createdAt: Date; +} +export type ProductFindOptions = Prisma.ProductFindManyArgs; +export type ArticleFindOptions = Prisma.ArticleFindManyArgs; +export interface ProductWithLikes extends ProductType { + productLikes: ProductLikeType[]; +} +export interface ProductWithIsLiked extends ProductType { + isLiked: boolean; +} +export interface ArticleWithLikes extends ArticleType { + articleLikes: ArticleLikeType[]; +} +export interface ArticleWithIsLiked extends ArticleType { + isLiked: boolean; +} +export type UserPublicData = Omit; +export type ProductPublicData = Omit; +export type UpdateProductData = Partial; +export type ArticlePublicData = Omit; +export type UpdateArticleData = Partial; +//# sourceMappingURL=interfaces.d.ts.map \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts.map b/dist/libs/interfaces.d.ts.map new file mode 100644 index 00000000..1ccd56a5 --- /dev/null +++ b/dist/libs/interfaces.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/interfaces.js b/dist/libs/interfaces.js new file mode 100644 index 00000000..db919115 --- /dev/null +++ b/dist/libs/interfaces.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=interfaces.js.map \ No newline at end of file diff --git a/dist/libs/interfaces.js.map b/dist/libs/interfaces.js.map new file mode 100644 index 00000000..cbb18070 --- /dev/null +++ b/dist/libs/interfaces.js.map @@ -0,0 +1 @@ +{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/libs/removeTool.d.ts b/dist/libs/removeTool.d.ts new file mode 100644 index 00000000..840d8a3a --- /dev/null +++ b/dist/libs/removeTool.d.ts @@ -0,0 +1,2 @@ +export declare const removeUndefined: (obj: any) => any; +//# sourceMappingURL=removeTool.d.ts.map \ No newline at end of file diff --git a/dist/libs/removeTool.d.ts.map b/dist/libs/removeTool.d.ts.map new file mode 100644 index 00000000..f7f0ec74 --- /dev/null +++ b/dist/libs/removeTool.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"removeTool.d.ts","sourceRoot":"","sources":["../../src/libs/removeTool.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,GAAI,KAAK,GAAG,QAQvC,CAAC"} \ No newline at end of file diff --git a/dist/libs/removeTool.js b/dist/libs/removeTool.js new file mode 100644 index 00000000..1120835f --- /dev/null +++ b/dist/libs/removeTool.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeUndefined = void 0; +// undefined인 키를 싹 지워주는 함수 +const removeUndefined = (obj) => { + const newObj = {}; + Object.keys(obj).forEach((key) => { + if (obj[key] !== undefined) { + newObj[key] = obj[key]; + } + }); + return newObj; +}; +exports.removeUndefined = removeUndefined; +//# sourceMappingURL=removeTool.js.map \ No newline at end of file diff --git a/dist/libs/removeTool.js.map b/dist/libs/removeTool.js.map new file mode 100644 index 00000000..4e1733c8 --- /dev/null +++ b/dist/libs/removeTool.js.map @@ -0,0 +1 @@ +{"version":3,"file":"removeTool.js","sourceRoot":"","sources":["../../src/libs/removeTool.ts"],"names":[],"mappings":";;;AAAA,0BAA0B;AACnB,MAAM,eAAe,GAAG,CAAC,GAAQ,EAAE,EAAE;IACxC,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7B,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AARW,QAAA,eAAe,mBAQ1B"} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts new file mode 100644 index 00000000..371115b3 --- /dev/null +++ b/dist/main.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/main.d.ts.map b/dist/main.d.ts.map new file mode 100644 index 00000000..28b87d89 --- /dev/null +++ b/dist/main.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 00000000..e5d4ec53 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,26 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("./libs/constants"); +const cors_1 = __importDefault(require("cors")); +const routerManager_1 = require("./Routers/routerManager"); +const corsSetUp_1 = require("./libs/corsSetUp"); +const errorHandler_1 = __importDefault(require("./libs/Handler/errorHandler")); +const app = (0, constants_1.EXPRESS)(); +app.use((0, cors_1.default)({ + origin: (0, corsSetUp_1.getCorsOrigin)(), + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], + allowedHeaders: ['Content-Type', 'Authorization'] +})); +app.use(constants_1.EXPRESS.json()); +app.use(constants_1.EXPRESS.urlencoded({ extended: true })); +app.use('/upload', constants_1.EXPRESS.static('upload')); +app.use('/', routerManager_1.RouterManager); +app.use(errorHandler_1.default); +app.listen(constants_1.PORT, () => { + console.log(`Server is running`); +}); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map new file mode 100644 index 00000000..4a49c239 --- /dev/null +++ b/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;AAAA,gDAAiD;AACjD,gDAAwB;AACxB,2DAAwD;AACxD,gDAAiD;AACjD,+EAAuD;AAEvD,MAAM,GAAG,GAAG,IAAA,mBAAO,GAAE,CAAC;AAGtB,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC;IACT,MAAM,EAAE,IAAA,yBAAa,GAAE;IACvB,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC;IAClD,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;CACpD,CAAC,CAAC,CAAC;AAIJ,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAI7C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,6BAAa,CAAC,CAAC;AAI5B,GAAG,CAAC,GAAG,CAAC,sBAAY,CAAC,CAAC;AAEtB,GAAG,CAAC,MAAM,CAAC,gBAAI,EAAE,GAAG,EAAE;IAClB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts b/dist/middlewares/auth.d.ts new file mode 100644 index 00000000..59c495ca --- /dev/null +++ b/dist/middlewares/auth.d.ts @@ -0,0 +1,12 @@ +import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; +declare function verifyProduectAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction): Promise; +declare function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction): Promise; +declare const _default: { + verifyAccessToken: ExpressHandler; + softVerifyAccessToken: ExpressHandler; + verifyProduectAuth: typeof verifyProduectAuth; + verifyArticleAuth: typeof verifyArticleAuth; + verifyRefreshToken: ExpressHandler; +}; +export default _default; +//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts.map b/dist/middlewares/auth.d.ts.map new file mode 100644 index 00000000..7d975c9a --- /dev/null +++ b/dist/middlewares/auth.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAkCzG,iBAAe,kBAAkB,CAC5B,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,iBAaxE;AAED,iBAAe,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,iBAapG;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/middlewares/auth.js b/dist/middlewares/auth.js new file mode 100644 index 00000000..024cf9c1 --- /dev/null +++ b/dist/middlewares/auth.js @@ -0,0 +1,92 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_jwt_1 = require("express-jwt"); // express-jwt 라이브러리 임포트 (JWT 검증 미들웨어 생성용) +const productRepository_1 = __importDefault(require("../repositories/productRepository")); // 상품 데이터 접근을 위한 레포지토리 임포트 +const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); // 게시글 데이터 접근을 위한 레포지토리 임포트 +const errorHandler_1 = require("../libs/Handler/errorHandler"); // 커스텀 에러 핸들러 임포트 +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); // jsonwebtoken 라이브러리 및 타입 임포트 +const JWT_SECRET = process.env.JWT_SECRET; // 환경 변수에서 JWT 비밀키를 가져옴 +if (!JWT_SECRET) { // 비밀키가 설정되지 않은 경우 에러 발생 + throw new Error('JWT_SECRET is not defined in environment variables.'); // 서버 실행 중단 및 에러 메시지 출력 +} // 비밀키 확인 종료 +const verifyAccessToken = (0, express_jwt_1.expressjwt)({ + secret: JWT_SECRET, // 검증에 사용할 비밀키 설정 + algorithms: ["HS256"], // 사용할 암호화 알고리즘 지정 + requestProperty: 'user' // 검증된 페이로드를 req.user 객체에 저장하도록 설정 +}); // 액세스 토큰 검증 설정 종료 +const softVerifyAccessToken = (req, res, next) => { + var _a; + const token = (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.split(' ')[1]; // Authorization 헤더에서 'Bearer '를 제외한 토큰 문자열 추출 + if (token) { // 토큰이 존재하는 경우 + jsonwebtoken_1.default.verify(token, JWT_SECRET, (err, decoded) => { + if (!err && decoded && typeof decoded === 'object') { // 에러가 없고 유효한 객체 형태의 페이로드인 경우 + req.user = decoded; // req.user에 사용자 정보 할당 + } // 유효성 확인 종료 + next(); // 다음 미들웨어로 진행 (검증 실패해도 진행) + }); // 검증 함수 종료 + } + else { // 토큰이 없는 경우 + next(); // 바로 다음 미들웨어로 진행 + } // 토큰 존재 여부 확인 종료 +}; // softVerifyAccessToken 종료 +const verifyRefreshToken = (0, express_jwt_1.expressjwt)({ + secret: JWT_SECRET, // 검증용 비밀키 + algorithms: ['HS256'], // 알고리즘 + getToken: (req) => req.cookies.refreshToken, // 쿠키에서 리프레시 토큰을 가져오도록 설정 +}); // 리프레시 토큰 검증 설정 종료 +function verifyProduectAuth // 상품 수정/삭제 권한 확인 함수 +(req, res, next) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id || ""; // URL 파라미터에서 상품 ID 추출 + if (!id) + throw new errorHandler_1.CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 + const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 + const product = yield productRepository_1.default.findById(idtoNumber); // DB에서 해당 상품 조회 + if (!product) { // 상품이 존재하지 않는 경우 + throw new errorHandler_1.CustomError(404, 'product not found'); // 404 에러 발생 + } // 상품 존재 확인 종료 + // 사용자 정보가 없거나, 상품의 소유자 ID(현재 product.id로 되어있으나 보통 product.userId)가 요청자 ID와 다르면 권한 없음 처리 + if (typeof req.user !== 'object' || req.user.userId === undefined || product.id !== req.user.userId) { + throw new errorHandler_1.CustomError(403, 'Forbidden'); // 403 금지 에러 발생 + } // 권한 확인 종료 + return next(); // 권한이 확인되면 다음 미들웨어로 진행 + }); +} +; // verifyProduectAuth 종료 +function verifyArticleAuth(req, res, next) { + return __awaiter(this, void 0, void 0, function* () { + const id = req.params.id; // URL 파라미터에서 게시글 ID 추출 + if (!id) + throw new errorHandler_1.CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 + const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 + const article = yield articleRepository_1.default.findById(idtoNumber); // DB에서 해당 게시글 조회 + if (!article) { // 게시글이 존재하지 않는 경우 + throw new errorHandler_1.CustomError(404, 'article not found'); // 404 에러 발생 + } // 게시글 존재 확인 종료 + // 사용자 정보가 없거나, 게시글의 소유자 ID(현재 article.id로 되어있으나 보통 article.userId)가 요청자 ID와 다르면 권한 없음 처리 + if (typeof req.user !== 'object' || req.user.userId === undefined || article.id !== req.user.userId) { + throw new errorHandler_1.CustomError(403, 'Forbidden'); // 403 금지 에러 발생 + } // 권한 확인 종료 + return next(); // 권한이 확인되면 다음 미들웨어로 진행 + }); +} // verifyArticleAuth 종료 +exports.default = { + verifyAccessToken, + softVerifyAccessToken, + verifyProduectAuth, + verifyArticleAuth, + verifyRefreshToken, +}; +//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/dist/middlewares/auth.js.map b/dist/middlewares/auth.js.map new file mode 100644 index 00000000..c3d77be2 --- /dev/null +++ b/dist/middlewares/auth.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,6CAAyC,CAAC,0CAA0C;AACpF,0FAAkE,CAAC,0BAA0B;AAC7F,0FAAkE,CAAC,2BAA2B;AAC9F,+DAA2D,CAAC,iBAAiB;AAE7E,gEAA+C,CAAC,8BAA8B;AAE9E,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,uBAAuB;AAClE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,wBAAwB;IACvC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC,CAAC,uBAAuB;AACnG,CAAC,CAAC,YAAY;AAEd,MAAM,iBAAiB,GAAmB,IAAA,wBAAU,EAAC;IACjD,MAAM,EAAE,UAAU,EAAE,iBAAiB;IACrC,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB;IACzC,eAAe,EAAE,MAAM,CAAC,kCAAkC;CAC7D,CAAC,CAAC,CAAC,kBAAkB;AAEtB,MAAM,qBAAqB,GAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;;IAC7D,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,OAAO,CAAC,aAAa,0CAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,8CAA8C;IACtG,IAAI,KAAK,EAAE,CAAC,CAAC,cAAc;QACvB,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;YAC3C,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC,CAAC,6BAA6B;gBAC/E,GAAG,CAAC,IAAI,GAAG,OAA6C,CAAC,CAAC,sBAAsB;YACpF,CAAC,CAAC,YAAY;YACd,IAAI,EAAE,CAAC,CAAC,2BAA2B;QACvC,CAAC,CAAC,CAAC,CAAC,WAAW;IACnB,CAAC;SAAM,CAAC,CAAC,YAAY;QACjB,IAAI,EAAE,CAAC,CAAC,iBAAiB;IAC7B,CAAC,CAAC,iBAAiB;AACvB,CAAC,CAAC,CAAC,2BAA2B;AAE9B,MAAM,kBAAkB,GAAmB,IAAA,wBAAU,EAAC;IAClD,MAAM,EAAE,UAAU,EAAE,UAAU;IAC9B,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO;IAC9B,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,yBAAyB;CACzE,CAAC,CAAC,CAAC,mBAAmB;AAEvB,SAAe,kBAAkB,CAAC,oBAAoB;CACjD,GAAmB,EAAE,GAAoB,EAAE,IAAyB;;QACrE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,sBAAsB;QACtD,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC,oBAAoB;QAC7E,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB;QAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB;QAC9E,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,iBAAiB;YAC7B,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC,YAAY;QACjE,CAAC,CAAC,cAAc;QAChB,wFAAwF;QACxF,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAClG,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,eAAe;QAC5D,CAAC,CAAC,WAAW;QACb,OAAO,IAAI,EAAE,CAAC,CAAC,uBAAuB;IAC1C,CAAC;CAAA;AAAA,CAAC,CAAC,wBAAwB;AAE3B,SAAe,iBAAiB,CAAC,GAAmB,EAAE,GAAoB,EAAE,IAAyB;;QACjG,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,uBAAuB;QACjD,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC,oBAAoB;QAC7E,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB;QAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB;QAC/E,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,kBAAkB;YAC9B,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC,YAAY;QACjE,CAAC,CAAC,eAAe;QACjB,yFAAyF;QACzF,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAClG,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,eAAe;QAC5D,CAAC,CAAC,WAAW;QACb,OAAO,IAAI,EAAE,CAAC,CAAC,uBAAuB;IAC1C,CAAC;CAAA,CAAC,uBAAuB;AAEzB,kBAAe;IACX,iBAAiB;IACjB,qBAAqB;IACrB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;CACrB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts b/dist/repositories/articleLikeRepository.d.ts new file mode 100644 index 00000000..06a022de --- /dev/null +++ b/dist/repositories/articleLikeRepository.d.ts @@ -0,0 +1,25 @@ +declare function find(userId: number, articleId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +} | null>; +declare function create(userId: number, articleId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +}>; +declare function remove(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + articleId: number; +}>; +declare const _default: { + find: typeof find; + create: typeof create; + remove: typeof remove; +}; +export default _default; +//# sourceMappingURL=articleLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts.map b/dist/repositories/articleLikeRepository.d.ts.map new file mode 100644 index 00000000..536644aa --- /dev/null +++ b/dist/repositories/articleLikeRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;;;;;;AAED,wBAIE"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js b/dist/repositories/articleLikeRepository.js new file mode 100644 index 00000000..37052fc7 --- /dev/null +++ b/dist/repositories/articleLikeRepository.js @@ -0,0 +1,49 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function find(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.findUnique({ + where: { + userId_articleId: { + userId, + articleId, + }, + }, + }); + }); +} +function create(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.create({ + data: { + userId, + articleId, + }, + }); + }); +} +function remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.articleLike.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + find, + create, + remove, +}; +//# sourceMappingURL=articleLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js.map b/dist/repositories/articleLikeRepository.js.map new file mode 100644 index 00000000..3b0581d5 --- /dev/null +++ b/dist/repositories/articleLikeRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAc,EAAE,SAAiB;;QACjD,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAc,EAAE,SAAiB;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU;;QAC5B,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts b/dist/repositories/articleRepository.d.ts new file mode 100644 index 00000000..8053f1d7 --- /dev/null +++ b/dist/repositories/articleRepository.d.ts @@ -0,0 +1,59 @@ +import { ArticlePublicData, ArticleFindOptions, UpdateArticleData } from '../libs/interfaces'; +declare function findById(id: number, userId?: number): Promise<{ + articleLikes: { + id: number; + createdAt: Date; + userId: number; + articleId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function findAll(findOptions: ArticleFindOptions, userId: number): Promise<({ + articleLikes: { + id: number; + createdAt: Date; + userId: number; + articleId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +})[]>; +declare function create(userFields: ArticlePublicData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function update(id: number, data: UpdateArticleData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare function ondelete(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; +}>; +declare const _default: { + findById: typeof findById; + findAll: typeof findAll; + create: typeof create; + update: typeof update; + ondelete: typeof ondelete; +}; +export default _default; +//# sourceMappingURL=articleRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts.map b/dist/repositories/articleRepository.d.ts.map new file mode 100644 index 00000000..f48d6544 --- /dev/null +++ b/dist/repositories/articleRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;GAQlD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;GAQxD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.js b/dist/repositories/articleRepository.js new file mode 100644 index 00000000..7d2e1d77 --- /dev/null +++ b/dist/repositories/articleRepository.js @@ -0,0 +1,80 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findById(id, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.article.findUniqueOrThrow({ + where: { + id, + }, + include, + }); + }); +} +function findAll(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + articleLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.article.findMany(Object.assign(Object.assign({}, findOptions), { include })); + }); +} +function create(userFields) { + return __awaiter(this, void 0, void 0, function* () { + const { comments, articleLikes } = userFields, NewuserFields = __rest(userFields, ["comments", "articleLikes"]); + return yield constants_1.prismaClient.article.create({ + data: Object.assign({}, NewuserFields), + }); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + const { comments, articleLikes } = data, updateData = __rest(data, ["comments", "articleLikes"]); + return constants_1.prismaClient.article.update({ + where: { + id, + }, + data: updateData, + }); + }); +} +function ondelete(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_1.prismaClient.article.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + findById, + findAll, + create, + update, + ondelete, +}; +//# sourceMappingURL=articleRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.js.map b/dist/repositories/articleRepository.js.map new file mode 100644 index 00000000..32bb34d2 --- /dev/null +++ b/dist/repositories/articleRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleRepository.js","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAOjD,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QAEF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAEhE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;CACX,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts b/dist/repositories/productLikeRepository.d.ts new file mode 100644 index 00000000..5eb660bc --- /dev/null +++ b/dist/repositories/productLikeRepository.d.ts @@ -0,0 +1,42 @@ +declare function find(userId: number, productId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +} | null>; +declare function create(userId: number, productId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +}>; +declare function remove(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + productId: number; +}>; +declare function findLikedProductsByUserId(userId: number): Promise<({ + product: { + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; + }; +} & { + id: number; + createdAt: Date; + userId: number; + productId: number; +})[]>; +declare const _default: { + find: typeof find; + create: typeof create; + remove: typeof remove; + findLikedProductsByUserId: typeof findLikedProductsByUserId; +}; +export default _default; +//# sourceMappingURL=productLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts.map b/dist/repositories/productLikeRepository.d.ts.map new file mode 100644 index 00000000..2fb02b5d --- /dev/null +++ b/dist/repositories/productLikeRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAStD;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js b/dist/repositories/productLikeRepository.js new file mode 100644 index 00000000..1aec8927 --- /dev/null +++ b/dist/repositories/productLikeRepository.js @@ -0,0 +1,62 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function find(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.productLike.findUnique({ + where: { + userId_productId: { + userId, + productId, + }, + }, + }); + }); +} +function create(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.productLike.create({ + data: { + userId, + productId, + }, + }); + }); +} +function remove(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.productLike.delete({ + where: { + id, + }, + }); + }); +} +function findLikedProductsByUserId(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.productLike.findMany({ + where: { + userId, + }, + include: { + product: true, + }, + }); + }); +} +exports.default = { + find, + create, + remove, + findLikedProductsByUserId, +}; +//# sourceMappingURL=productLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js.map b/dist/repositories/productLikeRepository.js.map new file mode 100644 index 00000000..33e8565a --- /dev/null +++ b/dist/repositories/productLikeRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAc,EAAE,SAAiB;;QACjD,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAc,EAAE,SAAiB;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU;;QAC5B,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,yBAAyB,CAAC,MAAc;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE;gBACH,MAAM;aACT;YACD,OAAO,EAAE;gBACL,OAAO,EAAE,IAAI;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;IACN,yBAAyB;CAC5B,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts b/dist/repositories/productRepository.d.ts new file mode 100644 index 00000000..b93ce191 --- /dev/null +++ b/dist/repositories/productRepository.d.ts @@ -0,0 +1,79 @@ +import { ProductPublicData, ProductFindOptions, UpdateProductData } from './../libs/interfaces'; +declare function findByUserId(userId: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +}[]>; +declare function findById(id: number, userId?: number): Promise<{ + productLikes: { + id: number; + createdAt: Date; + userId: number; + productId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +}>; +declare function findAll(findOptions: ProductFindOptions, userId: number): Promise<({ + productLikes: { + id: number; + createdAt: Date; + userId: number; + productId: number; + }[]; +} & { + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +})[]>; +declare function update(id: number, data: UpdateProductData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +}>; +declare function create(userFields: ProductPublicData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +}>; +declare function ondelete(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; +}>; +declare const _default: { + findById: typeof findById; + findAll: typeof findAll; + update: typeof update; + create: typeof create; + ondelete: typeof ondelete; + findByUserId: typeof findByUserId; +}; +export default _default; +//# sourceMappingURL=productRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts.map b/dist/repositories/productRepository.d.ts.map new file mode 100644 index 00000000..b426899b --- /dev/null +++ b/dist/repositories/productRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAKhG,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;;GAQxD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;GAOlD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;GAMjC;;;;;;;;;AAED,wBAOE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.js b/dist/repositories/productRepository.js new file mode 100644 index 00000000..1916a204 --- /dev/null +++ b/dist/repositories/productRepository.js @@ -0,0 +1,90 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findByUserId(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.product.findMany({ + where: { + id: userId, + }, + }); + }); +} +function findById(id, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.product.findUniqueOrThrow({ + where: { + id, + }, + include, + }); + }); +} +function findAll(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const include = { + productLikes: userId ? { where: { userId } } : false, + }; + return constants_1.prismaClient.product.findMany(Object.assign(Object.assign({}, findOptions), { include })); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + const { comments, productLikes } = data, updateData = __rest(data, ["comments", "productLikes"]); + return constants_1.prismaClient.product.update({ + where: { + id, + }, + data: updateData, + }); + }); +} +function create(userFields) { + return __awaiter(this, void 0, void 0, function* () { + const { comments, productLikes } = userFields, NewuserFields = __rest(userFields, ["comments", "productLikes"]); + return yield constants_1.prismaClient.product.create({ + data: Object.assign({}, NewuserFields), + }); + }); +} +function ondelete(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_1.prismaClient.product.delete({ + where: { + id, + }, + }); + }); +} +exports.default = { + findById, + findAll, + update, + create, + ondelete, + findByUserId +}; +//# sourceMappingURL=productRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productRepository.js.map b/dist/repositories/productRepository.js.map new file mode 100644 index 00000000..b67fd5f4 --- /dev/null +++ b/dist/repositories/productRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAMjD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAChE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;CACf,CAAC"} \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts b/dist/repositories/userRepository.d.ts new file mode 100644 index 00000000..82d71f6b --- /dev/null +++ b/dist/repositories/userRepository.d.ts @@ -0,0 +1,49 @@ +import { UserType } from "./../libs/interfaces"; +declare function findById(id: number): Promise<{ + password: string; + refreshToken: string | null; + id: number; + email: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; +} | null>; +declare function findByEmail(email: string): Promise<{ + password: string; + refreshToken: string | null; + id: number; + email: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; +} | null>; +declare function save(user: UserType): Promise<{ + password: string; + refreshToken: string | null; + id: number; + email: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; +}>; +declare function update(id: number, data: Partial): Promise<{ + password: string; + refreshToken: string | null; + id: number; + email: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; +}>; +declare const _default: { + findById: typeof findById; + findByEmail: typeof findByEmail; + save: typeof save; + update: typeof update; +}; +export default _default; +//# sourceMappingURL=userRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts.map b/dist/repositories/userRepository.d.ts.map new file mode 100644 index 00000000..ec82ac69 --- /dev/null +++ b/dist/repositories/userRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAMjC;AAED,iBAAe,WAAW,CAAC,KAAK,EAAE,MAAM;;;;;;;;;UAMvC;AAED,iBAAe,IAAI,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;GAQjC;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;GAWxD;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/userRepository.js b/dist/repositories/userRepository.js new file mode 100644 index 00000000..498af72e --- /dev/null +++ b/dist/repositories/userRepository.js @@ -0,0 +1,71 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findById(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.user.findUnique({ + where: { + id, + }, + }); + }); +} +function findByEmail(email) { + return __awaiter(this, void 0, void 0, function* () { + return yield constants_1.prismaClient.user.findUnique({ + where: { + email, + }, + }); + }); +} +; +function save(user) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.user.create({ + data: { + email: user.email, + nickname: user.nickname, + password: user.password, + }, + }); + }); +} +function update(id, data) { + return __awaiter(this, void 0, void 0, function* () { + const { createdAt, updatedAt, articleLikes, productLikes } = data, Newdata = __rest(data, ["createdAt", "updatedAt", "articleLikes", "productLikes"]); + return constants_1.prismaClient.user.update({ + where: { + id, + }, + data: Object.assign({}, Newdata), + }); + }); +} +exports.default = { + findById, + findByEmail, + save, + update +}; +//# sourceMappingURL=userRepository.js.map \ No newline at end of file diff --git a/dist/repositories/userRepository.js.map b/dist/repositories/userRepository.js.map new file mode 100644 index 00000000..1ede5f90 --- /dev/null +++ b/dist/repositories/userRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userRepository.js","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAGjD,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,KAAa;;QACpC,OAAO,MAAM,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE;gBACH,KAAK;aACR;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,IAAI,CAAC,IAAc;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,IAAI,EAAE;gBACF,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aAC1B;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,KAAiB,IAAI,EAAhB,OAAO,UAAK,IAAI,EAAvE,0DAAgE,CAAO,CAAC;QAE9E,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,OAAO,CACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,WAAW;IACX,IAAI;IACJ,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.d.ts b/dist/services/articleService.d.ts new file mode 100644 index 00000000..b2f37a7b --- /dev/null +++ b/dist/services/articleService.d.ts @@ -0,0 +1,25 @@ +import { ArticleFindOptions } from './../libs/interfaces'; +declare class ArticleService { + likeArticle(userId: number, articleId: number): Promise<{ + liked: boolean; + }>; + getArticleById(articleId: number, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; + }>; + getArticles(findOptions: ArticleFindOptions, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + title: string; + content: string; + }[]>; +} +export declare const articleService: ArticleService; +export {}; +//# sourceMappingURL=articleService.d.ts.map \ No newline at end of file diff --git a/dist/services/articleService.d.ts.map b/dist/services/articleService.d.ts.map new file mode 100644 index 00000000..6f1f8383 --- /dev/null +++ b/dist/services/articleService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.js b/dist/services/articleService.js new file mode 100644 index 00000000..23352590 --- /dev/null +++ b/dist/services/articleService.js @@ -0,0 +1,59 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.articleService = void 0; +const articleLikeRepository_1 = __importDefault(require("../repositories/articleLikeRepository")); +const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); +class ArticleService { + likeArticle(userId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + const existingLike = yield articleLikeRepository_1.default.find(userId, articleId); + if (existingLike) { + yield articleLikeRepository_1.default.remove(existingLike.id); + return { liked: false }; + } + yield articleLikeRepository_1.default.create(userId, articleId); + return { liked: true }; + }); + } + getArticleById(articleId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const article = yield articleRepository_1.default.findById(articleId, userId); + const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); + }); + } + getArticles(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const articles = yield articleRepository_1.default.findAll(findOptions, userId); + return articles.map((article) => { + const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); + }); + }); + } +} +exports.articleService = new ArticleService(); +//# sourceMappingURL=articleService.js.map \ No newline at end of file diff --git a/dist/services/articleService.js.map b/dist/services/articleService.js.map new file mode 100644 index 00000000..32aeee3b --- /dev/null +++ b/dist/services/articleService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kGAA0E;AAC1E,0FAAkE;AAGlE,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;YAED,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.d.ts b/dist/services/productService.d.ts new file mode 100644 index 00000000..347d88bb --- /dev/null +++ b/dist/services/productService.d.ts @@ -0,0 +1,29 @@ +import { ProductFindOptions } from '../libs/interfaces'; +declare class ProductService { + likeProduct(userId: number, productId: number): Promise<{ + liked: boolean; + }>; + getProductById(productId: number, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; + }>; + getProducts(findOptions: ProductFindOptions, userId: number): Promise<{ + isLiked: boolean; + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; + }[]>; +} +export declare const productService: ProductService; +export {}; +//# sourceMappingURL=productService.d.ts.map \ No newline at end of file diff --git a/dist/services/productService.d.ts.map b/dist/services/productService.d.ts.map new file mode 100644 index 00000000..49eab074 --- /dev/null +++ b/dist/services/productService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.js b/dist/services/productService.js new file mode 100644 index 00000000..045d62ec --- /dev/null +++ b/dist/services/productService.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.productService = void 0; +const productRepository_1 = __importDefault(require("../repositories/productRepository")); +const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); +class ProductService { + likeProduct(userId, productId) { + return __awaiter(this, void 0, void 0, function* () { + const existingLike = yield productLikeRepository_1.default.find(userId, productId); + if (existingLike) { + yield productLikeRepository_1.default.remove(existingLike.id); + return { liked: false }; + } + else { + yield productLikeRepository_1.default.create(userId, productId); + return { liked: true }; + } + }); + } + getProductById(productId, userId) { + return __awaiter(this, void 0, void 0, function* () { + const product = yield productRepository_1.default.findById(productId, userId); + const { productLikes } = product, rest = __rest(product, ["productLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); + }); + } + getProducts(findOptions, userId) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productRepository_1.default.findAll(findOptions, userId); + return products.map((product) => { + const { productLikes } = product, rest = __rest(product, ["productLikes"]); + return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); + }); + }); + } +} +exports.productService = new ProductService(); +//# sourceMappingURL=productService.js.map \ No newline at end of file diff --git a/dist/services/productService.js.map b/dist/services/productService.js.map new file mode 100644 index 00000000..865751b4 --- /dev/null +++ b/dist/services/productService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,kGAA0E;AAI1E,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.d.ts b/dist/services/userService.d.ts new file mode 100644 index 00000000..85cf2780 --- /dev/null +++ b/dist/services/userService.d.ts @@ -0,0 +1,53 @@ +import { UserType, UpdateUserPasswordType, UserPublicData } from "./../libs/interfaces"; +declare class UserService { + createUser(user: UserType): Promise; + filterSensitivceUserData(user: UserType): UserPublicData; + getUser(email: string, password: string): Promise; + getUserById(id: number): Promise; + updateProfile(id: number, data: Partial): Promise; + updatePassword(id: number, data: UpdateUserPasswordType): Promise; + getProductsByUserId(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; + }[]>; + getLikedProductsByUserId(id: number): Promise<({ + product: { + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + description: string | null; + price: number; + tags: string[]; + }; + } & { + id: number; + createdAt: Date; + userId: number; + productId: number; + })[]>; + updateUser(id: number, data: Partial): Promise<{ + password: string; + refreshToken: string | null; + id: number; + email: string; + nickname: string; + image: string | null; + createdAt: Date; + updatedAt: Date; + }>; + verifyPassword(inputPassword: string, savedPassword: string): Promise; + createToken(user: UserPublicData | UserType, type?: string): string; + refreshToken(userId: number, refreshToken: string): Promise<{ + accessToken: string; + newRefreshToken: string; + }>; +} +export declare const userService: UserService; +export {}; +//# sourceMappingURL=userService.d.ts.map \ No newline at end of file diff --git a/dist/services/userService.d.ts.map b/dist/services/userService.d.ts.map new file mode 100644 index 00000000..263cea90 --- /dev/null +++ b/dist/services/userService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQxF,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAezD,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc;IAKlD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IASjE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAQhD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAS3E,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IAiBjF,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;IAG9C,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUpD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.js b/dist/services/userService.js new file mode 100644 index 00000000..f9f6bf0c --- /dev/null +++ b/dist/services/userService.js @@ -0,0 +1,151 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.userService = void 0; +const superstruct_1 = require("superstruct"); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +const userRepository_1 = __importDefault(require("../repositories/userRepository")); +const productRepository_1 = __importDefault(require("../repositories/productRepository")); +const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); +const bcrypt_1 = __importDefault(require("bcrypt")); +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); +const userStructs_1 = require("../structs/userStructs"); +function hashingPassword(password) { + return __awaiter(this, void 0, void 0, function* () { + // 함수 추가 + return bcrypt_1.default.hash(password, 12); + }); +} +class UserService { + createUser(user) { + return __awaiter(this, void 0, void 0, function* () { + const existedUser = yield userRepository_1.default.findByEmail(user.email); + if (existedUser) { + throw new errorHandler_1.CustomError(422, 'User already exists', { + email: user.email, + }); + } + const hashedPassword = yield hashingPassword(user.password); + const createdUser = yield userRepository_1.default.save(Object.assign(Object.assign({}, user), { password: hashedPassword })); + return this.filterSensitivceUserData(createdUser); + }); + } + filterSensitivceUserData(user) { + const { password, refreshToken } = user, rest = __rest(user, ["password", "refreshToken"]); + return rest; + } + getUser(email, password) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findByEmail(email); + if (!user) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + yield this.verifyPassword(password, user.password); + return this.filterSensitivceUserData(user); + }); + } + getUserById(id) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findById(id); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + return this.filterSensitivceUserData(user); + }); + } + updateProfile(id, data) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(data, userStructs_1.PatchUser); + const user = yield userRepository_1.default.update(id, data); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + return this.filterSensitivceUserData(user); + }); + } + updatePassword(id, data) { + return __awaiter(this, void 0, void 0, function* () { + (0, superstruct_1.assert)(data, userStructs_1.ChangePassword); + const { currentPassword, newPassword, confirmNewPassword } = data; + if (newPassword !== confirmNewPassword) { + throw new errorHandler_1.CustomError(400, "Passwords don't match"); + } + const user = yield userRepository_1.default.findById(id); + if (!user) { + throw new errorHandler_1.CustomError(404, 'User not found'); + } + yield this.verifyPassword(currentPassword, user.password); + const hashedPassword = yield hashingPassword(newPassword); + const updatedUser = yield userRepository_1.default.update(id, { password: hashedPassword }); + return this.filterSensitivceUserData(updatedUser); + }); + } + getProductsByUserId(id) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productRepository_1.default.findByUserId(id); + return products; + }); + } + getLikedProductsByUserId(id) { + return __awaiter(this, void 0, void 0, function* () { + const products = yield productLikeRepository_1.default.findLikedProductsByUserId(id); + return products; + }); + } + updateUser(id, data) { + return __awaiter(this, void 0, void 0, function* () { + return yield userRepository_1.default.update(id, data); + }); + } + verifyPassword(inputPassword, savedPassword) { + return __awaiter(this, void 0, void 0, function* () { + const isValid = yield bcrypt_1.default.compare(inputPassword, savedPassword); + if (!isValid) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + }); + } + createToken(user, type) { + const JWTsecretKey = process.env.JWT_SECRET; + if (!JWTsecretKey) + throw new errorHandler_1.CustomError(500, 'JWT_SECRET is not defined'); + const payload = { userId: user.id }; + const options = { + expiresIn: type === 'refresh' ? '1d' : '10m', + }; + return jsonwebtoken_1.default.sign(payload, JWTsecretKey, options); + } + refreshToken(userId, refreshToken) { + return __awaiter(this, void 0, void 0, function* () { + const user = yield userRepository_1.default.findById(userId); + if (!user || user.refreshToken !== refreshToken) { + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + } + const accessToken = this.createToken(user); // 변경 + const newRefreshToken = this.createToken(user, 'refresh'); // 추가 + return { accessToken, newRefreshToken }; // 변경 + }); + } +} +exports.userService = new UserService(); +//# sourceMappingURL=userService.js.map \ No newline at end of file diff --git a/dist/services/userService.js.map b/dist/services/userService.js.map new file mode 100644 index 00000000..341834c2 --- /dev/null +++ b/dist/services/userService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userService.js","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/structs/structs.d.ts b/dist/structs/structs.d.ts new file mode 100644 index 00000000..abb29fad --- /dev/null +++ b/dist/structs/structs.d.ts @@ -0,0 +1,52 @@ +import * as s from "superstruct"; +export declare const CreateProduct: s.Struct<{ + name: string; + description: string; + price: number; + tags: string[]; +}, { + name: s.Struct; + description: s.Struct; + price: s.Struct; + tags: s.Struct>; +}>; +export declare const CreateArticle: s.Struct<{ + title: string; + content: string; +}, { + title: s.Struct; + content: s.Struct; +}>; +export declare const CreateComment: s.Struct<{ + content: string; + productId?: number | undefined; + articleId?: number | undefined; +}, { + content: s.Struct; + productId: s.Struct; + articleId: s.Struct; +}>; +export declare const PatchProduct: s.Struct<{ + name?: string | undefined; + description?: string | undefined; + price?: number | undefined; + tags?: string[] | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + name: s.Struct; + description: s.Struct; + price: s.Struct; + tags: s.Struct>; +}>>; +export declare const PatchArticle: s.Struct<{ + title?: string | undefined; + content?: string | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + title: s.Struct; + content: s.Struct; +}>>; +export declare const PatchComment: s.Struct<{ + content?: string | undefined; +}, { + content: s.Struct; +}>; +//# sourceMappingURL=structs.d.ts.map \ No newline at end of file diff --git a/dist/structs/structs.d.ts.map b/dist/structs/structs.d.ts.map new file mode 100644 index 00000000..40033255 --- /dev/null +++ b/dist/structs/structs.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAGjC,eAAO,MAAM,aAAa;;;;;;;;;;EAKxB,CAAC;AAGH,eAAO,MAAM,aAAa;;;;;;EAGxB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;EAepB,CAAC;AAEP,eAAO,MAAM,YAAY;;;;;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;EAAiD,CAAC"} \ No newline at end of file diff --git a/dist/structs/structs.js b/dist/structs/structs.js new file mode 100644 index 00000000..2f15113f --- /dev/null +++ b/dist/structs/structs.js @@ -0,0 +1,65 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PatchComment = exports.PatchArticle = exports.PatchProduct = exports.CreateComment = exports.CreateArticle = exports.CreateProduct = void 0; +const s = __importStar(require("superstruct")); +exports.CreateProduct = s.object({ + name: s.size(s.string(), 1, 100), + description: s.string(), + price: s.min(s.number(), 0), + tags: s.array(s.string()), +}); +exports.CreateArticle = s.object({ + title: s.size(s.string(), 1, 100), + content: s.string(), +}); +exports.CreateComment = s.refine(s.object({ + content: s.string(), + productId: s.optional(s.number()), + articleId: s.optional(s.number()), +}), 'EitherProductIdOrArticleId', //검증 규칙 이름 +(value) => { + const { productId, articleId } = value; + // 실패 케이스: (둘 다 있거나) OR (둘 다 없거나) + if ((productId && articleId) || (!productId && !articleId)) { + return 'Comment must have *either* a productId *or* an articleId, but not both.'; + } + // 성공 케이스: 둘 중 하나만 존재함 + return true; +}); +exports.PatchProduct = s.partial(exports.CreateProduct); +exports.PatchArticle = s.partial(exports.CreateArticle); +exports.PatchComment = s.object({ content: s.optional(s.string()), }); +//# sourceMappingURL=structs.js.map \ No newline at end of file diff --git a/dist/structs/structs.js.map b/dist/structs/structs.js.map new file mode 100644 index 00000000..ad9a7c5d --- /dev/null +++ b/dist/structs/structs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"structs.js","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGpB,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAGU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAEU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACpC,CAAC,EACE,4BAA4B,EAAC,UAAU;AACvC,CAAC,KAAK,EAAE,EAAE;IACN,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACvC,iCAAiC;IACjC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,yEAAyE,CAAC;IACrF,CAAC;IAED,sBAAsB;IACtB,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEM,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts b/dist/structs/userStructs.d.ts new file mode 100644 index 00000000..6c0e51d3 --- /dev/null +++ b/dist/structs/userStructs.d.ts @@ -0,0 +1,31 @@ +import * as s from "superstruct"; +export declare const CreateUser: s.Struct<{ + password: string; + email: string; + nickname: string; + image?: string | undefined; +}, { + email: s.Struct; + nickname: s.Struct; + image: s.Struct; + password: s.Struct; +}>; +export declare const PatchUser: s.Struct<{ + email?: string | undefined; + nickname?: string | undefined; + image?: string | undefined; +}, import("superstruct/dist/utils").PartialObjectSchema<{ + email: s.Struct; + nickname: s.Struct; + image: s.Struct; +}>>; +export declare const ChangePassword: s.Struct<{ + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +}, { + currentPassword: s.Struct; + newPassword: s.Struct; + confirmNewPassword: s.Struct; +}>; +//# sourceMappingURL=userStructs.d.ts.map \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts.map b/dist/structs/userStructs.d.ts.map new file mode 100644 index 00000000..c3a5c9b7 --- /dev/null +++ b/dist/structs/userStructs.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"userStructs.d.ts","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAKjC,eAAO,MAAM,UAAU;;;;;;;;;;EAKrB,CAAC;AAGH,eAAO,MAAM,SAAS;;;;;;;;GAInB,CAAC;AAEJ,eAAO,MAAM,cAAc;;;;;;;;EAIzB,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.js b/dist/structs/userStructs.js new file mode 100644 index 00000000..bde1f424 --- /dev/null +++ b/dist/structs/userStructs.js @@ -0,0 +1,59 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChangePassword = exports.PatchUser = exports.CreateUser = void 0; +const s = __importStar(require("superstruct")); +const is_email_1 = __importDefault(require("is-email")); +const email = s.refine(s.string(), 'is_email', (v) => (0, is_email_1.default)(v)); +exports.CreateUser = s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), + password: s.string(), +}); +exports.PatchUser = s.partial(s.object({ + email: email, + nickname: s.string(), + image: s.optional(s.string()), +})); +exports.ChangePassword = s.object({ + currentPassword: s.string(), + newPassword: s.string(), + confirmNewPassword: s.string(), +}); +//# sourceMappingURL=userStructs.js.map \ No newline at end of file diff --git a/dist/structs/userStructs.js.map b/dist/structs/userStructs.js.map new file mode 100644 index 00000000..662473fb --- /dev/null +++ b/dist/structs/userStructs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"userStructs.js","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,wDAA+B;AAE/B,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,kBAAO,EAAC,CAAC,CAAC,CAAC,CAAC;AAErD,QAAA,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGU,QAAA,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC,CAAC;AAES,QAAA,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;IAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC"} \ No newline at end of file diff --git a/launch.json b/launch.json new file mode 100644 index 00000000..6cf41b93 --- /dev/null +++ b/launch.json @@ -0,0 +1,31 @@ +{ + // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. + // 기존 특성에 대한 설명을 보려면 가리킵니다. + // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug TypeScript", + // "runtimeExecutable": "/home/bluehamster/.nvm/versions/node/v18.16.0/bin/node", + "runtimeArgs": ["-r", "ts-node/register"], + "args": ["${workspaceFolder}/src/main.ts"] + }, + { + "command": "npm start", + "name": "Run npm start", + "request": "launch", + "type": "node-terminal" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/dist/main.js" + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ec129f90..11f94128 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,9 @@ "is-email": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", - "superstruct": "^2.0.2" + "socket.io": "^4.8.3", + "superstruct": "^2.0.2", + "ws": "^8.19.0" }, "devDependencies": { "@types/bcrypt": "^6.0.0", @@ -29,6 +31,8 @@ "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", "@types/node": "^24.10.1", + "@types/socket.io": "^3.0.1", + "@types/ws": "^8.18.1", "nodemon": "^3.1.11", "prisma": "^6.18.0", "ts-node": "^10.9.2", @@ -161,6 +165,12 @@ "@prisma/debug": "6.18.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -231,7 +241,6 @@ "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -356,6 +365,26 @@ "@types/node": "*" } }, + "node_modules/@types/socket.io": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.1.tgz", + "integrity": "sha512-XSma2FhVD78ymvoxYV4xGXrIH/0EKQ93rR+YR0Y+Kw1xbPzLDCip/UWSejZ08FpxYeYNci/PZPQS9anrvJRqMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "socket.io": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -429,6 +458,15 @@ "dev": true, "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bcrypt": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", @@ -897,6 +935,99 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2131,6 +2262,111 @@ "node": ">=10" } }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.18.3" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2349,6 +2585,27 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 903b1c81..226d267c 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "is-email": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", - "superstruct": "^2.0.2" + "socket.io": "^4.8.3", + "superstruct": "^2.0.2", + "ws": "^8.19.0" }, "devDependencies": { "@types/bcrypt": "^6.0.0", @@ -31,6 +33,8 @@ "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", "@types/node": "^24.10.1", + "@types/socket.io": "^3.0.1", + "@types/ws": "^8.18.1", "nodemon": "^3.1.11", "prisma": "^6.18.0", "ts-node": "^10.9.2", diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..18bdb631 --- /dev/null +++ b/public/index.html @@ -0,0 +1,37 @@ + + + + + + Document + + +

Socket.IO client test

+
+ + +
+ + + + + diff --git a/src/main.ts b/src/main.ts index fa97a1b6..44162a8b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import cors from 'cors'; import { RouterManager } from './Routers/routerManager'; import { getCorsOrigin } from './libs/corsSetUp'; import errorHandler from './libs/Handler/errorHandler'; +import { expressjwt } from 'express-jwt'; const app = EXPRESS(); @@ -15,7 +16,7 @@ app.use(cors({ })); - +app.use(EXPRESS.static('public')); app.use(EXPRESS.json()); app.use(EXPRESS.urlencoded({ extended: true })); diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts index 84318b05..688dace3 100644 --- a/src/middlewares/auth.ts +++ b/src/middlewares/auth.ts @@ -1,71 +1,71 @@ -import { expressjwt } from 'express-jwt'; -import productRepository from '../repositories/productRepository'; -import articleRepository from '../repositories/articleRepository'; -import { CustomError } from '../libs/Handler/errorHandler'; -import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; -import jwt, { JwtPayload } from 'jsonwebtoken'; +import { expressjwt } from 'express-jwt'; // express-jwt 라이브러리 임포트 (JWT 검증 미들웨어 생성용) +import productRepository from '../repositories/productRepository'; // 상품 데이터 접근을 위한 레포지토리 임포트 +import articleRepository from '../repositories/articleRepository'; // 게시글 데이터 접근을 위한 레포지토리 임포트 +import { CustomError } from '../libs/Handler/errorHandler'; // 커스텀 에러 핸들러 임포트 +import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; // 공통 타입 정의 임포트 +import jwt, { JwtPayload } from 'jsonwebtoken'; // jsonwebtoken 라이브러리 및 타입 임포트 +const JWT_SECRET = process.env.JWT_SECRET; // 환경 변수에서 JWT 비밀키를 가져옴 +if (!JWT_SECRET) { // 비밀키가 설정되지 않은 경우 에러 발생 + throw new Error('JWT_SECRET is not defined in environment variables.'); // 서버 실행 중단 및 에러 메시지 출력 +} // 비밀키 확인 종료 -const JWT_SECRET = process.env.JWT_SECRET; -if (!JWT_SECRET) { - throw new Error('JWT_SECRET is not defined in environment variables.'); -} +const verifyAccessToken: ExpressHandler = expressjwt({ // 액세스 토큰 검증 미들웨어 설정 + secret: JWT_SECRET, // 검증에 사용할 비밀키 설정 + algorithms: ["HS256"], // 사용할 암호화 알고리즘 지정 + requestProperty: 'user' // 검증된 페이로드를 req.user 객체에 저장하도록 설정 +}); // 액세스 토큰 검증 설정 종료 -const verifyAccessToken: ExpressHandler = expressjwt({ - secret: JWT_SECRET, - algorithms: ["HS256"], - requestProperty: 'user' -}); +const softVerifyAccessToken: ExpressHandler = (req, res, next) => { // 토큰이 없어도 통과시키되, 있으면 정보를 추출하는 미들웨어 + const token = req.headers.authorization?.split(' ')[1]; // Authorization 헤더에서 'Bearer '를 제외한 토큰 문자열 추출 + if (token) { // 토큰이 존재하는 경우 + jwt.verify(token, JWT_SECRET, (err, decoded) => { // 토큰 검증 시도 + if (!err && decoded && typeof decoded === 'object') { // 에러가 없고 유효한 객체 형태의 페이로드인 경우 + req.user = decoded as (JwtPayload & { userId: number; }); // req.user에 사용자 정보 할당 + } // 유효성 확인 종료 + next(); // 다음 미들웨어로 진행 (검증 실패해도 진행) + }); // 검증 함수 종료 + } else { // 토큰이 없는 경우 + next(); // 바로 다음 미들웨어로 진행 + } // 토큰 존재 여부 확인 종료 +}; // softVerifyAccessToken 종료 -const softVerifyAccessToken: ExpressHandler = (req, res, next) => { - const token = req.headers.authorization?.split(' ')[1]; - if (token) { - jwt.verify(token, JWT_SECRET, (err, decoded) => { - if (!err && decoded && typeof decoded === 'object') { - req.user = decoded as (JwtPayload & { userId: number; }); - } - next(); - }); - } else { - next(); - } -}; +const verifyRefreshToken: ExpressHandler = expressjwt({ // 리프레시 토큰 검증 미들웨어 설정 + secret: JWT_SECRET, // 검증용 비밀키 + algorithms: ['HS256'], // 알고리즘 + getToken: (req) => req.cookies.refreshToken, // 쿠키에서 리프레시 토큰을 가져오도록 설정 +}); // 리프레시 토큰 검증 설정 종료 -const verifyRefreshToken: ExpressHandler = expressjwt({ - secret: JWT_SECRET, - algorithms: ['HS256'], - getToken: (req) => req.cookies.refreshToken, -}); - -async function verifyProduectAuth - (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { - const id = req.params.id || ""; - if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); - const idtoNumber = parseInt(id); - const product = await productRepository.findById(idtoNumber); - if (!product) { - throw new CustomError(404, 'product not found'); - } +async function verifyProduectAuth // 상품 수정/삭제 권한 확인 함수 + (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { // 익스프레스 핸들러 인자 + const id = req.params.id || ""; // URL 파라미터에서 상품 ID 추출 + if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 + const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 + const product = await productRepository.findById(idtoNumber); // DB에서 해당 상품 조회 + if (!product) { // 상품이 존재하지 않는 경우 + throw new CustomError(404, 'product not found'); // 404 에러 발생 + } // 상품 존재 확인 종료 + // 사용자 정보가 없거나, 상품의 소유자 ID(현재 product.id로 되어있으나 보통 product.userId)가 요청자 ID와 다르면 권한 없음 처리 if (typeof req.user !== 'object' || req.user.userId === undefined || product.id !== req.user.userId) { - throw new CustomError(403, 'Forbidden'); - } - return next(); -}; - -async function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { + throw new CustomError(403, 'Forbidden'); // 403 금지 에러 발생 + } // 권한 확인 종료 + return next(); // 권한이 확인되면 다음 미들웨어로 진행 +}; // verifyProduectAuth 종료 - const id = req.params.id; - if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); - const idtoNumber = parseInt(id); - const article = await articleRepository.findById(idtoNumber); - if (!article) { - throw new CustomError(404, 'article not found'); - } +async function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) { // 게시글 수정/삭제 권한 확인 함수 + const id = req.params.id; // URL 파라미터에서 게시글 ID 추출 + if (!id) throw new CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 + const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 + const article = await articleRepository.findById(idtoNumber); // DB에서 해당 게시글 조회 + if (!article) { // 게시글이 존재하지 않는 경우 + throw new CustomError(404, 'article not found'); // 404 에러 발생 + } // 게시글 존재 확인 종료 + // 사용자 정보가 없거나, 게시글의 소유자 ID(현재 article.id로 되어있으나 보통 article.userId)가 요청자 ID와 다르면 권한 없음 처리 if (typeof req.user !== 'object' || req.user.userId === undefined || article.id !== req.user.userId) { - throw new CustomError(403, 'Forbidden'); - } - return next(); -} + throw new CustomError(403, 'Forbidden'); // 403 금지 에러 발생 + } // 권한 확인 종료 + return next(); // 권한이 확인되면 다음 미들웨어로 진행 +} // verifyArticleAuth 종료 export default { verifyAccessToken, From 4c2c1cb283acb01e7260ddc108489e38075aed54 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Mon, 19 Jan 2026 10:08:35 +0900 Subject: [PATCH 26/53] =?UTF-8?q?readme=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 ++++++++++++-------------------------- sprint5-README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 sprint5-README.md diff --git a/README.md b/README.md index 42012d3b..b6d83557 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,21 @@ ## 미션 목표 -- [x] 타입스크립트 마이그레이션하기 -- [x] 타입스크립트 개발 환경 세팅하기 -- [x] (심화) Layered Architecture 적용하기 +- [ ] 알림 기능 구현하기 +- [ ] 웹소켓 또는 Socket.IO를 사용해 실시간 기능 구현하기 ## 기본 요구 사항 -- [x] 스프린트 미션 4의 구현이 완료된 상태에서 진행을 권장합니다. -- [x] 타입스크립트 마이그레이션을 먼저 진행해 보고, 이전 미션에서 구현하지 못한 부분이 있다면 추가로 구현해 보세요. +- [ ] 기존에 작업한 판다마켓 미션과 이어서 진행됩니다. +- [ ] 판다마켓 최종 디자인을 참고해주세요. -## 프로젝트 세팅 +## 알림 -- [x] tsconfig.json 파일을 생성하고, 필요한 옵션을 설정해 주세요. (예: outDir). -- [x] 필요한 npm script를 설정해 주세요. (예: 빌드 및 개발 서버 실행 명령어) +- [ ] 사용자는 자신의 알림 목록을 조회할 수 있습니다. +- [ ] 사용자는 자신의 안 읽은 알림의 개수를 조회할 수 있습니다. +- [ ] 사용자는 자신의 알림을 읽음 처리 할 수 있습니다. +- [ ] 클라이언트에서는 실시간으로 알림을 받을 수 있습니다. -## 타입스크립트 마이그레이션 +## 알림 전송 -- [x] 기존 Express.js 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요. -- [x] 필요한 타입 패키지를 설치해 주세요. -- [x] any 타입의 사용은 최소화해주세요. -- [x] 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요. -- [x] 필요한 경우, 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요. -- [x] 필요한 경우, declare를 사용하여 타입을 오버라이드하거나 확장합니다. (예: req.user) - -## 개발 환경 설정 - -- [x] ts-node 를 사용해 .ts 코드를 바로 실행할 수 있는 npm script를 만들어 주세요. (예: npm run dev) -- [x] nodemon을 사용해 .ts 코드가 변경될 때마다 서버가 다시 실행되는 npm script를 만들어 주세요. (예: npm run dev) - -## 심화 요구 사항 - -- [x] Layered Architecture 적용하기 -- [x] Controller, Service, Repository로 나누어 코드를 리팩토링해 주세요. -- [x] 필요하다면, 계층 사이에서 데이터를 주고 받을 때 DTO를 활용해 주세요. +- [ ] 좋아요한 상품의 가격이 변동되었을 때 알림을 보내주세요. +- [ ] 자신이 작성한 게시글에 댓글이 달렸을 때 알림을 보내주세요. diff --git a/sprint5-README.md b/sprint5-README.md new file mode 100644 index 00000000..42012d3b --- /dev/null +++ b/sprint5-README.md @@ -0,0 +1,35 @@ +## 미션 목표 + +- [x] 타입스크립트 마이그레이션하기 +- [x] 타입스크립트 개발 환경 세팅하기 +- [x] (심화) Layered Architecture 적용하기 + +## 기본 요구 사항 + +- [x] 스프린트 미션 4의 구현이 완료된 상태에서 진행을 권장합니다. +- [x] 타입스크립트 마이그레이션을 먼저 진행해 보고, 이전 미션에서 구현하지 못한 부분이 있다면 추가로 구현해 보세요. + +## 프로젝트 세팅 + +- [x] tsconfig.json 파일을 생성하고, 필요한 옵션을 설정해 주세요. (예: outDir). +- [x] 필요한 npm script를 설정해 주세요. (예: 빌드 및 개발 서버 실행 명령어) + +## 타입스크립트 마이그레이션 + +- [x] 기존 Express.js 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요. +- [x] 필요한 타입 패키지를 설치해 주세요. +- [x] any 타입의 사용은 최소화해주세요. +- [x] 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요. +- [x] 필요한 경우, 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요. +- [x] 필요한 경우, declare를 사용하여 타입을 오버라이드하거나 확장합니다. (예: req.user) + +## 개발 환경 설정 + +- [x] ts-node 를 사용해 .ts 코드를 바로 실행할 수 있는 npm script를 만들어 주세요. (예: npm run dev) +- [x] nodemon을 사용해 .ts 코드가 변경될 때마다 서버가 다시 실행되는 npm script를 만들어 주세요. (예: npm run dev) + +## 심화 요구 사항 + +- [x] Layered Architecture 적용하기 +- [x] Controller, Service, Repository로 나누어 코드를 리팩토링해 주세요. +- [x] 필요하다면, 계층 사이에서 데이터를 주고 받을 때 DTO를 활용해 주세요. From 4e85dec2915b282cb6623eb7beec28bf6d0b702b Mon Sep 17 00:00:00 2001 From: YooInHak Date: Mon, 19 Jan 2026 10:39:24 +0900 Subject: [PATCH 27/53] =?UTF-8?q?[=EC=88=98=EC=A0=95]=20comment=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/articleController.ts | 15 ++-- src/controller/commentController.ts | 104 +++++--------------------- src/controller/productController.ts | 14 ++-- src/controller/uploadController.ts | 2 +- src/main.ts | 27 ++++++- src/repositories/commentRepository.ts | 65 ++++++++++++++++ src/services/commentService.ts | 43 +++++++++++ 7 files changed, 167 insertions(+), 103 deletions(-) create mode 100644 src/repositories/commentRepository.ts create mode 100644 src/services/commentService.ts diff --git a/src/controller/articleController.ts b/src/controller/articleController.ts index e4e04b8a..402734b6 100644 --- a/src/controller/articleController.ts +++ b/src/controller/articleController.ts @@ -40,17 +40,17 @@ export default class ArticleController { } const userId = req.user ? req.user.userId : null; - if (!userId) return new CustomError(404, "UserId Not Found"); + if (!userId) throw new CustomError(404, "UserId Not Found"); const articles = await articleService.getArticles(findOptions, userId); res.send(articles); }; getArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; - if (!userId) return new CustomError(404, "userId Not Found"); + if (!userId) throw new CustomError(404, "userId Not Found"); const article = await articleService.getArticleById(_id, userId); res.send(article); }; @@ -64,7 +64,7 @@ export default class ArticleController { patchArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); assert(req.body, PatchArticle); const { ...userFields } = req.body; @@ -74,20 +74,19 @@ export default class ArticleController { deleteArticleById: ExpressHandler = async (req, res) => { const { id } = req.params; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); const article = await articleRepository.ondelete(_id); res.send(article); }; likeArticle: ExpressHandler = async (req, res) => { - if (!req.user) return new CustomError(404, "user Not Found"); + if (!req.user) throw new CustomError(404, "user Not Found"); const userId = req.user.userId; const articleId = req.params.id; - if (!articleId) return new CustomError(404, "articleId Not Found"); + if (!articleId) throw new CustomError(404, "articleId Not Found"); const _articleId = parseInt(articleId); const result = await articleService.likeArticle(userId, _articleId); return res.status(200).json(result); }; } - diff --git a/src/controller/commentController.ts b/src/controller/commentController.ts index 678616d6..5b045410 100644 --- a/src/controller/commentController.ts +++ b/src/controller/commentController.ts @@ -1,89 +1,40 @@ -import { prismaClient, Prisma } from '../libs/constants'; import { assert } from 'superstruct'; import { CreateComment, PatchComment } from '../structs/structs'; import { ExpressRequest, ExpressResponse } from '../libs/constants'; import { CustomError } from '../libs/Handler/errorHandler'; - - +import { commentService } from '../services/commentService'; export async function GetComment(req: ExpressRequest, res: ExpressResponse) { const { take = '10', cursor, productId, articleId } = req.query; const parsedTake = parseInt(take as string, 10) || 0; if (isNaN(parsedTake) || parsedTake <= 0) { - return res.status(400).send({ error: 'Invalid "take" parameter.' }); - } - - const whereClause: Prisma.CommentWhereInput = {}; - - if (productId) { - whereClause.productId = Number(productId); // 상품 ID로 필터링 - } else if (articleId) { - whereClause.articleId = Number(articleId); // 게시글 ID로 필터링 + throw new CustomError(400, 'Invalid "take" parameter.'); } - const findOptions: Prisma.CommentFindManyArgs = { - take: parsedTake, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, - }; - - if (cursor) { - const parsedCursor = parseInt(cursor as string, 10); - - findOptions.skip = 1; - findOptions.cursor = { - id: parsedCursor, - }; - } - - const comments = await prismaClient.comment.findMany(findOptions); // - let nextCursor = null; - if (comments.length > 0 && comments.length === parsedTake) { - nextCursor = comments[comments.length - 1]!.id; - } - res.status(200).json({ - comments, - nextCursor, - }); + const parsedCursor = cursor ? parseInt(cursor as string, 10) : undefined; + const parsedProductId = productId ? Number(productId) : undefined; + const parsedArticleId = articleId ? Number(articleId) : undefined; + const result = await commentService.getComments(parsedTake, parsedCursor, parsedProductId, parsedArticleId); + res.status(200).json(result); } export async function GetCommentById(req: ExpressRequest, res: ExpressResponse) { const { id } = req.params; - const paesedId = parseInt(id as string, 10) || null; - if (!paesedId) return new CustomError(404, "id Not Found"); - const Comment = await prismaClient.comment.findUniqueOrThrow({ - where: { - id: paesedId - }, - }); - res.send(Comment); + const parsedId = parseInt(id as string, 10) || null; + if (!parsedId) throw new CustomError(404, "id Not Found"); + const comment = await commentService.getCommentById(parsedId); + res.send(comment); } export async function PostComment(req: ExpressRequest, res: ExpressResponse) { assert(req.body, CreateComment); const { content, productId, articleId } = req.body; - if ((productId && articleId) || (!productId && !articleId)) { - return res.status(400).json({ - error: 'Comment must belong to EITHER a Product OR an Article.', - }); - } - if (!content || content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - const comment = await prismaClient.comment.create({ - data: { - content: content, - productId: productId, - articleId: articleId, - }, - }); + const comment = await commentService.createComment(content, productId, articleId); res.status(201).send(comment); } @@ -91,33 +42,18 @@ export async function PatchCommentById(req: ExpressRequest, res: ExpressResponse assert(req.body, PatchComment); const { content } = req.body; const { id } = req.params; - const paesedId = parseInt(id as string, 10) || null; - if (!paesedId) return new CustomError(404, "id Not Found"); - if (content !== undefined && content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } + const parsedId = parseInt(id as string, 10) || null; + if (!parsedId) throw new CustomError(404, "id Not Found"); + if (!content) throw new CustomError(404, "Request body is empty"); - const comment = await prismaClient.comment.update({ - where: { - id: paesedId - }, - data: { - content: content, - }, - }); + const comment = await commentService.updateComment(parsedId, content); res.send(comment); } export async function DeleteCommentById(req: ExpressRequest, res: ExpressResponse) { const { id } = req.params; - const paesedId = parseInt(id as string, 10) || null; - if (!paesedId) return new CustomError(404, "id Not Found"); - const Comment = await prismaClient.comment.delete({ - where: { - id: paesedId - }, - }); - res.send(Comment); + const parsedId = parseInt(id as string, 10) || null; + if (!parsedId) throw new CustomError(404, "id Not Found"); + const comment = await commentService.deleteComment(parsedId); + res.send(comment); } - - diff --git a/src/controller/productController.ts b/src/controller/productController.ts index 2baf0022..67df74a4 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -42,17 +42,17 @@ export default class ProductController { } const userId = req.user ? req.user.userId : null; - if (!userId) return new CustomError(404, "UserId Not Found"); + if (!userId) throw new CustomError(404, "UserId Not Found"); const products = await productService.getProducts(findOptions, userId); res.send(products); }; GetProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; - if (!userId) return new CustomError(404, "userId Not Found"); + if (!userId) throw new CustomError(404, "userId Not Found"); const product = await productService.getProductById(_id, userId); res.send(product); }; @@ -66,7 +66,7 @@ export default class ProductController { PatchProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); assert(req.body, PatchProduct); const userFields = removeUndefined(req.body); @@ -76,17 +76,17 @@ export default class ProductController { DeleteProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) return new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); const Product = await productRepository.ondelete(_id); res.send(Product); }; likeProduct: ExpressHandler = async (req, res) => { - if (!req.user) return new CustomError(404, "user Not Found"); + if (!req.user) throw new CustomError(404, "user Not Found"); const userId = req.user.userId; const productId = req.params.id; - if (!productId) return new CustomError(404, "productId Not Found"); + if (!productId) throw new CustomError(404, "productId Not Found"); const _productId = parseInt(productId); const result = await productService.likeProduct(userId, _productId); return res.status(200).json(result); diff --git a/src/controller/uploadController.ts b/src/controller/uploadController.ts index 46dde273..00ddda00 100644 --- a/src/controller/uploadController.ts +++ b/src/controller/uploadController.ts @@ -3,7 +3,7 @@ import { ExpressRequest, ExpressResponse } from '../libs/constants'; import { CustomError } from '../libs/Handler/errorHandler'; export function UploadSingleImage(req: ExpressRequest, res: ExpressResponse) { if (!req.file) - return new CustomError(404, "file not found"); + throw new CustomError(404, "file not found"); const { filename } = req.file; const path = `files/${filename}`; res.json({ path }); diff --git a/src/main.ts b/src/main.ts index 44162a8b..4bf507e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,9 +4,19 @@ import { RouterManager } from './Routers/routerManager'; import { getCorsOrigin } from './libs/corsSetUp'; import errorHandler from './libs/Handler/errorHandler'; import { expressjwt } from 'express-jwt'; +import { createServer } from 'http'; +import { Server } from 'socket.io'; const app = EXPRESS(); - +const httpServer = createServer(app); +const io = new Server(httpServer, { + cors: { + origin: getCorsOrigin(), // Express에서 쓰던 것과 똑같이 맞춰주세요 + methods: ["GET", "POST"], + credentials: true + } +}); +app.set('io', io); app.use(cors({ origin: getCorsOrigin(), @@ -30,6 +40,17 @@ app.use('/', RouterManager); app.use(errorHandler); -app.listen(PORT, () => { - console.log(`Server is running`); +// --- 소켓 연결 이벤트 (테스트용) --- +io.on('connection', (socket) => { + console.log('새로운 소켓 연결:', socket.id); + + // 로그인하면 해당 유저의 ID로 방을 만들어줌 (알림 기능을 위해 필수) + socket.on('join', (userId) => { + socket.join(userId); + console.log(`User ${userId} joined room`); + }); +}); + +httpServer.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); }); \ No newline at end of file diff --git a/src/repositories/commentRepository.ts b/src/repositories/commentRepository.ts new file mode 100644 index 00000000..ee7a1b92 --- /dev/null +++ b/src/repositories/commentRepository.ts @@ -0,0 +1,65 @@ +import { prismaClient, Prisma } from '../libs/constants'; + +async function findAll(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined) { + const whereClause: Prisma.CommentWhereInput = {}; + + if (productId) { + whereClause.productId = productId; + } else if (articleId) { + whereClause.articleId = articleId; + } + + const findOptions: Prisma.CommentFindManyArgs = { + take: take, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, + }; + } + + return prismaClient.comment.findMany(findOptions); +} + +async function findById(id: number) { + return prismaClient.comment.findUniqueOrThrow({ + where: { id }, + }); +} + +async function create(content: string, productId?: number, articleId?: number) { + return prismaClient.comment.create({ + data: { + content, + productId, + articleId, + }, + }); +} + +async function update(id: number, content: string) { + return prismaClient.comment.update({ + where: { id }, + data: { content }, + }); +} + +async function deleteComment(id: number) { + return prismaClient.comment.delete({ + where: { id }, + }); +} + +export default { + findAll, + findById, + create, + update, + delete: deleteComment, +}; \ No newline at end of file diff --git a/src/services/commentService.ts b/src/services/commentService.ts new file mode 100644 index 00000000..55f7d788 --- /dev/null +++ b/src/services/commentService.ts @@ -0,0 +1,43 @@ +import commentRepository from '../repositories/commentRepository'; +import { CustomError } from '../libs/Handler/errorHandler'; + +class CommentService { + async getComments(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined) { + const comments = await commentRepository.findAll(take, cursor, productId, articleId); + let nextCursor = null; + if (comments.length > 0 && comments.length === take) { + nextCursor = comments[comments.length - 1]!.id; + } + + return { comments, nextCursor }; + } + + async getCommentById(id: number) { + return await commentRepository.findById(id); + } + + async createComment(content: string, productId?: number, articleId?: number) { + if ((productId && articleId) || (!productId && !articleId)) { + throw new CustomError(400, 'Comment must belong to EITHER a Product OR an Article.'); + } + if (!content || content.trim() === '') { + throw new CustomError(400, 'Content cannot be empty.'); + } + + return await commentRepository.create(content, productId, articleId); + } + + async updateComment(id: number, content: string) { + if (content !== undefined && content.trim() === '') { + throw new CustomError(400, 'Content cannot be empty.'); + } + + return await commentRepository.update(id, content); + } + + async deleteComment(id: number) { + return await commentRepository.delete(id); + } +} + +export const commentService = new CommentService(); \ No newline at end of file From 968ff8db1cd9d719560232b62a3648f07b802097 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Mon, 19 Jan 2026 18:58:57 +0900 Subject: [PATCH 28/53] =?UTF-8?q?Layer=EC=BD=94=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20,=20=EB=8C=93=EA=B8=80=20=EB=8B=AC=EB=A6=AC?= =?UTF-8?q?=EB=A9=B4=20=EC=95=8C=EB=9E=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/api-test.html | 437 ++++++++++++++++++++++++++ src/Routers/routerManager.ts | 2 +- src/Routers/userRouter.ts | 2 +- src/controller/articleController.ts | 13 +- src/controller/commentController.ts | 15 +- src/controller/productController.ts | 13 +- src/libs/interfaces.ts | 4 + src/main.ts | 23 +- src/repositories/commentRepository.ts | 31 +- src/services/articleService.ts | 22 +- src/services/commentService.ts | 39 ++- src/services/productService.ts | 14 +- 12 files changed, 597 insertions(+), 18 deletions(-) create mode 100644 public/api-test.html diff --git a/public/api-test.html b/public/api-test.html new file mode 100644 index 00000000..642ac315 --- /dev/null +++ b/public/api-test.html @@ -0,0 +1,437 @@ + + + + + + Panda Market API Tester + + + +

🐼 Panda Market API Tester

+ +
+
+ +
+

🔐 Auth (인증)

+
+ + + + + +
+

Access Token

+
+ +
+ +
+ + +
+

📝 Articles (자유게시판)

+
+ +
+

Create / Update

+
+ + +
+
+ +
+

Actions by ID

+
+ + + + + +
+
+ + +
+

🛍️ Products (중고마켓)

+
+ +
+

Create / Update

+
+ + + +
+
+ +
+
+ +
+

Actions by ID

+
+ + + + + +
+
+ + +
+

💬 Comments (댓글)

+
+ + + +
+
+ + +
+
+
+ +
+

📡 Logs & Response

+ +
+
+
+ + + + + diff --git a/src/Routers/routerManager.ts b/src/Routers/routerManager.ts index 79aab70c..a390ba36 100644 --- a/src/Routers/routerManager.ts +++ b/src/Routers/routerManager.ts @@ -14,4 +14,4 @@ RouterManager.use('/products', productRouter); RouterManager.use('/articles', articleRouter); RouterManager.use('/comments', commentRouter); RouterManager.use('/files', uploadRouter); -RouterManager.use('/user', userRouter); +RouterManager.use('/auth', userRouter); diff --git a/src/Routers/userRouter.ts b/src/Routers/userRouter.ts index 320351dd..b0ab8cc9 100644 --- a/src/Routers/userRouter.ts +++ b/src/Routers/userRouter.ts @@ -7,7 +7,7 @@ const userRouter = EXPRESS.Router(); const userController = new UserServiceController(); -userRouter.post('/', catchAsync(userController.register)); +userRouter.post('/signup', catchAsync(userController.register)); userRouter.post('/login', catchAsync(userController.login)); userRouter.post('/token/refresh', auth.verifyRefreshToken, catchAsync(userController.refresh)); diff --git a/src/controller/articleController.ts b/src/controller/articleController.ts index 402734b6..c0754062 100644 --- a/src/controller/articleController.ts +++ b/src/controller/articleController.ts @@ -1,7 +1,6 @@ import { articleService } from '../services/articleService'; import { assert } from 'superstruct'; import { CreateArticle, PatchArticle } from '../structs/structs'; -import articleRepository from '../repositories/articleRepository'; import { ExpressHandler } from '../libs/constants'; import { CustomError } from '../libs/Handler/errorHandler'; @@ -58,7 +57,9 @@ export default class ArticleController { postArticle: ExpressHandler = async (req, res) => { assert(req.body, CreateArticle); const { ...userFields } = req.body; - const article = await articleRepository.create(userFields); + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, 'Unauthorized'); + const article = await articleService.postArticle({ ...userFields, userId }); res.status(201).send(article); }; @@ -68,7 +69,9 @@ export default class ArticleController { const _id = parseInt(id); assert(req.body, PatchArticle); const { ...userFields } = req.body; - const article = await articleRepository.update(_id, userFields); + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, 'Unauthorized'); + const article = await articleService.patchArticleById(_id, { ...userFields, userId }); res.send(article); }; @@ -76,7 +79,9 @@ export default class ArticleController { const { id } = req.params; if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); - const article = await articleRepository.ondelete(_id); + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, 'Unauthorized'); + const article = await articleService.deleteArticleById(_id, userId); res.send(article); }; diff --git a/src/controller/commentController.ts b/src/controller/commentController.ts index 5b045410..2f197b9b 100644 --- a/src/controller/commentController.ts +++ b/src/controller/commentController.ts @@ -33,8 +33,21 @@ export async function GetCommentById(req: ExpressRequest, res: ExpressResponse) export async function PostComment(req: ExpressRequest, res: ExpressResponse) { assert(req.body, CreateComment); const { content, productId, articleId } = req.body; + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, 'Unauthorized'); + // 서비스 호출 (userId 추가됨) + const { comment, notification } = await commentService.createComment(userId, content, productId, articleId); - const comment = await commentService.createComment(content, productId, articleId); + // [알림 발송] 알림이 생성되었다면 소켓으로 전송 + if (notification) { + const io = req.app.get('io'); // app.js에서 설정한 io 객체 가져오기 + if (io) { + // 알림 받을 사람(notification.userId)에게만 전송 + io.to(notification.userId).emit('notification', notification); + } + } + + // 클라이언트에는 댓글 정보만 주거나, 필요하면 알림 생성 여부도 줄 수 있음 res.status(201).send(comment); } diff --git a/src/controller/productController.ts b/src/controller/productController.ts index 67df74a4..319d23a4 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -1,7 +1,6 @@ import { productService } from '../services/productService'; import { assert } from 'superstruct'; import { CreateProduct, PatchProduct } from '../structs/structs'; -import productRepository from '../repositories/productRepository'; import { ExpressHandler } from '../libs/constants'; import { CustomError } from '../libs/Handler/errorHandler'; import { removeUndefined } from './../libs/removeTool'; @@ -60,7 +59,9 @@ export default class ProductController { PostProduct: ExpressHandler = async (req, res) => { assert(req.body, CreateProduct); const { ...userFields } = req.body; - const product = await productRepository.create(userFields); + const userId = req.user ? req.user.userId : null; + if (!userId) throw new CustomError(404, "userId Not Found"); + const product = await productService.createProducts({ ...userFields, userId }); res.status(201).send(product); }; @@ -68,9 +69,11 @@ export default class ProductController { const id = req.params.id; if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); + const userId = req.user ? req.user.userId : null; + if (!userId) throw new CustomError(404, "userId Not Found"); assert(req.body, PatchProduct); const userFields = removeUndefined(req.body); - const Product = await productRepository.update(_id, userFields); + const Product = await productService.updateProduct(_id, userFields); res.send(Product); }; @@ -78,7 +81,9 @@ export default class ProductController { const id = req.params.id; if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); - const Product = await productRepository.ondelete(_id); + const userId = req.user ? req.user.userId : null; + if (!userId) throw new CustomError(404, "userId Not Found"); + const Product = await productService.deleteProduct(_id); res.send(Product); }; diff --git a/src/libs/interfaces.ts b/src/libs/interfaces.ts index 6ef9ffe2..c0b8b70c 100644 --- a/src/libs/interfaces.ts +++ b/src/libs/interfaces.ts @@ -4,6 +4,7 @@ import { Prisma } from './../libs/constants'; export interface ProductType { id: number; name: string; + userId: number; description?: string; price: number; tags: string[]; @@ -15,6 +16,7 @@ export interface ProductType { export interface ArticleType { id: number; title: string; + userId: number; content: string; createdAt: Date; comments?: CommentType[]; @@ -25,8 +27,10 @@ export interface CommentType { content: string; createdAt: Date; updatedAt: Date; + userId: number; productId?: number; articleId?: number; + user: UserType; product?: ProductType; article?: ArticleType; } diff --git a/src/main.ts b/src/main.ts index 4bf507e3..777a2dc5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,8 @@ import errorHandler from './libs/Handler/errorHandler'; import { expressjwt } from 'express-jwt'; import { createServer } from 'http'; import { Server } from 'socket.io'; +import jwt from 'jsonwebtoken'; + const app = EXPRESS(); const httpServer = createServer(app); @@ -16,6 +18,22 @@ const io = new Server(httpServer, { credentials: true } }); + +io.use((socket, next) => { + const token = socket.handshake.auth.accessToken; + if (!token) { + return next(new Error('Authentication error')); + } + try { + const payload = jwt.verify(token, process.env.JWT_SECRET!) as { userId: number; }; + socket.data.userId = payload.userId; + next(); + } catch (e) { + next(new Error('Authentication error')); + } +}); + + app.set('io', io); app.use(cors({ @@ -45,10 +63,11 @@ io.on('connection', (socket) => { console.log('새로운 소켓 연결:', socket.id); // 로그인하면 해당 유저의 ID로 방을 만들어줌 (알림 기능을 위해 필수) - socket.on('join', (userId) => { + const userId = socket.data.userId; + if (userId) { socket.join(userId); console.log(`User ${userId} joined room`); - }); + } }); httpServer.listen(PORT, () => { diff --git a/src/repositories/commentRepository.ts b/src/repositories/commentRepository.ts index ee7a1b92..4212ca10 100644 --- a/src/repositories/commentRepository.ts +++ b/src/repositories/commentRepository.ts @@ -33,10 +33,11 @@ async function findById(id: number) { }); } -async function create(content: string, productId?: number, articleId?: number) { +async function create(content: string, userId: number, productId?: number, articleId?: number) { return prismaClient.comment.create({ data: { content, + userId, productId, articleId, }, @@ -55,11 +56,39 @@ async function deleteComment(id: number) { where: { id }, }); } +// [추가] 상품 주인 찾기 +async function findProductOwner(productId: number) { + return prismaClient.product.findUnique({ + where: { id: productId }, + select: { userId: true, name: true } // 주인 ID와 상품명만 가져옴 + }); +} + +// [추가] 아티클 주인 찾기 +async function findArticleOwner(articleId: number) { + return prismaClient.article.findUnique({ + where: { id: articleId }, + select: { userId: true, title: true } // 주인 ID와 제목만 가져옴 + }); +} +// [추가] 알림 생성 +async function createNotification(userId: number, content: string) { + return prismaClient.notification.create({ + data: { + userId, + content, + isRead: false, + } + }); +} export default { findAll, findById, create, update, + findProductOwner, + findArticleOwner, + createNotification, delete: deleteComment, }; \ No newline at end of file diff --git a/src/services/articleService.ts b/src/services/articleService.ts index a3a41c2e..88203f48 100644 --- a/src/services/articleService.ts +++ b/src/services/articleService.ts @@ -1,6 +1,8 @@ +import { Prisma } from '@prisma/client'; import articleLikeRepository from '../repositories/articleLikeRepository'; import articleRepository from '../repositories/articleRepository'; -import { ArticleFindOptions } from './../libs/interfaces'; +import { ArticleFindOptions, ArticlePublicData, UpdateArticleData } from './../libs/interfaces'; +import { CustomError } from '../libs/Handler/errorHandler'; class ArticleService { async likeArticle(userId: number, articleId: number) { @@ -28,6 +30,24 @@ class ArticleService { return { ...rest, isLiked: articleLikes.length > 0 }; }); } + async postArticle(userFields: ArticlePublicData) { + return await articleRepository.create(userFields); + } + async patchArticleById(id: number, userFields: UpdateArticleData) { + const article = await articleRepository.findById(id); + if (article.userId !== userFields.userId) { + throw new CustomError(403, "권한이 없습니다."); + } + return await articleRepository.update(id, userFields); + } + async deleteArticleById(id: number, userId: number) { + const article = await articleRepository.findById(id); + if (article.userId !== userId) { + throw new CustomError(403, "권한이 없습니다."); + } + + return await articleRepository.ondelete(id); + } } export const articleService = new ArticleService(); diff --git a/src/services/commentService.ts b/src/services/commentService.ts index 55f7d788..63079bb0 100644 --- a/src/services/commentService.ts +++ b/src/services/commentService.ts @@ -16,7 +16,7 @@ class CommentService { return await commentRepository.findById(id); } - async createComment(content: string, productId?: number, articleId?: number) { + async createComment(userId: number, content: string, productId?: number, articleId?: number) { if ((productId && articleId) || (!productId && !articleId)) { throw new CustomError(400, 'Comment must belong to EITHER a Product OR an Article.'); } @@ -24,7 +24,42 @@ class CommentService { throw new CustomError(400, 'Content cannot be empty.'); } - return await commentRepository.create(content, productId, articleId); + // 2. 댓글 생성 (DB 저장) + const comment = await commentRepository.create(content, userId, productId, articleId); + + // 3. 알림 로직 시작 + let notification = null; + let ownerId: number | undefined; + let targetName = ""; + + // 상품 댓글인 경우 + if (productId) { + const product = await commentRepository.findProductOwner(productId); + if (product) { + ownerId = product.userId; // 상품 주인 ID + targetName = product.name; + } + } + // 아티클 댓글인 경우 + else if (articleId) { + const article = await commentRepository.findArticleOwner(articleId); + if (article) { + ownerId = article.userId; // 아티클 주인 ID + targetName = article.title; + } + } + + // 4. 작성자가 본인이 아닐 경우에만 알림 생성 + // (ownerId가 존재하고, 댓글 쓴 사람(userId)과 다를 때) + if (ownerId) {//&& ownerId !== userId) { + const message = `작성하신 글 '${targetName}'에 새로운 댓글이 달렸습니다: ${content}`; + + // DB에 알림 저장 + notification = await commentRepository.createNotification(ownerId, message); + } + + // 5. 댓글 정보와 알림 정보(있으면)를 함께 반환 + return { comment, notification }; } async updateComment(id: number, content: string) { diff --git a/src/services/productService.ts b/src/services/productService.ts index 0a116d9e..79c3847d 100644 --- a/src/services/productService.ts +++ b/src/services/productService.ts @@ -1,6 +1,7 @@ import productRepository from '../repositories/productRepository'; import productLikeRepository from '../repositories/productLikeRepository'; -import { ProductFindOptions } from '../libs/interfaces'; +import { ProductFindOptions, ProductPublicData } from '../libs/interfaces'; +import { Prisma } from '@prisma/client'; class ProductService { @@ -29,6 +30,17 @@ class ProductService { return { ...rest, isLiked: productLikes.length > 0 }; }); } + async createProducts(userFields: ProductPublicData) { + const product = await productRepository.create(userFields); + return product; + } + async updateProduct(id: number, userFields: ProductPublicData) { + return await productRepository.update(id, userFields); + } + async deleteProduct(id: number) { + return await productRepository.ondelete(id); + } + } export const productService = new ProductService(); From d09a70d2531f8335fda9112aa37f8e40f68ad5d1 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 20 Jan 2026 18:29:05 +0900 Subject: [PATCH 29/53] =?UTF-8?q?[=EC=B6=94=EA=B0=80]=20=EA=B0=80=EA=B2=A9?= =?UTF-8?q?=20=EB=B3=80=EB=8F=99=EC=8B=9C=20=EC=95=8C=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/productController.ts | 15 +++++++++++++-- src/repositories/productRepository.ts | 27 ++++++++++++++++++++++++--- src/services/productService.ts | 22 +++++++++++++++++++++- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/controller/productController.ts b/src/controller/productController.ts index 319d23a4..ad9d687e 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -69,12 +69,23 @@ export default class ProductController { const id = req.params.id; if (!id) throw new CustomError(404, "id Not Found"); const _id = parseInt(id); + const userId = req.user ? req.user.userId : null; if (!userId) throw new CustomError(404, "userId Not Found"); + assert(req.body, PatchProduct); const userFields = removeUndefined(req.body); - const Product = await productService.updateProduct(_id, userFields); - res.send(Product); + const { product, notifications } = await productService.updateProduct(_id, userFields); + if (notifications && notifications.length > 0) { + const io = req.app.get('io'); + if (io) { + notifications.forEach((noti) => { + io.to(noti.userId).emit('notification', noti); + }); + } + } + + res.send(product); }; DeleteProductById: ExpressHandler = async (req, res) => { diff --git a/src/repositories/productRepository.ts b/src/repositories/productRepository.ts index b83cb099..879fe48c 100644 --- a/src/repositories/productRepository.ts +++ b/src/repositories/productRepository.ts @@ -2,8 +2,26 @@ import { prismaClient } from '../libs/constants'; import { ProductPublicData, ProductFindOptions, UpdateProductData } from './../libs/interfaces'; - - +async function findByIdSimple(id: number) { + return prismaClient.product.findUnique({ + where: { id }, + }); +} +async function findLikers(productId: number) { + return prismaClient.productLike.findMany({ + where: { productId }, + select: { userId: true } // 유저 ID만 쏙 뽑아옵니다. + }); +} +async function createNotification(userId: number, content: string) { + return prismaClient.notification.create({ + data: { + userId, + content, + isRead: false, // 안 읽음 상태로 생성 + } + }); +} async function findByUserId(userId: number) { return prismaClient.product.findMany({ where: { @@ -67,5 +85,8 @@ export default { update, create, ondelete, - findByUserId + findByUserId, + findByIdSimple, + findLikers, + createNotification }; diff --git a/src/services/productService.ts b/src/services/productService.ts index 79c3847d..0c617960 100644 --- a/src/services/productService.ts +++ b/src/services/productService.ts @@ -35,7 +35,27 @@ class ProductService { return product; } async updateProduct(id: number, userFields: ProductPublicData) { - return await productRepository.update(id, userFields); + const oldProduct = await productRepository.findByIdSimple(id); + + const updatedProduct = await productRepository.update(id, userFields); + // 3. 알림 로직: 가격이 존재하고, 이전 가격과 다를 때 + let notifications: any[] = []; // 컨트롤러로 보낼 알림 목록 + + if (oldProduct && userFields.price !== undefined && oldProduct.price !== userFields.price) { + // 3-1. 찜한 유저들 찾기 + const likers = await productRepository.findLikers(id); + + // 3-2. 각 유저에게 알림 DB 저장 (Promise.all로 병렬 처리) + notifications = await Promise.all( + likers.map(async (liker) => { + const message = `찜한 상품 '${updatedProduct.name}'의 가격이 변경되었습니다. (${oldProduct.price}원 -> ${updatedProduct.price}원)`; + return await productRepository.createNotification(liker.userId, message); + }) + ); + } + + // 4. 결과 반환 (기존에는 product만 줬지만, 이제 알림 목록도 같이 줌) + return { product: updatedProduct, notifications }; } async deleteProduct(id: number) { return await productRepository.ondelete(id); From 68a5b62202ca2c577b09c0f5596ada05cad4bd11 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 20 Jan 2026 18:34:39 +0900 Subject: [PATCH 30/53] =?UTF-8?q?[=EC=B6=94=EA=B0=80]=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Routers/notificationRouter.ts | 13 ++++++++ src/Routers/routerManager.ts | 3 +- src/controller/notificationController.ts | 38 ++++++++++++++++++++++ src/main.ts | 1 - src/repositories/notificationRepository.ts | 37 +++++++++++++++++++++ src/services/notificationService.ts | 33 +++++++++++++++++++ 6 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/Routers/notificationRouter.ts create mode 100644 src/controller/notificationController.ts create mode 100644 src/repositories/notificationRepository.ts create mode 100644 src/services/notificationService.ts diff --git a/src/Routers/notificationRouter.ts b/src/Routers/notificationRouter.ts new file mode 100644 index 00000000..6e351b95 --- /dev/null +++ b/src/Routers/notificationRouter.ts @@ -0,0 +1,13 @@ +import { Router } from 'express'; +import NotificationController from '../controller/notificationController'; +import auth from '../middlewares/auth'; + +const router = Router(); +const controller = new NotificationController(); + + +router.get('/', auth.softVerifyAccessToken, controller.GetNotifications); +router.get('/count', auth.softVerifyAccessToken, controller.GetUnreadCount); +router.patch('/:id/read', auth.verifyAccessToken, controller.ReadNotification); + +export default router; \ No newline at end of file diff --git a/src/Routers/routerManager.ts b/src/Routers/routerManager.ts index a390ba36..e0ad5110 100644 --- a/src/Routers/routerManager.ts +++ b/src/Routers/routerManager.ts @@ -4,7 +4,7 @@ import articleRouter from './articleRouter'; import commentRouter from './commentRouter'; import uploadRouter from './uploadRouter'; import userRouter from './userRouter'; - +import notificationRouter from './notificationRouter'; export const RouterManager = EXPRESS.Router(); @@ -15,3 +15,4 @@ RouterManager.use('/articles', articleRouter); RouterManager.use('/comments', commentRouter); RouterManager.use('/files', uploadRouter); RouterManager.use('/auth', userRouter); +RouterManager.use('/notifications', notificationRouter); \ No newline at end of file diff --git a/src/controller/notificationController.ts b/src/controller/notificationController.ts new file mode 100644 index 00000000..93332a80 --- /dev/null +++ b/src/controller/notificationController.ts @@ -0,0 +1,38 @@ +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +import { CustomError } from '../libs/Handler/errorHandler'; +import { notificationService } from '../services/notificationService'; + +export default class NotificationController { + + // GET /notifications + GetNotifications = async (req: ExpressRequest, res: ExpressResponse) => { + const userId = req.user?.userId; // 미들웨어에서 userId 가져오기 + if (!userId) throw new CustomError(401, "Unauthorized"); + + const notifications = await notificationService.getNotifications(userId); + res.status(200).send(notifications); + }; + + // GET /notifications/count + GetUnreadCount = async (req: ExpressRequest, res: ExpressResponse) => { + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, "Unauthorized"); + + const result = await notificationService.getUnreadCount(userId); + res.status(200).send(result); + }; + + // PATCH /notifications/:id/read + ReadNotification = async (req: ExpressRequest, res: ExpressResponse) => { + const { id } = req.params; + if (!id) throw new CustomError(400, "Missing notification ID"); + const notificationId = parseInt(id, 10); + if (!notificationId) throw new CustomError(400, "Invalid notification ID"); + + const userId = req.user?.userId; + if (!userId) throw new CustomError(401, "Unauthorized"); + + const result = await notificationService.readNotification(userId, notificationId); + res.status(200).send(result); + }; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 777a2dc5..0a12d5d7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,6 @@ import { createServer } from 'http'; import { Server } from 'socket.io'; import jwt from 'jsonwebtoken'; - const app = EXPRESS(); const httpServer = createServer(app); const io = new Server(httpServer, { diff --git a/src/repositories/notificationRepository.ts b/src/repositories/notificationRepository.ts new file mode 100644 index 00000000..16df3b1a --- /dev/null +++ b/src/repositories/notificationRepository.ts @@ -0,0 +1,37 @@ +import { prismaClient } from '../libs/constants'; + +async function findAll(userId: number) { + return prismaClient.notification.findMany({ + where: { userId }, + orderBy: { createdAt: 'desc' }, + }); +} + +async function countUnread(userId: number) { + return prismaClient.notification.count({ + where: { + userId, + isRead: false, + }, + }); +} + +async function findById(id: number) { + return prismaClient.notification.findUnique({ + where: { id }, + }); +} + +async function markAsRead(id: number) { + return prismaClient.notification.update({ + where: { id }, + data: { isRead: true }, + }); +} + +export default { + findAll, + countUnread, + findById, + markAsRead, +}; \ No newline at end of file diff --git a/src/services/notificationService.ts b/src/services/notificationService.ts new file mode 100644 index 00000000..e8b0f329 --- /dev/null +++ b/src/services/notificationService.ts @@ -0,0 +1,33 @@ +import notificationRepository from '../repositories/notificationRepository'; +import { CustomError } from '../libs/Handler/errorHandler'; + +class NotificationService { + async getNotifications(userId: number) { + return await notificationRepository.findAll(userId); + } + + async getUnreadCount(userId: number) { + const count = await notificationRepository.countUnread(userId); + return { count }; + } + + async readNotification(userId: number, notificationId: number) { + const notification = await notificationRepository.findById(notificationId); + + if (!notification) { + throw new CustomError(404, 'Notification not found'); + } + + if (notification.userId !== userId) { + throw new CustomError(403, 'Forbidden: Not your notification'); + } + + if (notification.isRead) { + return notification; + } + + return await notificationRepository.markAsRead(notificationId); + } +} + +export const notificationService = new NotificationService(); \ No newline at end of file From a5ca40b7d3d818d90c49c2cfefe67f499949264f Mon Sep 17 00:00:00 2001 From: YooInHak Date: Tue, 20 Jan 2026 18:36:00 +0900 Subject: [PATCH 31/53] =?UTF-8?q?[=EC=88=98=EC=A0=95]=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=94=84=EB=A1=A0=ED=8A=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/api-test.html | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/public/api-test.html b/public/api-test.html index 642ac315..fabff80f 100644 --- a/public/api-test.html +++ b/public/api-test.html @@ -183,6 +183,35 @@

Access Token

+ +
+

👤 User Profile (마이페이지)

+
+ + +
+

Update Profile

+
+ + +
+

Change Password

+
+ + + + +
+
+

📝 Articles (자유게시판)

@@ -259,6 +288,20 @@

💬 Comments (댓글)

+ + +
+

🔔 Notifications (알림)

+
+ + +
+

Mark as Read

+
+ + +
+
@@ -342,6 +385,20 @@

📡 Logs & Response

if (socket) socket.disconnect(); } + // User Profile + const getMe = () => req("/auth/me"); + const getMyProducts = () => req("/auth/me/products"); + const updateMe = () => + req("/auth/me", "PATCH", { + nickname: document.getElementById("me-nickname").value, + }); + const updatePassword = () => + req("/auth/me/password", "PATCH", { + currentPassword: document.getElementById("me-old-pw").value, + newPassword: document.getElementById("me-new-pw").value, + confirmNewPassword: document.getElementById("me-confirm-pw").value, + }); + // Socket function initSocket(userId) { const token = document.getElementById("access-token").value; @@ -422,6 +479,15 @@

📡 Logs & Response

req("/comments", "POST", body); }; + // Notifications + const getNotifications = () => req("/notifications"); + const getUnreadCount = () => req("/notifications/count"); + const readNotification = () => + req( + `/notifications/${document.getElementById("noti-id").value}/read`, + "PATCH", + ); + // Init const savedToken = localStorage.getItem("accessToken"); if (savedToken) { From d7e0ffad55922c4b5bb7710419eebb561b7b7363 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 1 Feb 2026 14:53:23 +0900 Subject: [PATCH 32/53] =?UTF-8?q?=EB=AF=B8=EC=85=988=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EC=88=98=EC=A0=95,=20=EB=AF=B8=EC=85=989=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +- jest.config.js | 10 + package-lock.json | 6582 +++++++++++++++++++++++---- package.json | 5 + sprint8-README.md | 21 + src/controller/productController.ts | 10 +- src/main.ts | 4 +- src/services/commentService.ts | 2 +- src/services/productService.ts | 4 +- test/article.test.ts | 68 + test/auth.test.ts | 44 + test/product.test.ts | 79 + test/productService.test.ts | 82 + tsconfig.json | 17 - 14 files changed, 5963 insertions(+), 989 deletions(-) create mode 100644 jest.config.js create mode 100644 sprint8-README.md create mode 100644 test/article.test.ts create mode 100644 test/auth.test.ts create mode 100644 test/product.test.ts create mode 100644 test/productService.test.ts diff --git a/README.md b/README.md index b6d83557..aa34f7d5 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ ## 미션 목표 -- [ ] 알림 기능 구현하기 -- [ ] 웹소켓 또는 Socket.IO를 사용해 실시간 기능 구현하기 +- [x] Jest와 Supertest를 사용해 유닛 테스트, 통합 테스트 작성하기 ## 기본 요구 사항 -- [ ] 기존에 작업한 판다마켓 미션과 이어서 진행됩니다. -- [ ] 판다마켓 최종 디자인을 참고해주세요. +- [x] Jest의 테스트 커버리지 도구를 사용하도록 설정해 주세요. -## 알림 +- [x] 인증이 필요하지 않은 상품 API에 대한 통합 테스트를 작성해 주세요. -- [ ] 사용자는 자신의 알림 목록을 조회할 수 있습니다. -- [ ] 사용자는 자신의 안 읽은 알림의 개수를 조회할 수 있습니다. -- [ ] 사용자는 자신의 알림을 읽음 처리 할 수 있습니다. -- [ ] 클라이언트에서는 실시간으로 알림을 받을 수 있습니다. +- [x] 인증이 필요하지 않은 게시글 API에 대한 통합 테스트를 작성해 주세요. -## 알림 전송 +- [x] 로그인, 회원가입 API에 대한 통합 테스트를 작성해 주세요. -- [ ] 좋아요한 상품의 가격이 변동되었을 때 알림을 보내주세요. -- [ ] 자신이 작성한 게시글에 댓글이 달렸을 때 알림을 보내주세요. +- [x] 인증이 필요한 상품 API에 대해 통합 테스트를 작성해 주세요. + +- [x] 인증이 필요한 게시글 API에 대해 통합 테스트를 작성해 주세요. + +## 심화 요구사항 + +- [x] 상품 API의 비즈니스 로직에 대해 Mock, Spy를 활용해 유닛 테스트를 작성해 주세요. diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..bad57bd4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,10 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + collectCoverage: true, + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: ['/node_modules/', '/dist/', '/tests/'], + testMatch: ['**/*.test.ts'], + verbose: true, +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 11f94128..13bc0ff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,1454 +28,4885 @@ "@types/dotenv": "^6.1.1", "@types/express": "^5.0.6", "@types/is-email": "^1.0.0", + "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", "@types/node": "^24.10.1", "@types/socket.io": "^3.0.1", + "@types/supertest": "^6.0.3", "@types/ws": "^8.18.1", + "jest": "^30.2.0", "nodemon": "^3.1.11", "prisma": "^6.18.0", + "supertest": "^7.2.2", + "ts-jest": "^29.4.6", "ts-node": "^10.9.2", "typescript": "^5.9.3" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@prisma/client": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", - "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" + "engines": { + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@prisma/config": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", - "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", - "devOptional": true, - "license": "Apache-2.0", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@prisma/debug": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", - "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", - "devOptional": true, - "license": "Apache-2.0" + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/@prisma/engines": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", - "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/fetch-engine": "6.18.0", - "@prisma/get-platform": "6.18.0" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@prisma/engines-version": { - "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", - "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", - "devOptional": true, - "license": "Apache-2.0" + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@prisma/fetch-engine": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", - "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/get-platform": "6.18.0" + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@prisma/get-platform": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", - "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", - "devOptional": true, - "license": "Apache-2.0", + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", "dependencies": { - "@prisma/debug": "6.18.0" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", - "devOptional": true, - "license": "MIT" + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@types/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/dotenv": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", - "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", - "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@types/is-email": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/is-email/-/is-email-1.0.0.tgz", - "integrity": "sha512-b/76ooKpYY/b+oPrOuc/pmM5eag+ZlzctPsKcRCIKs+TFzh0FL58OeXtSPkbXt3uKNK84YCKHmjnoREtwve5Kg==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { - "@types/ms": "*", - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/multer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", - "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "license": "MIT", "dependencies": { - "@types/express": "*" + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/socket.io": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.1.tgz", - "integrity": "sha512-XSma2FhVD78ymvoxYV4xGXrIH/0EKQ93rR+YR0Y+Kw1xbPzLDCip/UWSejZ08FpxYeYNci/PZPQS9anrvJRqMA==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", "dependencies": { - "socket.io": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">= 0.6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.11.0" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">=12" } }, - "node_modules/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", - "hasInstallScript": true, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">= 18" + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" + "tslib": "^2.4.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "fill-range": "^7.1.1" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", "dependencies": { - "streamsearch": "^1.1.0" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=10.16.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">= 0.8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/c12": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", - "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.3", - "confbox": "^0.2.2", - "defu": "^6.1.4", - "dotenv": "^16.6.1", - "exsolve": "^1.0.7", - "giget": "^2.0.0", - "jiti": "^2.4.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^1.0.0", - "pkg-types": "^2.2.0", - "rc9": "^2.1.2" - }, - "peerDependencies": { - "magicast": "^0.3.5" + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" }, - "peerDependenciesMeta": { - "magicast": { - "optional": true - } + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/c12/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "expect": "30.2.0", + "jest-snapshot": "30.2.0" }, "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/c12/node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, - "license": "BSD-2-Clause", + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, "engines": { - "node": ">=12" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, - "funding": { - "url": "https://dotenvx.com" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/c12/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, + "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { - "consola": "^3.2.3" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, "engines": { - "node": "^14.18.0 || >=16.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=6.6.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" }, "engines": { - "node": ">= 0.10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=16.0.0" + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, - "license": "MIT" + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, - "license": "MIT" + "node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", + "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", + "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", + "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", + "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/fetch-engine": "6.18.0", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", + "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", + "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", + "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.47", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", + "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/dotenv": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", + "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/is-email": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/is-email/-/is-email-1.0.0.tgz", + "integrity": "sha512-b/76ooKpYY/b+oPrOuc/pmM5eag+ZlzctPsKcRCIKs+TFzh0FL58OeXtSPkbXt3uKNK84YCKHmjnoREtwve5Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/socket.io": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.1.tgz", + "integrity": "sha512-XSma2FhVD78ymvoxYV4xGXrIH/0EKQ93rR+YR0Y+Kw1xbPzLDCip/UWSejZ08FpxYeYNci/PZPQS9anrvJRqMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "socket.io": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", + "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.17", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", + "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001765", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", + "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "BSD-3-Clause", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.277", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.277.tgz", + "integrity": "sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-jwt": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz", + "integrity": "sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, "engines": { - "node": ">=0.3.1" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "license": "BSD-2-Clause", + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://dotenvx.com" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "dependencies": { - "safe-buffer": "^5.0.1" + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, - "node_modules/effect": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", - "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "devOptional": true, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", - "fast-check": "^3.23.1" + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/empathic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", - "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, + "node_modules/is-email": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-email/-/is-email-1.0.2.tgz", + "integrity": "sha512-UojUgD2EhDTBQ2SGKwrK9edce5phRzgLsP+V5+Uu2Swi+uvjVXgH3zduM3HhT9iaC/9Kq19/TYUbP0jPoi6ioA==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" } }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/engine.io": { - "version": "6.6.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", - "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", - "license": "MIT", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.18.3" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=10.2.0" + "node": ">=10" } }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=8" } }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/engine.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" } }, - "node_modules/engine.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/istanbul-lib-source-maps/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "node_modules/jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=10.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { + "node-notifier": { "optional": true } } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/jest-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "dev": true, "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "node_modules/jest-circus/node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], "license": "MIT" }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "node_modules/jest-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "dev": true, "license": "MIT", "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/express-jwt": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz", - "integrity": "sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ==", + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, "license": "MIT", "dependencies": { - "@types/jsonwebtoken": "^9", - "express-unless": "^2.1.3", - "jsonwebtoken": "^9.0.0" + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" }, "engines": { - "node": ">= 8.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/express-unless": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", - "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==", - "license": "MIT" + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "devOptional": true, - "license": "MIT" + "node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, "license": "MIT", "dependencies": { - "pure-rand": "^6.1.0" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "@jest/types": "30.2.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" }, "engines": { - "node": ">= 0.8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, "engines": { - "node": ">= 0.8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, + "node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, "license": "MIT", "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "defu": "^6.1.4", - "node-fetch-native": "^1.6.6", - "nypm": "^0.6.0", - "pathe": "^2.0.3" + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, - "bin": { - "giget": "dist/cli.mjs" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/jest-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/jest-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, "engines": { - "node": ">=4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + }, "engines": { - "node": ">= 0.8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "ISC" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/is-email": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-email/-/is-email-1.0.2.tgz", - "integrity": "sha512-UojUgD2EhDTBQ2SGKwrK9edce5phRzgLsP+V5+Uu2Swi+uvjVXgH3zduM3HhT9iaC/9Kq19/TYUbP0jPoi6ioA==", - "license": "SEE LICENSE IN LICENSE" - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", "bin": { - "jiti": "lib/jiti-cli.mjs" + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonwebtoken": { @@ -1521,6 +4952,36 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1557,12 +5018,45 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1570,6 +5064,16 @@ "dev": true, "license": "ISC" }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1600,6 +5104,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -1621,6 +5169,16 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1643,6 +5201,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -1722,6 +5290,29 @@ "node": ">= 0.6" } }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1731,6 +5322,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-addon-api": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", @@ -1758,6 +5356,20 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, "node_modules/nodemon": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", @@ -1797,6 +5409,19 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", @@ -1866,6 +5491,103 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1875,6 +5597,60 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", @@ -1899,6 +5675,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1908,8 +5691,31 @@ "engines": { "node": ">=8.6" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/pkg-types": { @@ -1924,6 +5730,34 @@ "pathe": "^2.0.3" } }, + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prisma": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", @@ -1988,9 +5822,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -2053,6 +5887,13 @@ "destr": "^2.0.3" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2080,6 +5921,39 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -2177,6 +6051,29 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -2249,6 +6146,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -2262,6 +6172,16 @@ "node": ">=10" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/socket.io": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", @@ -2367,6 +6287,47 @@ "node": ">= 0.6" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2393,26 +6354,289 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/superstruct": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=14.0.0" + } + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "has-flag": "^3.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=4" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/tinyexec": { @@ -2425,6 +6649,13 @@ "node": ">=18" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2457,6 +6688,72 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/ts-jest": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -2501,6 +6798,37 @@ } } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -2535,6 +6863,20 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -2557,6 +6899,72 @@ "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2570,6 +6978,32 @@ "dev": true, "license": "MIT" }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2579,12 +7013,154 @@ "node": ">= 0.8" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", @@ -2615,6 +7191,97 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -2624,6 +7291,19 @@ "engines": { "node": ">=6" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 226d267c..a6384c8c 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,18 @@ "@types/dotenv": "^6.1.1", "@types/express": "^5.0.6", "@types/is-email": "^1.0.0", + "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", "@types/node": "^24.10.1", "@types/socket.io": "^3.0.1", + "@types/supertest": "^6.0.3", "@types/ws": "^8.18.1", + "jest": "^30.2.0", "nodemon": "^3.1.11", "prisma": "^6.18.0", + "supertest": "^7.2.2", + "ts-jest": "^29.4.6", "ts-node": "^10.9.2", "typescript": "^5.9.3" } diff --git a/sprint8-README.md b/sprint8-README.md new file mode 100644 index 00000000..ec63f69c --- /dev/null +++ b/sprint8-README.md @@ -0,0 +1,21 @@ +## 미션 목표 + +- [x] 알림 기능 구현하기 +- [x] 웹소켓 또는 Socket.IO를 사용해 실시간 기능 구현하기 + +## 기본 요구 사항 + +- [x] 기존에 작업한 판다마켓 미션과 이어서 진행됩니다. +- [x] 판다마켓 최종 디자인을 참고해주세요. + +## 알림 + +- [x] 사용자는 자신의 알림 목록을 조회할 수 있습니다. +- [x] 사용자는 자신의 안 읽은 알림의 개수를 조회할 수 있습니다. +- [x] 사용자는 자신의 알림을 읽음 처리 할 수 있습니다. +- [x] 클라이언트에서는 실시간으로 알림을 받을 수 있습니다. + +## 알림 전송 + +- [x] 좋아요한 상품의 가격이 변동되었을 때 알림을 보내주세요. +- [x] 자신이 작성한 게시글에 댓글이 달렸을 때 알림을 보내주세요. diff --git a/src/controller/productController.ts b/src/controller/productController.ts index ad9d687e..6aa10506 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -48,7 +48,7 @@ export default class ProductController { GetProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) throw new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(400, "id is required"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; if (!userId) throw new CustomError(404, "userId Not Found"); @@ -67,11 +67,11 @@ export default class ProductController { PatchProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) throw new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(400, "id is required"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; - if (!userId) throw new CustomError(404, "userId Not Found"); + if (!userId) throw new CustomError(401, "Unauthorized"); assert(req.body, PatchProduct); const userFields = removeUndefined(req.body); @@ -90,7 +90,7 @@ export default class ProductController { DeleteProductById: ExpressHandler = async (req, res) => { const id = req.params.id; - if (!id) throw new CustomError(404, "id Not Found"); + if (!id) throw new CustomError(400, "id is required"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; if (!userId) throw new CustomError(404, "userId Not Found"); @@ -102,7 +102,7 @@ export default class ProductController { if (!req.user) throw new CustomError(404, "user Not Found"); const userId = req.user.userId; const productId = req.params.id; - if (!productId) throw new CustomError(404, "productId Not Found"); + if (!productId) throw new CustomError(400, "productId is required"); const _productId = parseInt(productId); const result = await productService.likeProduct(userId, _productId); return res.status(200).json(result); diff --git a/src/main.ts b/src/main.ts index 0a12d5d7..2e1abb90 100644 --- a/src/main.ts +++ b/src/main.ts @@ -71,4 +71,6 @@ io.on('connection', (socket) => { httpServer.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); -}); \ No newline at end of file +}); + +export default app; \ No newline at end of file diff --git a/src/services/commentService.ts b/src/services/commentService.ts index 63079bb0..0f9d5a20 100644 --- a/src/services/commentService.ts +++ b/src/services/commentService.ts @@ -51,7 +51,7 @@ class CommentService { // 4. 작성자가 본인이 아닐 경우에만 알림 생성 // (ownerId가 존재하고, 댓글 쓴 사람(userId)과 다를 때) - if (ownerId) {//&& ownerId !== userId) { + if (ownerId && ownerId !== userId) { const message = `작성하신 글 '${targetName}'에 새로운 댓글이 달렸습니다: ${content}`; // DB에 알림 저장 diff --git a/src/services/productService.ts b/src/services/productService.ts index 0c617960..73b81133 100644 --- a/src/services/productService.ts +++ b/src/services/productService.ts @@ -1,7 +1,7 @@ import productRepository from '../repositories/productRepository'; import productLikeRepository from '../repositories/productLikeRepository'; import { ProductFindOptions, ProductPublicData } from '../libs/interfaces'; -import { Prisma } from '@prisma/client'; +import { Notification } from '@prisma/client'; class ProductService { @@ -39,7 +39,7 @@ class ProductService { const updatedProduct = await productRepository.update(id, userFields); // 3. 알림 로직: 가격이 존재하고, 이전 가격과 다를 때 - let notifications: any[] = []; // 컨트롤러로 보낼 알림 목록 + let notifications: Notification[] = []; // 컨트롤러로 보낼 알림 목록 if (oldProduct && userFields.price !== undefined && oldProduct.price !== userFields.price) { // 3-1. 찜한 유저들 찾기 diff --git a/test/article.test.ts b/test/article.test.ts new file mode 100644 index 00000000..5b77ba6b --- /dev/null +++ b/test/article.test.ts @@ -0,0 +1,68 @@ +import request from 'supertest'; +import app from './../src/main'; + +describe('Article API Integration Tests', () => { + let accessToken: string; + let createdArticleId: number; + + beforeAll(async () => { + const user = { + email: `article_tester_${Date.now()}@test.com`, + nickname: 'writer', + password: 'password123' + }; + await request(app).post('/auth/signup').send(user); + const res = await request(app).post('/auth/login').send({ email: user.email, password: user.password }); + accessToken = res.body.accessToken; + }); + + describe('Public Routes', () => { + it('GET /articles - should return list of articles', async () => { + const res = await request(app).get('/articles'); + expect(res.status).toBe(200); + expect(Array.isArray(res.body.list)).toBe(true); + }); + }); + + describe('Protected Routes', () => { + const newArticle = { + title: 'Test Article', + content: 'This is a test content.' + }; + + it('POST /articles - should create an article', async () => { + const res = await request(app) + .post('/articles') + .set('Authorization', `Bearer ${accessToken}`) + .send(newArticle); + + expect(res.status).toBe(201); + expect(res.body).toHaveProperty('id'); + createdArticleId = res.body.id; + }); + + it('GET /articles/:id - should return article detail', async () => { + const res = await request(app).get(`/articles/${createdArticleId}`); + expect(res.status).toBe(200); + expect(res.body.id).toBe(createdArticleId); + }); + + it('PATCH /articles/:id - should update article', async () => { + const res = await request(app) + .patch(`/articles/${createdArticleId}`) + .set('Authorization', `Bearer ${accessToken}`) + .send({ title: 'Updated Title' }); + + expect(res.status).toBe(200); + expect(res.body.title).toBe('Updated Title'); + }); + + it('DELETE /articles/:id - should delete article', async () => { + const res = await request(app) + .delete(`/articles/${createdArticleId}`) + .set('Authorization', `Bearer ${accessToken}`); + + expect(res.status).toBe(200); + }); + }); +}); \ No newline at end of file diff --git a/test/auth.test.ts b/test/auth.test.ts new file mode 100644 index 00000000..24d3c98d --- /dev/null +++ b/test/auth.test.ts @@ -0,0 +1,44 @@ +import request from 'supertest'; +import app from './../src/main'; + +describe('Auth API Integration Tests', () => { + const newUser = { + email: `test${Date.now()}@example.com`, + nickname: 'testuser', + password: 'password123' + }; + + describe('POST /auth/signup', () => { + it('should register a new user', async () => { + const res = await request(app) + .post('/auth/signup') + .send(newUser); + + expect(res.status).toBe(201); + expect(res.body).toHaveProperty('email', newUser.email); + }); + + it('should fail if email already exists', async () => { + const res = await request(app) + .post('/auth/signup') + .send(newUser); + + expect(res.status).not.toBe(201); + }); + }); + + describe('POST /auth/login', () => { + it('should login and return tokens', async () => { + const res = await request(app) + .post('/auth/login') + .send({ + email: newUser.email, + password: newUser.password + }); + + expect(res.status).toBe(200); + expect(res.body).toHaveProperty('accessToken'); + expect(res.body).toHaveProperty('refreshToken'); + }); + }); +}); \ No newline at end of file diff --git a/test/product.test.ts b/test/product.test.ts new file mode 100644 index 00000000..5ea4dd07 --- /dev/null +++ b/test/product.test.ts @@ -0,0 +1,79 @@ +import request from 'supertest'; +import app from './../src/main'; + +describe('Product API Integration Tests', () => { + let accessToken: string; + let createdProductId: number; + + // 테스트 전 토큰 발급 + beforeAll(async () => { + const user = { + email: `product_tester_${Date.now()}@test.com`, + nickname: 'tester', + password: 'password123' + }; + await request(app).post('/auth/signup').send(user); + const res = await request(app).post('/auth/login').send({ email: user.email, password: user.password }); + accessToken = res.body.accessToken; + }); + + describe('Public Routes', () => { + it('GET /products - should return list of products', async () => { + const res = await request(app).get('/products'); + expect(res.status).toBe(200); + expect(Array.isArray(res.body.list)).toBe(true); + }); + }); + + describe('Protected Routes', () => { + const newProduct = { + name: 'Test Product', + description: 'Test Description', + price: 10000, + tags: ['test', 'jest'] + }; + + it('POST /products - should create a product (Authorized)', async () => { + const res = await request(app) + .post('/products') + .set('Authorization', `Bearer ${accessToken}`) + .send(newProduct); + + expect(res.status).toBe(201); + expect(res.body).toHaveProperty('id'); + createdProductId = res.body.id; + }); + + it('POST /products - should fail without token', async () => { + const res = await request(app) + .post('/products') + .send(newProduct); + + expect(res.status).toBe(401); + }); + + it('GET /products/:id - should return product detail', async () => { + const res = await request(app).get(`/products/${createdProductId}`); + expect(res.status).toBe(200); + expect(res.body.id).toBe(createdProductId); + }); + + it('PATCH /products/:id - should update product', async () => { + const res = await request(app) + .patch(`/products/${createdProductId}`) + .set('Authorization', `Bearer ${accessToken}`) + .send({ price: 20000 }); + + expect(res.status).toBe(200); + expect(res.body.price).toBe(20000); + }); + + it('DELETE /products/:id - should delete product', async () => { + const res = await request(app) + .delete(`/products/${createdProductId}`) + .set('Authorization', `Bearer ${accessToken}`); + + expect(res.status).toBe(200); // 또는 204 + }); + }); +}); \ No newline at end of file diff --git a/test/productService.test.ts b/test/productService.test.ts new file mode 100644 index 00000000..324dc55e --- /dev/null +++ b/test/productService.test.ts @@ -0,0 +1,82 @@ +import { productService } from '../src/services/productService'; +import productRepository from '../src/repositories/productRepository'; + +// Repository 모듈 전체를 Mocking +jest.mock('../src/repositories/productRepository'); + +// Mock 객체에 대한 타입 정의 (타입 안전성 및 자동완성 지원) +const mockProductRepository = productRepository as jest.Mocked; + +describe('ProductService Unit Tests', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('createProducts', () => { + it('should create a product successfully', async () => { + // Arrange + const productData = { + name: 'Unit Test Product', + description: 'Desc', + price: 1000, + tags: ['tag'], + images: [], + userId: 1 + }; + const mockCreatedProduct = { + id: 1, + name: productData.name, + description: productData.description, + price: productData.price, + tags: productData.tags, + userId: productData.userId, + createdAt: new Date(), + updatedAt: new Date() + }; + + // Repository의 create 메소드가 mockCreatedProduct를 반환하도록 설정 (Mock) + mockProductRepository.create.mockResolvedValue(mockCreatedProduct); + + // Act + const result = await productService.createProducts(productData); + + // Assert + expect(mockProductRepository.create).toHaveBeenCalledTimes(1); + expect(mockProductRepository.create).toHaveBeenCalledWith(productData); + expect(result).toEqual(mockCreatedProduct); + }); + }); + + describe('getProductById', () => { + it('should return product with isLiked status', async () => { + const productId = 1; + const userId = 1; + const mockProduct = { + id: productId, + name: 'Test', + description: 'Test Description', + price: 1000, + tags: ['tag'], + userId: 1, + createdAt: new Date(), + updatedAt: new Date(), + productLikes: [{ + id: 1, + userId: userId, + productId: productId, + createdAt: new Date() + }] // 좋아요를 누른 상태 시뮬레이션 + }; + + mockProductRepository.findById.mockResolvedValue(mockProduct); + + const result = await productService.getProductById(productId, userId); + + expect(mockProductRepository.findById).toHaveBeenCalledWith(productId, userId); + // Service 로직에 의해 productLikes 배열은 사라지고 isLiked 속성이 생겨야 함 + expect(result).toHaveProperty('isLiked', true); + expect(result).toHaveProperty('name', 'Test'); + expect(result).not.toHaveProperty('productLikes'); + }); + }); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b38385a6..51c248aa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,34 +1,17 @@ { - // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { - // File Layout "rootDir": "./src", "outDir": "./dist", - // Environment Settings - // See also https://aka.ms/tsconfig/module "module": "commonjs", "target": "es2016", "typeRoots": ["./types", "./node_modules/@types"], - // Other Outputs "sourceMap": true, "declaration": true, "declarationMap": true, - // Stricter Typechecking Options "noUncheckedIndexedAccess": true, - // "exactOptionalPropertyTypes": true, - - // Style Options - // "noImplicitReturns": true, - // "noImplicitOverride": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - // "noFallthroughCasesInSwitch": true, - // "noPropertyAccessFromIndexSignature": true, - - // Recommended Options "strict": true, "noImplicitAny": true, "noUncheckedSideEffectImports": true, From a97c5d5f149db1644cb889fe61eba6d814cb77fd Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 20:41:37 +0900 Subject: [PATCH 33/53] =?UTF-8?q?=EC=8A=A4=ED=94=84=EB=A6=B0=ED=8A=B810=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=A4=91=EA=B0=84=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 +- dist/Routers/notificationRouter.d.ts | 3 + dist/Routers/notificationRouter.d.ts.map | 1 + dist/Routers/notificationRouter.js | 15 + dist/Routers/notificationRouter.js.map | 1 + dist/Routers/routerManager.js | 4 +- dist/Routers/routerManager.js.map | 2 +- dist/Routers/userRouter.js | 2 +- dist/Routers/userRouter.js.map | 2 +- dist/controller/articleController.d.ts.map | 2 +- dist/controller/articleController.js | 36 +- dist/controller/articleController.js.map | 2 +- dist/controller/commentController.d.ts | 11 +- dist/controller/commentController.d.ts.map | 2 +- dist/controller/commentController.js | 115 +- dist/controller/commentController.js.map | 2 +- dist/controller/notificationController.d.ts | 7 + .../notificationController.d.ts.map | 1 + dist/controller/notificationController.js | 52 + dist/controller/notificationController.js.map | 1 + dist/controller/productController.d.ts.map | 2 +- dist/controller/productController.js | 43 +- dist/controller/productController.js.map | 2 +- dist/controller/uploadController.d.ts | 3 +- dist/controller/uploadController.d.ts.map | 2 +- dist/controller/uploadController.js | 2 +- dist/controller/uploadController.js.map | 2 +- dist/libs/interfaces.d.ts | 4 + dist/libs/interfaces.d.ts.map | 2 +- dist/main.d.ts | 3 +- dist/main.d.ts.map | 2 +- dist/main.js | 42 +- dist/main.js.map | 2 +- dist/repositories/articleRepository.d.ts | 5 + dist/repositories/articleRepository.d.ts.map | 2 +- dist/repositories/commentRepository.d.ts | 67 + dist/repositories/commentRepository.d.ts.map | 1 + dist/repositories/commentRepository.js | 112 + dist/repositories/commentRepository.js.map | 1 + dist/repositories/notificationRepository.d.ts | 30 + .../notificationRepository.d.ts.map | 1 + dist/repositories/notificationRepository.js | 52 + .../notificationRepository.js.map | 1 + dist/repositories/productLikeRepository.d.ts | 1 + .../productLikeRepository.d.ts.map | 2 +- dist/repositories/productRepository.d.ts | 29 + dist/repositories/productRepository.d.ts.map | 2 +- dist/repositories/productRepository.js | 31 +- dist/repositories/productRepository.js.map | 2 +- dist/services/articleService.d.ts | 28 +- dist/services/articleService.d.ts.map | 2 +- dist/services/articleService.js | 24 + dist/services/articleService.js.map | 2 +- dist/services/commentService.d.ts | 57 + dist/services/commentService.d.ts.map | 1 + dist/services/commentService.js | 90 + dist/services/commentService.js.map | 1 + dist/services/notificationService.d.ts | 22 + dist/services/notificationService.d.ts.map | 1 + dist/services/notificationService.js | 47 + dist/services/notificationService.js.map | 1 + dist/services/productService.d.ts | 43 +- dist/services/productService.d.ts.map | 2 +- dist/services/productService.js | 30 + dist/services/productService.js.map | 2 +- dist/services/userService.d.ts | 2 + dist/services/userService.d.ts.map | 2 +- package-lock.json | 3451 ++++++++++++----- package.json | 1 + sprint9-README.md | 21 + start.sh | 0 tsconfig.json | 3 +- 72 files changed, 3526 insertions(+), 1048 deletions(-) create mode 100644 dist/Routers/notificationRouter.d.ts create mode 100644 dist/Routers/notificationRouter.d.ts.map create mode 100644 dist/Routers/notificationRouter.js create mode 100644 dist/Routers/notificationRouter.js.map create mode 100644 dist/controller/notificationController.d.ts create mode 100644 dist/controller/notificationController.d.ts.map create mode 100644 dist/controller/notificationController.js create mode 100644 dist/controller/notificationController.js.map create mode 100644 dist/repositories/commentRepository.d.ts create mode 100644 dist/repositories/commentRepository.d.ts.map create mode 100644 dist/repositories/commentRepository.js create mode 100644 dist/repositories/commentRepository.js.map create mode 100644 dist/repositories/notificationRepository.d.ts create mode 100644 dist/repositories/notificationRepository.d.ts.map create mode 100644 dist/repositories/notificationRepository.js create mode 100644 dist/repositories/notificationRepository.js.map create mode 100644 dist/services/commentService.d.ts create mode 100644 dist/services/commentService.d.ts.map create mode 100644 dist/services/commentService.js create mode 100644 dist/services/commentService.js.map create mode 100644 dist/services/notificationService.d.ts create mode 100644 dist/services/notificationService.d.ts.map create mode 100644 dist/services/notificationService.js create mode 100644 dist/services/notificationService.js.map create mode 100644 sprint9-README.md create mode 100644 start.sh diff --git a/README.md b/README.md index aa34f7d5..f7bb9890 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,35 @@ ## 미션 목표 -- [x] Jest와 Supertest를 사용해 유닛 테스트, 통합 테스트 작성하기 +- [x] 판다마켓 서비스를 AWS로 배포하기 +- [x] AWS S3 적용 +- [x] AWS RDS 적용 +- [x] AWS EC2에 Express 서버 배포하기 +- [x] (심화) 프로세스 매니저 적용 +- [x] (심화) 리버스 프록시 적용 ## 기본 요구 사항 -- [x] Jest의 테스트 커버리지 도구를 사용하도록 설정해 주세요. +- [x] 프로젝트에 프로덕션 배포를 위한 환경 변수 설정을 해 주세요. -- [x] 인증이 필요하지 않은 상품 API에 대한 통합 테스트를 작성해 주세요. +##AWS S3 적용 -- [x] 인증이 필요하지 않은 게시글 API에 대한 통합 테스트를 작성해 주세요. +- [x] AWS S3 버킷을 생성하고, 퍼블릭 액세스를 허용해 주세요. +- [x] 일반 사용자가 S3 업로드된 파일에 접근할 수 있도록 S3 버킷 정책을 설정해 주세요. +- [x] AWS EC2에서 AWS S3를 사용하기 위한 액세스 키를 AWS IAM에서 발급하세요. +- [x] 프로덕션 환경에서는 파일 업로드에 AWS S3를 사용하도록 구현을 수정해 주세요. -- [x] 로그인, 회원가입 API에 대한 통합 테스트를 작성해 주세요. +## AWS RDS 적용 -- [x] 인증이 필요한 상품 API에 대해 통합 테스트를 작성해 주세요. +- [x] AWS RDS 프리티어에 해당하는 인스턴스를 생성합니다. +- [x] RDS 인스턴스에 대한 보안 그룹을 설정합니다. +- [x] 프로덕션 환경에서는 Prisma에 프로젝트 데이터베이스와 연결하도록 합니다. -- [x] 인증이 필요한 게시글 API에 대해 통합 테스트를 작성해 주세요. +## AWS EC2에 Express 서버 배포하기 + +- [x] AWS EC2 프리티어에 해당하는 인스턴스를 생성합니다. +- [x] SSH를 사용해 EC2 인스턴스에 접속해 Express 서버를 배포해 주세요. ## 심화 요구사항 -- [x] 상품 API의 비즈니스 로직에 대해 Mock, Spy를 활용해 유닛 테스트를 작성해 주세요. +- [x] EC2 인스턴스에서 pm2 프로세스 매니저를 사용하여 애플리케이션을 실행해 주세요. +- [x] EC2 인스턴스에서 nginx 리버스 프록시를 설정해 서버를 80번 포트로 서비스합니다. diff --git a/dist/Routers/notificationRouter.d.ts b/dist/Routers/notificationRouter.d.ts new file mode 100644 index 00000000..ec471d7f --- /dev/null +++ b/dist/Routers/notificationRouter.d.ts @@ -0,0 +1,3 @@ +declare const router: import("express-serve-static-core").Router; +export default router; +//# sourceMappingURL=notificationRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/notificationRouter.d.ts.map b/dist/Routers/notificationRouter.d.ts.map new file mode 100644 index 00000000..44cab9e1 --- /dev/null +++ b/dist/Routers/notificationRouter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/notificationRouter.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAQxB,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/dist/Routers/notificationRouter.js b/dist/Routers/notificationRouter.js new file mode 100644 index 00000000..a765773b --- /dev/null +++ b/dist/Routers/notificationRouter.js @@ -0,0 +1,15 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const notificationController_1 = __importDefault(require("../controller/notificationController")); +const auth_1 = __importDefault(require("../middlewares/auth")); +const router = (0, express_1.Router)(); +const controller = new notificationController_1.default(); +router.get('/', auth_1.default.softVerifyAccessToken, controller.GetNotifications); +router.get('/count', auth_1.default.softVerifyAccessToken, controller.GetUnreadCount); +router.patch('/:id/read', auth_1.default.verifyAccessToken, controller.ReadNotification); +exports.default = router; +//# sourceMappingURL=notificationRouter.js.map \ No newline at end of file diff --git a/dist/Routers/notificationRouter.js.map b/dist/Routers/notificationRouter.js.map new file mode 100644 index 00000000..1240def1 --- /dev/null +++ b/dist/Routers/notificationRouter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationRouter.js","sourceRoot":"","sources":["../../src/Routers/notificationRouter.ts"],"names":[],"mappings":";;;;;AAAA,qCAAiC;AACjC,kGAA0E;AAC1E,+DAAuC;AAEvC,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AACxB,MAAM,UAAU,GAAG,IAAI,gCAAsB,EAAE,CAAC;AAGhD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,cAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACzE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;AAC5E,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAE/E,kBAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.js b/dist/Routers/routerManager.js index 7d4eb65f..09c1df5d 100644 --- a/dist/Routers/routerManager.js +++ b/dist/Routers/routerManager.js @@ -10,10 +10,12 @@ const articleRouter_1 = __importDefault(require("./articleRouter")); const commentRouter_1 = __importDefault(require("./commentRouter")); const uploadRouter_1 = __importDefault(require("./uploadRouter")); const userRouter_1 = __importDefault(require("./userRouter")); +const notificationRouter_1 = __importDefault(require("./notificationRouter")); exports.RouterManager = constants_1.EXPRESS.Router(); exports.RouterManager.use('/products', productRouter_1.default); exports.RouterManager.use('/articles', articleRouter_1.default); exports.RouterManager.use('/comments', commentRouter_1.default); exports.RouterManager.use('/files', uploadRouter_1.default); -exports.RouterManager.use('/user', userRouter_1.default); +exports.RouterManager.use('/auth', userRouter_1.default); +exports.RouterManager.use('/notifications', notificationRouter_1.default); //# sourceMappingURL=routerManager.js.map \ No newline at end of file diff --git a/dist/Routers/routerManager.js.map b/dist/Routers/routerManager.js.map index e21219bc..2ef0f797 100644 --- a/dist/Routers/routerManager.js.map +++ b/dist/Routers/routerManager.js.map @@ -1 +1 @@ -{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,iDAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,kEAA0C;AAC1C,8DAAsC;AAGzB,QAAA,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAU,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,iDAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,kEAA0C;AAC1C,8DAAsC;AACtC,8EAAsD;AAEzC,QAAA,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAU,CAAC,CAAC;AACvC,qBAAa,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAkB,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.js b/dist/Routers/userRouter.js index 980a7423..7224940d 100644 --- a/dist/Routers/userRouter.js +++ b/dist/Routers/userRouter.js @@ -9,7 +9,7 @@ const userController_1 = __importDefault(require("../controller/userController") const auth_1 = __importDefault(require("../middlewares/auth")); const userRouter = constants_1.EXPRESS.Router(); const userController = new userController_1.default(); -userRouter.post('/', (0, catchAsync_1.catchAsync)(userController.register)); +userRouter.post('/signup', (0, catchAsync_1.catchAsync)(userController.register)); userRouter.post('/login', (0, catchAsync_1.catchAsync)(userController.login)); userRouter.post('/token/refresh', auth_1.default.verifyRefreshToken, (0, catchAsync_1.catchAsync)(userController.refresh)); userRouter.get('/me', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.GetMe)); diff --git a/dist/Routers/userRouter.js.map b/dist/Routers/userRouter.js.map index 9b027a1a..8ee3abe1 100644 --- a/dist/Routers/userRouter.js.map +++ b/dist/Routers/userRouter.js.map @@ -1 +1 @@ -{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,kFAAiE;AACjE,+DAAuC;AAEvC,MAAM,UAAU,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,wBAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1D,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,cAAI,CAAC,kBAAkB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file +{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,kFAAiE;AACjE,+DAAuC;AAEvC,MAAM,UAAU,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,wBAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChE,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,cAAI,CAAC,kBAAkB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/controller/articleController.d.ts.map b/dist/controller/articleController.d.ts.map index 75097cd4..37904518 100644 --- a/dist/controller/articleController.d.ts.map +++ b/dist/controller/articleController.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,WAAW,EAAE,cAAc,CAqCzB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAKzB;IAEF,gBAAgB,EAAE,cAAc,CAQ9B;IAEF,iBAAiB,EAAE,cAAc,CAM/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file +{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,WAAW,EAAE,cAAc,CAqCzB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAOzB;IAEF,gBAAgB,EAAE,cAAc,CAU9B;IAEF,iBAAiB,EAAE,cAAc,CAQ/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/articleController.js b/dist/controller/articleController.js index 1bd6ce98..4f41551f 100644 --- a/dist/controller/articleController.js +++ b/dist/controller/articleController.js @@ -19,14 +19,10 @@ var __rest = (this && this.__rest) || function (s, e) { } return t; }; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); const articleService_1 = require("../services/articleService"); const superstruct_1 = require("superstruct"); const structs_1 = require("../structs/structs"); -const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); const errorHandler_1 = require("../libs/Handler/errorHandler"); class ArticleController { constructor() { @@ -62,52 +58,64 @@ class ArticleController { } const userId = req.user ? req.user.userId : null; if (!userId) - return new errorHandler_1.CustomError(404, "UserId Not Found"); + throw new errorHandler_1.CustomError(404, "UserId Not Found"); const articles = yield articleService_1.articleService.getArticles(findOptions, userId); res.send(articles); }); this.getArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { const { id } = req.params; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(404, "id Not Found"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; if (!userId) - return new errorHandler_1.CustomError(404, "userId Not Found"); + throw new errorHandler_1.CustomError(404, "userId Not Found"); const article = yield articleService_1.articleService.getArticleById(_id, userId); res.send(article); }); this.postArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; (0, superstruct_1.assert)(req.body, structs_1.CreateArticle); const userFields = __rest(req.body, []); - const article = yield articleRepository_1.default.create(userFields); + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + const article = yield articleService_1.articleService.postArticle(Object.assign(Object.assign({}, userFields), { userId })); res.status(201).send(article); }); this.patchArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; const { id } = req.params; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(404, "id Not Found"); const _id = parseInt(id); (0, superstruct_1.assert)(req.body, structs_1.PatchArticle); const userFields = __rest(req.body, []); - const article = yield articleRepository_1.default.update(_id, userFields); + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + const article = yield articleService_1.articleService.patchArticleById(_id, Object.assign(Object.assign({}, userFields), { userId })); res.send(article); }); this.deleteArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; const { id } = req.params; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(404, "id Not Found"); const _id = parseInt(id); - const article = yield articleRepository_1.default.ondelete(_id); + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + const article = yield articleService_1.articleService.deleteArticleById(_id, userId); res.send(article); }); this.likeArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { if (!req.user) - return new errorHandler_1.CustomError(404, "user Not Found"); + throw new errorHandler_1.CustomError(404, "user Not Found"); const userId = req.user.userId; const articleId = req.params.id; if (!articleId) - return new errorHandler_1.CustomError(404, "articleId Not Found"); + throw new errorHandler_1.CustomError(404, "articleId Not Found"); const _articleId = parseInt(articleId); const result = yield articleService_1.articleService.likeArticle(userId, _articleId); return res.status(200).json(result); diff --git a/dist/controller/articleController.js.map b/dist/controller/articleController.js.map index 18705063..9095f7df 100644 --- a/dist/controller/articleController.js.map +++ b/dist/controller/articleController.js.map @@ -1 +1 @@ -{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/commentController.d.ts b/dist/controller/commentController.d.ts index 4865520f..f5c03c08 100644 --- a/dist/controller/commentController.d.ts +++ b/dist/controller/commentController.d.ts @@ -1,8 +1,7 @@ import { ExpressRequest, ExpressResponse } from '../libs/constants'; -import { CustomError } from '../libs/Handler/errorHandler'; -export declare function GetComment(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function GetCommentById(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function PostComment(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function PatchCommentById(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function DeleteCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function GetComment(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function GetCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function PostComment(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function PatchCommentById(req: ExpressRequest, res: ExpressResponse): Promise; +export declare function DeleteCommentById(req: ExpressRequest, res: ExpressResponse): Promise; //# sourceMappingURL=commentController.d.ts.map \ No newline at end of file diff --git a/dist/controller/commentController.d.ts.map b/dist/controller/commentController.d.ts.map index 5e354b93..188ce74f 100644 --- a/dist/controller/commentController.d.ts.map +++ b/dist/controller/commentController.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,wCA2CzE;AAGD,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,oCAU7E;AAGD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,wCAoB1E;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,sDAmB/E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,oCAUhF"} \ No newline at end of file +{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpE,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAczE;AAGD,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAM7E;AAGD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAmB1E;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAU/E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAMhF"} \ No newline at end of file diff --git a/dist/controller/commentController.js b/dist/controller/commentController.js index ef9e30ee..e3b2854d 100644 --- a/dist/controller/commentController.js +++ b/dist/controller/commentController.js @@ -14,82 +14,53 @@ exports.GetCommentById = GetCommentById; exports.PostComment = PostComment; exports.PatchCommentById = PatchCommentById; exports.DeleteCommentById = DeleteCommentById; -const constants_1 = require("../libs/constants"); const superstruct_1 = require("superstruct"); const structs_1 = require("../structs/structs"); const errorHandler_1 = require("../libs/Handler/errorHandler"); +const commentService_1 = require("../services/commentService"); function GetComment(req, res) { return __awaiter(this, void 0, void 0, function* () { const { take = '10', cursor, productId, articleId } = req.query; const parsedTake = parseInt(take, 10) || 0; if (isNaN(parsedTake) || parsedTake <= 0) { - return res.status(400).send({ error: 'Invalid "take" parameter.' }); + throw new errorHandler_1.CustomError(400, 'Invalid "take" parameter.'); } - const whereClause = {}; - if (productId) { - whereClause.productId = Number(productId); // 상품 ID로 필터링 - } - else if (articleId) { - whereClause.articleId = Number(articleId); // 게시글 ID로 필터링 - } - const findOptions = { - take: parsedTake, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, - }; - if (cursor) { - const parsedCursor = parseInt(cursor, 10); - findOptions.skip = 1; - findOptions.cursor = { - id: parsedCursor, - }; - } - const comments = yield constants_1.prismaClient.comment.findMany(findOptions); // - let nextCursor = null; - if (comments.length > 0 && comments.length === parsedTake) { - nextCursor = comments[comments.length - 1].id; - } - res.status(200).json({ - comments, - nextCursor, - }); + const parsedCursor = cursor ? parseInt(cursor, 10) : undefined; + const parsedProductId = productId ? Number(productId) : undefined; + const parsedArticleId = articleId ? Number(articleId) : undefined; + const result = yield commentService_1.commentService.getComments(parsedTake, parsedCursor, parsedProductId, parsedArticleId); + res.status(200).json(result); }); } function GetCommentById(req, res) { return __awaiter(this, void 0, void 0, function* () { const { id } = req.params; - const paesedId = parseInt(id, 10) || null; - if (!paesedId) - return new errorHandler_1.CustomError(404, "id Not Found"); - const Comment = yield constants_1.prismaClient.comment.findUniqueOrThrow({ - where: { - id: paesedId - }, - }); - res.send(Comment); + const parsedId = parseInt(id, 10) || null; + if (!parsedId) + throw new errorHandler_1.CustomError(404, "id Not Found"); + const comment = yield commentService_1.commentService.getCommentById(parsedId); + res.send(comment); }); } function PostComment(req, res) { return __awaiter(this, void 0, void 0, function* () { + var _a; (0, superstruct_1.assert)(req.body, structs_1.CreateComment); const { content, productId, articleId } = req.body; - if ((productId && articleId) || (!productId && !articleId)) { - return res.status(400).json({ - error: 'Comment must belong to EITHER a Product OR an Article.', - }); - } - if (!content || content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, 'Unauthorized'); + // 서비스 호출 (userId 추가됨) + const { comment, notification } = yield commentService_1.commentService.createComment(userId, content, productId, articleId); + // [알림 발송] 알림이 생성되었다면 소켓으로 전송 + if (notification) { + const io = req.app.get('io'); // app.js에서 설정한 io 객체 가져오기 + if (io) { + // 알림 받을 사람(notification.userId)에게만 전송 + io.to(notification.userId).emit('notification', notification); + } } - const comment = yield constants_1.prismaClient.comment.create({ - data: { - content: content, - productId: productId, - articleId: articleId, - }, - }); + // 클라이언트에는 댓글 정보만 주거나, 필요하면 알림 생성 여부도 줄 수 있음 res.status(201).send(comment); }); } @@ -98,35 +69,23 @@ function PatchCommentById(req, res) { (0, superstruct_1.assert)(req.body, structs_1.PatchComment); const { content } = req.body; const { id } = req.params; - const paesedId = parseInt(id, 10) || null; - if (!paesedId) - return new errorHandler_1.CustomError(404, "id Not Found"); - if (content !== undefined && content.trim() === '') { - return res.status(400).json({ error: 'Content cannot be empty.' }); - } - const comment = yield constants_1.prismaClient.comment.update({ - where: { - id: paesedId - }, - data: { - content: content, - }, - }); + const parsedId = parseInt(id, 10) || null; + if (!parsedId) + throw new errorHandler_1.CustomError(404, "id Not Found"); + if (!content) + throw new errorHandler_1.CustomError(404, "Request body is empty"); + const comment = yield commentService_1.commentService.updateComment(parsedId, content); res.send(comment); }); } function DeleteCommentById(req, res) { return __awaiter(this, void 0, void 0, function* () { const { id } = req.params; - const paesedId = parseInt(id, 10) || null; - if (!paesedId) - return new errorHandler_1.CustomError(404, "id Not Found"); - const Comment = yield constants_1.prismaClient.comment.delete({ - where: { - id: paesedId - }, - }); - res.send(Comment); + const parsedId = parseInt(id, 10) || null; + if (!parsedId) + throw new errorHandler_1.CustomError(404, "id Not Found"); + const comment = yield commentService_1.commentService.deleteComment(parsedId); + res.send(comment); }); } //# sourceMappingURL=commentController.js.map \ No newline at end of file diff --git a/dist/controller/commentController.js.map b/dist/controller/commentController.js.map index 6a765c54..e15408cd 100644 --- a/dist/controller/commentController.js.map +++ b/dist/controller/commentController.js.map @@ -1 +1 @@ -{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/notificationController.d.ts b/dist/controller/notificationController.d.ts new file mode 100644 index 00000000..06fd096d --- /dev/null +++ b/dist/controller/notificationController.d.ts @@ -0,0 +1,7 @@ +import { ExpressRequest, ExpressResponse } from '../libs/constants'; +export default class NotificationController { + GetNotifications: (req: ExpressRequest, res: ExpressResponse) => Promise; + GetUnreadCount: (req: ExpressRequest, res: ExpressResponse) => Promise; + ReadNotification: (req: ExpressRequest, res: ExpressResponse) => Promise; +} +//# sourceMappingURL=notificationController.d.ts.map \ No newline at end of file diff --git a/dist/controller/notificationController.d.ts.map b/dist/controller/notificationController.d.ts.map new file mode 100644 index 00000000..7612e358 --- /dev/null +++ b/dist/controller/notificationController.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationController.d.ts","sourceRoot":"","sources":["../../src/controller/notificationController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpE,MAAM,CAAC,OAAO,OAAO,sBAAsB;IAGvC,gBAAgB,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAMjE;IAGF,cAAc,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAM/D;IAGF,gBAAgB,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAWjE;CACL"} \ No newline at end of file diff --git a/dist/controller/notificationController.js b/dist/controller/notificationController.js new file mode 100644 index 00000000..f7290317 --- /dev/null +++ b/dist/controller/notificationController.js @@ -0,0 +1,52 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +const notificationService_1 = require("../services/notificationService"); +class NotificationController { + constructor() { + // GET /notifications + this.GetNotifications = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; // 미들웨어에서 userId 가져오기 + if (!userId) + throw new errorHandler_1.CustomError(401, "Unauthorized"); + const notifications = yield notificationService_1.notificationService.getNotifications(userId); + res.status(200).send(notifications); + }); + // GET /notifications/count + this.GetUnreadCount = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, "Unauthorized"); + const result = yield notificationService_1.notificationService.getUnreadCount(userId); + res.status(200).send(result); + }); + // PATCH /notifications/:id/read + this.ReadNotification = (req, res) => __awaiter(this, void 0, void 0, function* () { + var _a; + const { id } = req.params; + if (!id) + throw new errorHandler_1.CustomError(400, "Missing notification ID"); + const notificationId = parseInt(id, 10); + if (!notificationId) + throw new errorHandler_1.CustomError(400, "Invalid notification ID"); + const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; + if (!userId) + throw new errorHandler_1.CustomError(401, "Unauthorized"); + const result = yield notificationService_1.notificationService.readNotification(userId, notificationId); + res.status(200).send(result); + }); + } +} +exports.default = NotificationController; +//# sourceMappingURL=notificationController.js.map \ No newline at end of file diff --git a/dist/controller/notificationController.js.map b/dist/controller/notificationController.js.map new file mode 100644 index 00000000..6485f199 --- /dev/null +++ b/dist/controller/notificationController.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationController.js","sourceRoot":"","sources":["../../src/controller/notificationController.ts"],"names":[],"mappings":";;;;;;;;;;;AACA,+DAA2D;AAC3D,yEAAsE;AAEtE,MAAqB,sBAAsB;IAA3C;QAEI,qBAAqB;QACrB,qBAAgB,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACnE,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC,CAAC,qBAAqB;YACtD,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,aAAa,GAAG,MAAM,yCAAmB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACzE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxC,CAAC,CAAA,CAAC;QAEF,2BAA2B;QAC3B,mBAAc,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACjE,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC;YAChC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,yCAAmB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAChE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAA,CAAC;QAEF,gCAAgC;QAChC,qBAAgB,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACnE,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YAC/D,MAAM,cAAc,GAAG,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,cAAc;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YAE3E,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC;YAChC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,yCAAmB,CAAC,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;YAClF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAA,CAAC;IACN,CAAC;CAAA;AAjCD,yCAiCC"} \ No newline at end of file diff --git a/dist/controller/productController.d.ts.map b/dist/controller/productController.d.ts.map index 573bd7c2..6bc5f629 100644 --- a/dist/controller/productController.d.ts.map +++ b/dist/controller/productController.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,UAAU,EAAE,cAAc,CAqCxB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAKzB;IAEF,gBAAgB,EAAE,cAAc,CAQ9B;IAEF,iBAAiB,EAAE,cAAc,CAM/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file +{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,UAAU,EAAE,cAAc,CAqCxB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAOzB;IAEF,gBAAgB,EAAE,cAAc,CAqB9B;IAEF,iBAAiB,EAAE,cAAc,CAQ/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/productController.js b/dist/controller/productController.js index 8af6264f..38201b5e 100644 --- a/dist/controller/productController.js +++ b/dist/controller/productController.js @@ -19,14 +19,10 @@ var __rest = (this && this.__rest) || function (s, e) { } return t; }; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); const productService_1 = require("../services/productService"); const superstruct_1 = require("superstruct"); const structs_1 = require("../structs/structs"); -const productRepository_1 = __importDefault(require("../repositories/productRepository")); const errorHandler_1 = require("../libs/Handler/errorHandler"); const removeTool_1 = require("./../libs/removeTool"); class ProductController { @@ -63,52 +59,69 @@ class ProductController { } const userId = req.user ? req.user.userId : null; if (!userId) - return new errorHandler_1.CustomError(404, "UserId Not Found"); + throw new errorHandler_1.CustomError(404, "UserId Not Found"); const products = yield productService_1.productService.getProducts(findOptions, userId); res.send(products); }); this.GetProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { const id = req.params.id; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(400, "id is required"); const _id = parseInt(id); const userId = req.user ? req.user.userId : null; if (!userId) - return new errorHandler_1.CustomError(404, "userId Not Found"); + throw new errorHandler_1.CustomError(404, "userId Not Found"); const product = yield productService_1.productService.getProductById(_id, userId); res.send(product); }); this.PostProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { (0, superstruct_1.assert)(req.body, structs_1.CreateProduct); const userFields = __rest(req.body, []); - const product = yield productRepository_1.default.create(userFields); + const userId = req.user ? req.user.userId : null; + if (!userId) + throw new errorHandler_1.CustomError(404, "userId Not Found"); + const product = yield productService_1.productService.createProducts(Object.assign(Object.assign({}, userFields), { userId })); res.status(201).send(product); }); this.PatchProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { const id = req.params.id; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(400, "id is required"); const _id = parseInt(id); + const userId = req.user ? req.user.userId : null; + if (!userId) + throw new errorHandler_1.CustomError(401, "Unauthorized"); (0, superstruct_1.assert)(req.body, structs_1.PatchProduct); const userFields = (0, removeTool_1.removeUndefined)(req.body); - const Product = yield productRepository_1.default.update(_id, userFields); - res.send(Product); + const { product, notifications } = yield productService_1.productService.updateProduct(_id, userFields); + if (notifications && notifications.length > 0) { + const io = req.app.get('io'); + if (io) { + notifications.forEach((noti) => { + io.to(noti.userId).emit('notification', noti); + }); + } + } + res.send(product); }); this.DeleteProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { const id = req.params.id; if (!id) - return new errorHandler_1.CustomError(404, "id Not Found"); + throw new errorHandler_1.CustomError(400, "id is required"); const _id = parseInt(id); - const Product = yield productRepository_1.default.ondelete(_id); + const userId = req.user ? req.user.userId : null; + if (!userId) + throw new errorHandler_1.CustomError(404, "userId Not Found"); + const Product = yield productService_1.productService.deleteProduct(_id); res.send(Product); }); this.likeProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { if (!req.user) - return new errorHandler_1.CustomError(404, "user Not Found"); + throw new errorHandler_1.CustomError(404, "user Not Found"); const userId = req.user.userId; const productId = req.params.id; if (!productId) - return new errorHandler_1.CustomError(404, "productId Not Found"); + throw new errorHandler_1.CustomError(400, "productId is required"); const _productId = parseInt(productId); const result = yield productService_1.productService.likeProduct(userId, _productId); return res.status(200).json(result); diff --git a/dist/controller/productController.js.map b/dist/controller/productController.js.map index 66c6c2c5..e4dba20a 100644 --- a/dist/controller/productController.js.map +++ b/dist/controller/productController.js.map @@ -1 +1 @@ -{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts b/dist/controller/uploadController.d.ts index 2f0cef25..03fa9134 100644 --- a/dist/controller/uploadController.d.ts +++ b/dist/controller/uploadController.d.ts @@ -1,4 +1,3 @@ import { ExpressRequest, ExpressResponse } from '../libs/constants'; -import { CustomError } from '../libs/Handler/errorHandler'; -export declare function UploadSingleImage(req: ExpressRequest, res: ExpressResponse): CustomError | undefined; +export declare function UploadSingleImage(req: ExpressRequest, res: ExpressResponse): void; //# sourceMappingURL=uploadController.d.ts.map \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts.map b/dist/controller/uploadController.d.ts.map index cada72c6..dc048139 100644 --- a/dist/controller/uploadController.d.ts.map +++ b/dist/controller/uploadController.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,2BAM1E"} \ No newline at end of file +{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,QAM1E"} \ No newline at end of file diff --git a/dist/controller/uploadController.js b/dist/controller/uploadController.js index 41ac5899..b99c5ae1 100644 --- a/dist/controller/uploadController.js +++ b/dist/controller/uploadController.js @@ -4,7 +4,7 @@ exports.UploadSingleImage = UploadSingleImage; const errorHandler_1 = require("../libs/Handler/errorHandler"); function UploadSingleImage(req, res) { if (!req.file) - return new errorHandler_1.CustomError(404, "file not found"); + throw new errorHandler_1.CustomError(404, "file not found"); const { filename } = req.file; const path = `files/${filename}`; res.json({ path }); diff --git a/dist/controller/uploadController.js.map b/dist/controller/uploadController.js.map index a68037dd..1e58faa5 100644 --- a/dist/controller/uploadController.js.map +++ b/dist/controller/uploadController.js.map @@ -1 +1 @@ -{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAGA,8CAMC;AAPD,+DAA2D;AAC3D,SAAgB,iBAAiB,CAAC,GAAmB,EAAE,GAAoB;IACvE,IAAI,CAAC,GAAG,CAAC,IAAI;QACT,OAAO,IAAI,0BAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAClD,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file +{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAGA,8CAMC;AAPD,+DAA2D;AAC3D,SAAgB,iBAAiB,CAAC,GAAmB,EAAE,GAAoB;IACvE,IAAI,CAAC,GAAG,CAAC,IAAI;QACT,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts b/dist/libs/interfaces.d.ts index ee927452..050f7c2d 100644 --- a/dist/libs/interfaces.d.ts +++ b/dist/libs/interfaces.d.ts @@ -2,6 +2,7 @@ import { Prisma } from './../libs/constants'; export interface ProductType { id: number; name: string; + userId: number; description?: string; price: number; tags: string[]; @@ -13,6 +14,7 @@ export interface ProductType { export interface ArticleType { id: number; title: string; + userId: number; content: string; createdAt: Date; comments?: CommentType[]; @@ -23,8 +25,10 @@ export interface CommentType { content: string; createdAt: Date; updatedAt: Date; + userId: number; productId?: number; articleId?: number; + user: UserType; product?: ProductType; article?: ArticleType; } diff --git a/dist/libs/interfaces.d.ts.map b/dist/libs/interfaces.d.ts.map index 1ccd56a5..e0c71028 100644 --- a/dist/libs/interfaces.d.ts.map +++ b/dist/libs/interfaces.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts index 371115b3..20bc8c4a 100644 --- a/dist/main.d.ts +++ b/dist/main.d.ts @@ -1,2 +1,3 @@ -export {}; +declare const app: import("express-serve-static-core").Express; +export default app; //# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/main.d.ts.map b/dist/main.d.ts.map index 28b87d89..4df96fc2 100644 --- a/dist/main.d.ts.map +++ b/dist/main.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""} \ No newline at end of file +{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAiEtB,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js index e5d4ec53..731a45a7 100644 --- a/dist/main.js +++ b/dist/main.js @@ -8,19 +8,57 @@ const cors_1 = __importDefault(require("cors")); const routerManager_1 = require("./Routers/routerManager"); const corsSetUp_1 = require("./libs/corsSetUp"); const errorHandler_1 = __importDefault(require("./libs/Handler/errorHandler")); +const http_1 = require("http"); +const socket_io_1 = require("socket.io"); +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const app = (0, constants_1.EXPRESS)(); +const httpServer = (0, http_1.createServer)(app); +const io = new socket_io_1.Server(httpServer, { + cors: { + origin: (0, corsSetUp_1.getCorsOrigin)(), // Express에서 쓰던 것과 똑같이 맞춰주세요 + methods: ["GET", "POST"], + credentials: true + } +}); +io.use((socket, next) => { + const token = socket.handshake.auth.accessToken; + if (!token) { + return next(new Error('Authentication error')); + } + try { + const payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET); + socket.data.userId = payload.userId; + next(); + } + catch (e) { + next(new Error('Authentication error')); + } +}); +app.set('io', io); app.use((0, cors_1.default)({ origin: (0, corsSetUp_1.getCorsOrigin)(), credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'] })); +app.use(constants_1.EXPRESS.static('public')); app.use(constants_1.EXPRESS.json()); app.use(constants_1.EXPRESS.urlencoded({ extended: true })); app.use('/upload', constants_1.EXPRESS.static('upload')); app.use('/', routerManager_1.RouterManager); app.use(errorHandler_1.default); -app.listen(constants_1.PORT, () => { - console.log(`Server is running`); +// --- 소켓 연결 이벤트 (테스트용) --- +io.on('connection', (socket) => { + console.log('새로운 소켓 연결:', socket.id); + // 로그인하면 해당 유저의 ID로 방을 만들어줌 (알림 기능을 위해 필수) + const userId = socket.data.userId; + if (userId) { + socket.join(userId); + console.log(`User ${userId} joined room`); + } +}); +httpServer.listen(constants_1.PORT, () => { + console.log(`Server is running on port ${constants_1.PORT}`); }); +exports.default = app; //# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map index 4a49c239..accc0fe7 100644 --- a/dist/main.js.map +++ b/dist/main.js.map @@ -1 +1 @@ -{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;AAAA,gDAAiD;AACjD,gDAAwB;AACxB,2DAAwD;AACxD,gDAAiD;AACjD,+EAAuD;AAEvD,MAAM,GAAG,GAAG,IAAA,mBAAO,GAAE,CAAC;AAGtB,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC;IACT,MAAM,EAAE,IAAA,yBAAa,GAAE;IACvB,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC;IAClD,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;CACpD,CAAC,CAAC,CAAC;AAIJ,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,mBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAEhD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAI7C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,6BAAa,CAAC,CAAC;AAI5B,GAAG,CAAC,GAAG,CAAC,sBAAY,CAAC,CAAC;AAEtB,GAAG,CAAC,MAAM,CAAC,gBAAI,EAAE,GAAG,EAAE;IAClB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts b/dist/repositories/articleRepository.d.ts index 8053f1d7..b4a60bf0 100644 --- a/dist/repositories/articleRepository.d.ts +++ b/dist/repositories/articleRepository.d.ts @@ -10,6 +10,7 @@ declare function findById(id: number, userId?: number): Promise<{ id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }>; @@ -24,6 +25,7 @@ declare function findAll(findOptions: ArticleFindOptions, userId: number): Promi id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; })[]>; @@ -31,6 +33,7 @@ declare function create(userFields: ArticlePublicData): Promise<{ id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }>; @@ -38,6 +41,7 @@ declare function update(id: number, data: UpdateArticleData): Promise<{ id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }>; @@ -45,6 +49,7 @@ declare function ondelete(id: number): Promise<{ id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }>; diff --git a/dist/repositories/articleRepository.d.ts.map b/dist/repositories/articleRepository.d.ts.map index f48d6544..f3c5e93c 100644 --- a/dist/repositories/articleRepository.d.ts.map +++ b/dist/repositories/articleRepository.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;GAQlD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;GAQxD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file +{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;GAQlD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;GAQxD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/repositories/commentRepository.d.ts b/dist/repositories/commentRepository.d.ts new file mode 100644 index 00000000..368030f3 --- /dev/null +++ b/dist/repositories/commentRepository.d.ts @@ -0,0 +1,67 @@ +declare function findAll(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; +}[]>; +declare function findById(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; +}>; +declare function create(content: string, userId: number, productId?: number, articleId?: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; +}>; +declare function update(id: number, content: string): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; +}>; +declare function deleteComment(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; +}>; +declare function findProductOwner(productId: number): Promise<{ + name: string; + userId: number; +} | null>; +declare function findArticleOwner(articleId: number): Promise<{ + userId: number; + title: string; +} | null>; +declare function createNotification(userId: number, content: string): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; +}>; +declare const _default: { + findAll: typeof findAll; + findById: typeof findById; + create: typeof create; + update: typeof update; + findProductOwner: typeof findProductOwner; + findArticleOwner: typeof findArticleOwner; + createNotification: typeof createNotification; + delete: typeof deleteComment; +}; +export default _default; +//# sourceMappingURL=commentRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/commentRepository.d.ts.map b/dist/repositories/commentRepository.d.ts.map new file mode 100644 index 00000000..6abaeaad --- /dev/null +++ b/dist/repositories/commentRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/commentRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;;;;;;;KAyB5H;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;GAIjC;AAED,iBAAe,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;GAS5F;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;GAKhD;AAED,iBAAe,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;GAItC;AAED,iBAAe,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;UAKhD;AAGD,iBAAe,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;UAKhD;AAGD,iBAAe,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;GAQhE;;;;;;;;;;;AACD,wBASE"} \ No newline at end of file diff --git a/dist/repositories/commentRepository.js b/dist/repositories/commentRepository.js new file mode 100644 index 00000000..d6322db6 --- /dev/null +++ b/dist/repositories/commentRepository.js @@ -0,0 +1,112 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findAll(take, cursor, productId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + const whereClause = {}; + if (productId) { + whereClause.productId = productId; + } + else if (articleId) { + whereClause.articleId = articleId; + } + const findOptions = { + take: take, + where: whereClause, + orderBy: { + createdAt: 'desc', + }, + }; + if (cursor) { + findOptions.skip = 1; + findOptions.cursor = { + id: cursor, + }; + } + return constants_1.prismaClient.comment.findMany(findOptions); + }); +} +function findById(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.comment.findUniqueOrThrow({ + where: { id }, + }); + }); +} +function create(content, userId, productId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.comment.create({ + data: { + content, + userId, + productId, + articleId, + }, + }); + }); +} +function update(id, content) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.comment.update({ + where: { id }, + data: { content }, + }); + }); +} +function deleteComment(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.comment.delete({ + where: { id }, + }); + }); +} +// [추가] 상품 주인 찾기 +function findProductOwner(productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.product.findUnique({ + where: { id: productId }, + select: { userId: true, name: true } // 주인 ID와 상품명만 가져옴 + }); + }); +} +// [추가] 아티클 주인 찾기 +function findArticleOwner(articleId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.article.findUnique({ + where: { id: articleId }, + select: { userId: true, title: true } // 주인 ID와 제목만 가져옴 + }); + }); +} +// [추가] 알림 생성 +function createNotification(userId, content) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.create({ + data: { + userId, + content, + isRead: false, + } + }); + }); +} +exports.default = { + findAll, + findById, + create, + update, + findProductOwner, + findArticleOwner, + createNotification, + delete: deleteComment, +}; +//# sourceMappingURL=commentRepository.js.map \ No newline at end of file diff --git a/dist/repositories/commentRepository.js.map b/dist/repositories/commentRepository.js.map new file mode 100644 index 00000000..872acb1a --- /dev/null +++ b/dist/repositories/commentRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentRepository.js","sourceRoot":"","sources":["../../src/repositories/commentRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAyD;AAEzD,SAAe,OAAO,CAAC,IAAY,EAAE,MAA0B,EAAE,SAA6B,EAAE,SAA6B;;QACzH,MAAM,WAAW,GAA6B,EAAE,CAAC;QAEjD,IAAI,SAAS,EAAE,CAAC;YACZ,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC;QACtC,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACnB,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC;QACtC,CAAC;QAED,MAAM,WAAW,GAA+B;YAC5C,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;gBACL,SAAS,EAAE,MAAM;aACpB;SACJ,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC;YACrB,WAAW,CAAC,MAAM,GAAG;gBACjB,EAAE,EAAE,MAAM;aACb,CAAC;QACN,CAAC;QAED,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,OAAe,EAAE,MAAc,EAAE,SAAkB,EAAE,SAAkB;;QACzF,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,IAAI,EAAE;gBACF,OAAO;gBACP,MAAM;gBACN,SAAS;gBACT,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,OAAe;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,OAAO,EAAE;SACpB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,aAAa,CAAC,EAAU;;QACnC,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AACD,gBAAgB;AAChB,SAAe,gBAAgB,CAAC,SAAiB;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,kBAAkB;SAC1D,CAAC,CAAC;IACP,CAAC;CAAA;AAED,iBAAiB;AACjB,SAAe,gBAAgB,CAAC,SAAiB;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,iBAAiB;SAC1D,CAAC,CAAC;IACP,CAAC;CAAA;AAED,aAAa;AACb,SAAe,kBAAkB,CAAC,MAAc,EAAE,OAAe;;QAC7D,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,IAAI,EAAE;gBACF,MAAM;gBACN,OAAO;gBACP,MAAM,EAAE,KAAK;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AACD,kBAAe;IACX,OAAO;IACP,QAAQ;IACR,MAAM;IACN,MAAM;IACN,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,MAAM,EAAE,aAAa;CACxB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/notificationRepository.d.ts b/dist/repositories/notificationRepository.d.ts new file mode 100644 index 00000000..880acfdf --- /dev/null +++ b/dist/repositories/notificationRepository.d.ts @@ -0,0 +1,30 @@ +declare function findAll(userId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; +}[]>; +declare function countUnread(userId: number): Promise; +declare function findById(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; +} | null>; +declare function markAsRead(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; +}>; +declare const _default: { + findAll: typeof findAll; + countUnread: typeof countUnread; + findById: typeof findById; + markAsRead: typeof markAsRead; +}; +export default _default; +//# sourceMappingURL=notificationRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/notificationRepository.d.ts.map b/dist/repositories/notificationRepository.d.ts.map new file mode 100644 index 00000000..ff96ed0c --- /dev/null +++ b/dist/repositories/notificationRepository.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/notificationRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,OAAO,CAAC,MAAM,EAAE,MAAM;;;;;;KAKpC;AAED,iBAAe,WAAW,CAAC,MAAM,EAAE,MAAM,mBAOxC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;UAIjC;AAED,iBAAe,UAAU,CAAC,EAAE,EAAE,MAAM;;;;;;GAKnC;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/notificationRepository.js b/dist/repositories/notificationRepository.js new file mode 100644 index 00000000..b3ff7ddc --- /dev/null +++ b/dist/repositories/notificationRepository.js @@ -0,0 +1,52 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const constants_1 = require("../libs/constants"); +function findAll(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.findMany({ + where: { userId }, + orderBy: { createdAt: 'desc' }, + }); + }); +} +function countUnread(userId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.count({ + where: { + userId, + isRead: false, + }, + }); + }); +} +function findById(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.findUnique({ + where: { id }, + }); + }); +} +function markAsRead(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.update({ + where: { id }, + data: { isRead: true }, + }); + }); +} +exports.default = { + findAll, + countUnread, + findById, + markAsRead, +}; +//# sourceMappingURL=notificationRepository.js.map \ No newline at end of file diff --git a/dist/repositories/notificationRepository.js.map b/dist/repositories/notificationRepository.js.map new file mode 100644 index 00000000..ec39f4dd --- /dev/null +++ b/dist/repositories/notificationRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationRepository.js","sourceRoot":"","sources":["../../src/repositories/notificationRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,OAAO,CAAC,MAAc;;QACjC,OAAO,wBAAY,CAAC,YAAY,CAAC,QAAQ,CAAC;YACtC,KAAK,EAAE,EAAE,MAAM,EAAE;YACjB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SACjC,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,MAAc;;QACrC,OAAO,wBAAY,CAAC,YAAY,CAAC,KAAK,CAAC;YACnC,KAAK,EAAE;gBACH,MAAM;gBACN,MAAM,EAAE,KAAK;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,YAAY,CAAC,UAAU,CAAC;YACxC,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,UAAU,CAAC,EAAU;;QAChC,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,OAAO;IACP,WAAW;IACX,QAAQ;IACR,UAAU;CACb,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts b/dist/repositories/productLikeRepository.d.ts index 5eb660bc..785af337 100644 --- a/dist/repositories/productLikeRepository.d.ts +++ b/dist/repositories/productLikeRepository.d.ts @@ -22,6 +22,7 @@ declare function findLikedProductsByUserId(userId: number): Promise<({ createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; diff --git a/dist/repositories/productLikeRepository.d.ts.map b/dist/repositories/productLikeRepository.d.ts.map index 2fb02b5d..9620c392 100644 --- a/dist/repositories/productLikeRepository.d.ts.map +++ b/dist/repositories/productLikeRepository.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAStD;;;;;;;AAED,wBAKE"} \ No newline at end of file +{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;MAStD;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts b/dist/repositories/productRepository.d.ts index b93ce191..9f6a2055 100644 --- a/dist/repositories/productRepository.d.ts +++ b/dist/repositories/productRepository.d.ts @@ -1,9 +1,30 @@ import { ProductPublicData, ProductFindOptions, UpdateProductData } from './../libs/interfaces'; +declare function findByIdSimple(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + userId: number; + description: string | null; + price: number; + tags: string[]; +} | null>; +declare function findLikers(productId: number): Promise<{ + userId: number; +}[]>; +declare function createNotification(userId: number, content: string): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; +}>; declare function findByUserId(userId: number): Promise<{ id: number; createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -20,6 +41,7 @@ declare function findById(id: number, userId?: number): Promise<{ createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -36,6 +58,7 @@ declare function findAll(findOptions: ProductFindOptions, userId: number): Promi createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -45,6 +68,7 @@ declare function update(id: number, data: UpdateProductData): Promise<{ createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -54,6 +78,7 @@ declare function create(userFields: ProductPublicData): Promise<{ createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -63,6 +88,7 @@ declare function ondelete(id: number): Promise<{ createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -74,6 +100,9 @@ declare const _default: { create: typeof create; ondelete: typeof ondelete; findByUserId: typeof findByUserId; + findByIdSimple: typeof findByIdSimple; + findLikers: typeof findLikers; + createNotification: typeof createNotification; }; export default _default; //# sourceMappingURL=productRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts.map b/dist/repositories/productRepository.d.ts.map index b426899b..d1492e92 100644 --- a/dist/repositories/productRepository.d.ts.map +++ b/dist/repositories/productRepository.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAKhG,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;;GAQxD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;GAOlD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;GAMjC;;;;;;;;;AAED,wBAOE"} \ No newline at end of file +{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGhG,iBAAe,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAIvC;AACD,iBAAe,UAAU,CAAC,SAAS,EAAE,MAAM;;KAK1C;AACD,iBAAe,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;GAQhE;AACD,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;;;GAQxD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;;GAOlD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;GAMjC;;;;;;;;;;;;AAED,wBAUE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.js b/dist/repositories/productRepository.js index 1916a204..d808f87f 100644 --- a/dist/repositories/productRepository.js +++ b/dist/repositories/productRepository.js @@ -21,6 +21,32 @@ var __rest = (this && this.__rest) || function (s, e) { }; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../libs/constants"); +function findByIdSimple(id) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.product.findUnique({ + where: { id }, + }); + }); +} +function findLikers(productId) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.productLike.findMany({ + where: { productId }, + select: { userId: true } // 유저 ID만 쏙 뽑아옵니다. + }); + }); +} +function createNotification(userId, content) { + return __awaiter(this, void 0, void 0, function* () { + return constants_1.prismaClient.notification.create({ + data: { + userId, + content, + isRead: false, // 안 읽음 상태로 생성 + } + }); + }); +} function findByUserId(userId) { return __awaiter(this, void 0, void 0, function* () { return constants_1.prismaClient.product.findMany({ @@ -85,6 +111,9 @@ exports.default = { update, create, ondelete, - findByUserId + findByUserId, + findByIdSimple, + findLikers, + createNotification }; //# sourceMappingURL=productRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productRepository.js.map b/dist/repositories/productRepository.js.map index b67fd5f4..b172031f 100644 --- a/dist/repositories/productRepository.js.map +++ b/dist/repositories/productRepository.js.map @@ -1 +1 @@ -{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAMjD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAChE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;CACf,CAAC"} \ No newline at end of file +{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAIjD,SAAe,cAAc,CAAC,EAAU;;QACpC,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,UAAU,CAAC,SAAiB;;QACvC,OAAO,wBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,kBAAkB;SAC9C,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,kBAAkB,CAAC,MAAc,EAAE,OAAe;;QAC7D,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,IAAI,EAAE;gBACF,MAAM;gBACN,OAAO;gBACP,MAAM,EAAE,KAAK,EAAE,cAAc;aAChC;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAChE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,UAAU;IACV,kBAAkB;CACrB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.d.ts b/dist/services/articleService.d.ts index b2f37a7b..6b6e94e7 100644 --- a/dist/services/articleService.d.ts +++ b/dist/services/articleService.d.ts @@ -1,4 +1,4 @@ -import { ArticleFindOptions } from './../libs/interfaces'; +import { ArticleFindOptions, ArticlePublicData, UpdateArticleData } from './../libs/interfaces'; declare class ArticleService { likeArticle(userId: number, articleId: number): Promise<{ liked: boolean; @@ -8,6 +8,7 @@ declare class ArticleService { id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }>; @@ -16,9 +17,34 @@ declare class ArticleService { id: number; createdAt: Date; updatedAt: Date; + userId: number; title: string; content: string; }[]>; + postArticle(userFields: ArticlePublicData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + title: string; + content: string; + }>; + patchArticleById(id: number, userFields: UpdateArticleData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + title: string; + content: string; + }>; + deleteArticleById(id: number, userId: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + userId: number; + title: string; + content: string; + }>; } export declare const articleService: ArticleService; export {}; diff --git a/dist/services/articleService.d.ts.map b/dist/services/articleService.d.ts.map index 6f1f8383..e648b3ce 100644 --- a/dist/services/articleService.d.ts.map +++ b/dist/services/articleService.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file +{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGhG,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAO3D,WAAW,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;IAGzC,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB;;;;;;;;IAO1D,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAQrD;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.js b/dist/services/articleService.js index 23352590..d1c21267 100644 --- a/dist/services/articleService.js +++ b/dist/services/articleService.js @@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.articleService = void 0; const articleLikeRepository_1 = __importDefault(require("../repositories/articleLikeRepository")); const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); +const errorHandler_1 = require("../libs/Handler/errorHandler"); class ArticleService { likeArticle(userId, articleId) { return __awaiter(this, void 0, void 0, function* () { @@ -54,6 +55,29 @@ class ArticleService { }); }); } + postArticle(userFields) { + return __awaiter(this, void 0, void 0, function* () { + return yield articleRepository_1.default.create(userFields); + }); + } + patchArticleById(id, userFields) { + return __awaiter(this, void 0, void 0, function* () { + const article = yield articleRepository_1.default.findById(id); + if (article.userId !== userFields.userId) { + throw new errorHandler_1.CustomError(403, "권한이 없습니다."); + } + return yield articleRepository_1.default.update(id, userFields); + }); + } + deleteArticleById(id, userId) { + return __awaiter(this, void 0, void 0, function* () { + const article = yield articleRepository_1.default.findById(id); + if (article.userId !== userId) { + throw new errorHandler_1.CustomError(403, "권한이 없습니다."); + } + return yield articleRepository_1.default.ondelete(id); + }); + } } exports.articleService = new ArticleService(); //# sourceMappingURL=articleService.js.map \ No newline at end of file diff --git a/dist/services/articleService.js.map b/dist/services/articleService.js.map index 32aeee3b..e3ccd723 100644 --- a/dist/services/articleService.js.map +++ b/dist/services/articleService.js.map @@ -1 +1 @@ -{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kGAA0E;AAC1E,0FAAkE;AAGlE,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;YAED,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,kGAA0E;AAC1E,0FAAkE;AAElE,+DAA2D;AAE3D,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;YAED,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;IACK,WAAW,CAAC,UAA6B;;YAC3C,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC;KAAA;IACK,gBAAgB,CAAC,EAAU,EAAE,UAA6B;;YAC5D,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrD,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;gBACvC,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC1D,CAAC;KAAA;IACK,iBAAiB,CAAC,EAAU,EAAE,MAAc;;YAC9C,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC5C,CAAC;YAED,OAAO,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/commentService.d.ts b/dist/services/commentService.d.ts new file mode 100644 index 00000000..d169e641 --- /dev/null +++ b/dist/services/commentService.d.ts @@ -0,0 +1,57 @@ +declare class CommentService { + getComments(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined): Promise<{ + comments: { + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; + }[]; + nextCursor: number | null; + }>; + getCommentById(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; + }>; + createComment(userId: number, content: string, productId?: number, articleId?: number): Promise<{ + comment: { + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; + }; + notification: { + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; + } | null; + }>; + updateComment(id: number, content: string): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; + }>; + deleteComment(id: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + productId: number | null; + articleId: number | null; + }>; +} +export declare const commentService: CommentService; +export {}; +//# sourceMappingURL=commentService.d.ts.map \ No newline at end of file diff --git a/dist/services/commentService.d.ts.map b/dist/services/commentService.d.ts.map new file mode 100644 index 00000000..554d0e1f --- /dev/null +++ b/dist/services/commentService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"commentService.d.ts","sourceRoot":"","sources":["../../src/services/commentService.ts"],"names":[],"mappings":"AAGA,cAAM,cAAc;IACV,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;;;;;;;;;;;IAUlH,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;IAIzB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;IA8CrF,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;IAQzC,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;;CAGjC;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/commentService.js b/dist/services/commentService.js new file mode 100644 index 00000000..aa1e6bd2 --- /dev/null +++ b/dist/services/commentService.js @@ -0,0 +1,90 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commentService = void 0; +const commentRepository_1 = __importDefault(require("../repositories/commentRepository")); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +class CommentService { + getComments(take, cursor, productId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + const comments = yield commentRepository_1.default.findAll(take, cursor, productId, articleId); + let nextCursor = null; + if (comments.length > 0 && comments.length === take) { + nextCursor = comments[comments.length - 1].id; + } + return { comments, nextCursor }; + }); + } + getCommentById(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield commentRepository_1.default.findById(id); + }); + } + createComment(userId, content, productId, articleId) { + return __awaiter(this, void 0, void 0, function* () { + if ((productId && articleId) || (!productId && !articleId)) { + throw new errorHandler_1.CustomError(400, 'Comment must belong to EITHER a Product OR an Article.'); + } + if (!content || content.trim() === '') { + throw new errorHandler_1.CustomError(400, 'Content cannot be empty.'); + } + // 2. 댓글 생성 (DB 저장) + const comment = yield commentRepository_1.default.create(content, userId, productId, articleId); + // 3. 알림 로직 시작 + let notification = null; + let ownerId; + let targetName = ""; + // 상품 댓글인 경우 + if (productId) { + const product = yield commentRepository_1.default.findProductOwner(productId); + if (product) { + ownerId = product.userId; // 상품 주인 ID + targetName = product.name; + } + } + // 아티클 댓글인 경우 + else if (articleId) { + const article = yield commentRepository_1.default.findArticleOwner(articleId); + if (article) { + ownerId = article.userId; // 아티클 주인 ID + targetName = article.title; + } + } + // 4. 작성자가 본인이 아닐 경우에만 알림 생성 + // (ownerId가 존재하고, 댓글 쓴 사람(userId)과 다를 때) + if (ownerId && ownerId !== userId) { + const message = `작성하신 글 '${targetName}'에 새로운 댓글이 달렸습니다: ${content}`; + // DB에 알림 저장 + notification = yield commentRepository_1.default.createNotification(ownerId, message); + } + // 5. 댓글 정보와 알림 정보(있으면)를 함께 반환 + return { comment, notification }; + }); + } + updateComment(id, content) { + return __awaiter(this, void 0, void 0, function* () { + if (content !== undefined && content.trim() === '') { + throw new errorHandler_1.CustomError(400, 'Content cannot be empty.'); + } + return yield commentRepository_1.default.update(id, content); + }); + } + deleteComment(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield commentRepository_1.default.delete(id); + }); + } +} +exports.commentService = new CommentService(); +//# sourceMappingURL=commentService.js.map \ No newline at end of file diff --git a/dist/services/commentService.js.map b/dist/services/commentService.js.map new file mode 100644 index 00000000..7bbc664d --- /dev/null +++ b/dist/services/commentService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commentService.js","sourceRoot":"","sources":["../../src/services/commentService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/services/notificationService.d.ts b/dist/services/notificationService.d.ts new file mode 100644 index 00000000..3b260f15 --- /dev/null +++ b/dist/services/notificationService.d.ts @@ -0,0 +1,22 @@ +declare class NotificationService { + getNotifications(userId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; + }[]>; + getUnreadCount(userId: number): Promise<{ + count: number; + }>; + readNotification(userId: number, notificationId: number): Promise<{ + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; + }>; +} +export declare const notificationService: NotificationService; +export {}; +//# sourceMappingURL=notificationService.d.ts.map \ No newline at end of file diff --git a/dist/services/notificationService.d.ts.map b/dist/services/notificationService.d.ts.map new file mode 100644 index 00000000..df8954bc --- /dev/null +++ b/dist/services/notificationService.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationService.d.ts","sourceRoot":"","sources":["../../src/services/notificationService.ts"],"names":[],"mappings":"AAGA,cAAM,mBAAmB;IACf,gBAAgB,CAAC,MAAM,EAAE,MAAM;;;;;;;IAI/B,cAAc,CAAC,MAAM,EAAE,MAAM;;;IAK7B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;;;;;;CAiBhE;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"} \ No newline at end of file diff --git a/dist/services/notificationService.js b/dist/services/notificationService.js new file mode 100644 index 00000000..018f4c83 --- /dev/null +++ b/dist/services/notificationService.js @@ -0,0 +1,47 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.notificationService = void 0; +const notificationRepository_1 = __importDefault(require("../repositories/notificationRepository")); +const errorHandler_1 = require("../libs/Handler/errorHandler"); +class NotificationService { + getNotifications(userId) { + return __awaiter(this, void 0, void 0, function* () { + return yield notificationRepository_1.default.findAll(userId); + }); + } + getUnreadCount(userId) { + return __awaiter(this, void 0, void 0, function* () { + const count = yield notificationRepository_1.default.countUnread(userId); + return { count }; + }); + } + readNotification(userId, notificationId) { + return __awaiter(this, void 0, void 0, function* () { + const notification = yield notificationRepository_1.default.findById(notificationId); + if (!notification) { + throw new errorHandler_1.CustomError(404, 'Notification not found'); + } + if (notification.userId !== userId) { + throw new errorHandler_1.CustomError(403, 'Forbidden: Not your notification'); + } + if (notification.isRead) { + return notification; + } + return yield notificationRepository_1.default.markAsRead(notificationId); + }); + } +} +exports.notificationService = new NotificationService(); +//# sourceMappingURL=notificationService.js.map \ No newline at end of file diff --git a/dist/services/notificationService.js.map b/dist/services/notificationService.js.map new file mode 100644 index 00000000..7381a48a --- /dev/null +++ b/dist/services/notificationService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notificationService.js","sourceRoot":"","sources":["../../src/services/notificationService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,oGAA4E;AAC5E,+DAA2D;AAE3D,MAAM,mBAAmB;IACf,gBAAgB,CAAC,MAAc;;YACjC,OAAO,MAAM,gCAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;KAAA;IAEK,cAAc,CAAC,MAAc;;YAC/B,MAAM,KAAK,GAAG,MAAM,gCAAsB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC/D,OAAO,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAEK,gBAAgB,CAAC,MAAc,EAAE,cAAsB;;YACzD,MAAM,YAAY,GAAG,MAAM,gCAAsB,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;YAE3E,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;YACzD,CAAC;YAED,IAAI,YAAY,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACjC,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,kCAAkC,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;gBACtB,OAAO,YAAY,CAAC;YACxB,CAAC;YAED,OAAO,MAAM,gCAAsB,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QACnE,CAAC;KAAA;CACJ;AAEY,QAAA,mBAAmB,GAAG,IAAI,mBAAmB,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.d.ts b/dist/services/productService.d.ts index 347d88bb..73aa51e1 100644 --- a/dist/services/productService.d.ts +++ b/dist/services/productService.d.ts @@ -1,4 +1,4 @@ -import { ProductFindOptions } from '../libs/interfaces'; +import { ProductFindOptions, ProductPublicData } from '../libs/interfaces'; declare class ProductService { likeProduct(userId: number, productId: number): Promise<{ liked: boolean; @@ -9,6 +9,7 @@ declare class ProductService { createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -19,10 +20,50 @@ declare class ProductService { createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; }[]>; + createProducts(userFields: ProductPublicData): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + userId: number; + description: string | null; + price: number; + tags: string[]; + }>; + updateProduct(id: number, userFields: ProductPublicData): Promise<{ + product: { + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + userId: number; + description: string | null; + price: number; + tags: string[]; + }; + notifications: { + id: number; + createdAt: Date; + userId: number; + content: string; + isRead: boolean; + }[]; + }>; + deleteProduct(id: number): Promise<{ + id: number; + createdAt: Date; + updatedAt: Date; + name: string; + userId: number; + description: string | null; + price: number; + tags: string[]; + }>; } export declare const productService: ProductService; export {}; diff --git a/dist/services/productService.d.ts.map b/dist/services/productService.d.ts.map index 49eab074..0135af6c 100644 --- a/dist/services/productService.d.ts.map +++ b/dist/services/productService.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;CAOpE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file +{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI3E,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;IAO3D,cAAc,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;;;IAI5C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;IAuBvD,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;CAIjC;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.js b/dist/services/productService.js index 045d62ec..86408470 100644 --- a/dist/services/productService.js +++ b/dist/services/productService.js @@ -56,6 +56,36 @@ class ProductService { }); }); } + createProducts(userFields) { + return __awaiter(this, void 0, void 0, function* () { + const product = yield productRepository_1.default.create(userFields); + return product; + }); + } + updateProduct(id, userFields) { + return __awaiter(this, void 0, void 0, function* () { + const oldProduct = yield productRepository_1.default.findByIdSimple(id); + const updatedProduct = yield productRepository_1.default.update(id, userFields); + // 3. 알림 로직: 가격이 존재하고, 이전 가격과 다를 때 + let notifications = []; // 컨트롤러로 보낼 알림 목록 + if (oldProduct && userFields.price !== undefined && oldProduct.price !== userFields.price) { + // 3-1. 찜한 유저들 찾기 + const likers = yield productRepository_1.default.findLikers(id); + // 3-2. 각 유저에게 알림 DB 저장 (Promise.all로 병렬 처리) + notifications = yield Promise.all(likers.map((liker) => __awaiter(this, void 0, void 0, function* () { + const message = `찜한 상품 '${updatedProduct.name}'의 가격이 변경되었습니다. (${oldProduct.price}원 -> ${updatedProduct.price}원)`; + return yield productRepository_1.default.createNotification(liker.userId, message); + }))); + } + // 4. 결과 반환 (기존에는 product만 줬지만, 이제 알림 목록도 같이 줌) + return { product: updatedProduct, notifications }; + }); + } + deleteProduct(id) { + return __awaiter(this, void 0, void 0, function* () { + return yield productRepository_1.default.ondelete(id); + }); + } } exports.productService = new ProductService(); //# sourceMappingURL=productService.js.map \ No newline at end of file diff --git a/dist/services/productService.js.map b/dist/services/productService.js.map index 865751b4..ca70692a 100644 --- a/dist/services/productService.js.map +++ b/dist/services/productService.js.map @@ -1 +1 @@ -{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,kGAA0E;AAI1E,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,kGAA0E;AAK1E,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;QACL,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;IACK,cAAc,CAAC,UAA6B;;YAC9C,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC3D,OAAO,OAAO,CAAC;QACnB,CAAC;KAAA;IACK,aAAa,CAAC,EAAU,EAAE,UAA6B;;YACzD,MAAM,UAAU,GAAG,MAAM,2BAAiB,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAE9D,MAAM,cAAc,GAAG,MAAM,2BAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YACtE,kCAAkC;YAClC,IAAI,aAAa,GAAmB,EAAE,CAAC,CAAC,iBAAiB;YAEzD,IAAI,UAAU,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC;gBACxF,iBAAiB;gBACjB,MAAM,MAAM,GAAG,MAAM,2BAAiB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAEtD,4CAA4C;gBAC5C,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,MAAM,CAAC,GAAG,CAAC,CAAO,KAAK,EAAE,EAAE;oBACvB,MAAM,OAAO,GAAG,UAAU,cAAc,CAAC,IAAI,oBAAoB,UAAU,CAAC,KAAK,QAAQ,cAAc,CAAC,KAAK,IAAI,CAAC;oBAClH,OAAO,MAAM,2BAAiB,CAAC,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC7E,CAAC,CAAA,CAAC,CACL,CAAC;YACN,CAAC;YAED,+CAA+C;YAC/C,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;QACtD,CAAC;KAAA;IACK,aAAa,CAAC,EAAU;;YAC1B,OAAO,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;KAAA;CAEJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.d.ts b/dist/services/userService.d.ts index 85cf2780..bbb78c22 100644 --- a/dist/services/userService.d.ts +++ b/dist/services/userService.d.ts @@ -11,6 +11,7 @@ declare class UserService { createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; @@ -21,6 +22,7 @@ declare class UserService { createdAt: Date; updatedAt: Date; name: string; + userId: number; description: string | null; price: number; tags: string[]; diff --git a/dist/services/userService.d.ts.map b/dist/services/userService.d.ts.map index 263cea90..a30eb9f9 100644 --- a/dist/services/userService.d.ts.map +++ b/dist/services/userService.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQxF,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAezD,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc;IAKlD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IASjE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAQhD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAS3E,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IAiBjF,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;IAG9C,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUpD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file +{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQxF,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAezD,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc;IAKlD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IASjE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAQhD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAS3E,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IAiBjF,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;IAG9C,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUpD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 13bc0ff3..bf7379dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@aws-sdk/client-s3": "^3.985.0", "@prisma/client": "^6.18.0", "bcrypt": "^6.0.0", "cors": "^2.8.5", @@ -44,1242 +45,2872 @@ "typescript": "^5.9.3" } }, - "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", - "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/core": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", - "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-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.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.28.6" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-s3": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.985.0.tgz", + "integrity": "sha512-S9TqjzzZEEIKBnC7yFpvqM7CG9ALpY5qhQ5BnDBJtdG20NoGpjKLGUUfD2wmZItuhbrcM4Z8c6m6Fg0XYIOVvw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/credential-provider-node": "^3.972.6", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", + "@aws-sdk/middleware-expect-continue": "^3.972.3", + "@aws-sdk/middleware-flexible-checksums": "^3.972.5", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-location-constraint": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-sdk-s3": "^3.972.7", + "@aws-sdk/middleware-ssec": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/signature-v4-multi-region": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.985.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.5", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.1", + "@smithy/eventstream-serde-browser": "^4.2.8", + "@smithy/eventstream-serde-config-resolver": "^4.3.8", + "@smithy/eventstream-serde-node": "^4.2.8", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-blob-browser": "^4.2.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/hash-stream-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/md5-js": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-retry": "^4.4.30", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.29", + "@smithy/util-defaults-mode-node": "^4.2.32", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.985.0.tgz", + "integrity": "sha512-81J8iE8MuXhdbMfIz4sWFj64Pe41bFi/uqqmqOC5SlGv+kwoyLsyKS/rH2tW2t5buih4vTUxskRjxlqikTD4oQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.985.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.5", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-retry": "^4.4.30", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.29", + "@smithy/util-defaults-mode-node": "^4.2.32", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.7.tgz", + "integrity": "sha512-wNZZQQNlJ+hzD49cKdo+PY6rsTDElO8yDImnrI69p2PLBa7QomeUKAJWYp9xnaR38nlHqWhMHZuYLCQ3oSX+xg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/xml-builder": "^3.972.4", + "@smithy/core": "^3.22.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.0.tgz", + "integrity": "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.5.tgz", + "integrity": "sha512-LxJ9PEO4gKPXzkufvIESUysykPIdrV7+Ocb9yAhbhJLE4TiAYqbCVUE+VuKP1leGR1bBfjWjYgSV5MxprlX3mQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.7.tgz", + "integrity": "sha512-L2uOGtvp2x3bTcxFTpSM+GkwFIPd8pHfGWO1764icMbo7e5xJh0nfhx1UwkXLnwvocTNEf8A7jISZLYjUSNaTg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.11", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.5.tgz", + "integrity": "sha512-SdDTYE6jkARzOeL7+kudMIM4DaFnP5dZVeatzw849k4bSXDdErDS188bgeNzc/RA2WGrlEpsqHUKP6G7sVXhZg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/credential-provider-env": "^3.972.5", + "@aws-sdk/credential-provider-http": "^3.972.7", + "@aws-sdk/credential-provider-login": "^3.972.5", + "@aws-sdk/credential-provider-process": "^3.972.5", + "@aws-sdk/credential-provider-sso": "^3.972.5", + "@aws-sdk/credential-provider-web-identity": "^3.972.5", + "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.5.tgz", + "integrity": "sha512-uYq1ILyTSI6ZDCMY5+vUsRM0SOCVI7kaW4wBrehVVkhAxC6y+e9rvGtnoZqCOWL1gKjTMouvsf4Ilhc5NCg1Aw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.6.tgz", + "integrity": "sha512-DZ3CnAAtSVtVz+G+ogqecaErMLgzph4JH5nYbHoBMgBkwTUV+SUcjsjOJwdBJTHu3Dm6l5LBYekZoU2nDqQk2A==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/credential-provider-env": "^3.972.5", + "@aws-sdk/credential-provider-http": "^3.972.7", + "@aws-sdk/credential-provider-ini": "^3.972.5", + "@aws-sdk/credential-provider-process": "^3.972.5", + "@aws-sdk/credential-provider-sso": "^3.972.5", + "@aws-sdk/credential-provider-web-identity": "^3.972.5", + "@aws-sdk/types": "^3.973.1", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.5.tgz", + "integrity": "sha512-HDKF3mVbLnuqGg6dMnzBf1VUOywE12/N286msI9YaK9mEIzdsGCtLTvrDhe3Up0R9/hGFbB+9l21/TwF5L1C6g==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.5.tgz", + "integrity": "sha512-8urj3AoeNeQisjMmMBhFeiY2gxt6/7wQQbEGun0YV/OaOOiXrIudTIEYF8ZfD+NQI6X1FY5AkRsx6O/CaGiybA==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@aws-sdk/client-sso": "3.985.0", + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/token-providers": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.5.tgz", + "integrity": "sha512-OK3cULuJl6c+RcDZfPpaK5o3deTOnKZbxm7pzhFNGA3fI2hF9yDih17fGRazJzGGWaDVlR9ejZrpDef4DJCEsw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.3.tgz", + "integrity": "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.3.tgz", + "integrity": "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.972.5.tgz", + "integrity": "sha512-SF/1MYWx67OyCrLA4icIpWUfCkdlOi8Y1KecQ9xYxkL10GMjVdPTGPnYhAg0dw5U43Y9PVUWhAV2ezOaG+0BLg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/crc64-nvme": "3.972.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz", + "integrity": "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.3.tgz", + "integrity": "sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz", + "integrity": "sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz", + "integrity": "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/types": "^3.973.1", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.7.tgz", + "integrity": "sha512-VtZ7tMIw18VzjG+I6D6rh2eLkJfTtByiFoCIauGDtTTPBEUMQUiGaJ/zZrPlCY6BsvLLeFKz3+E5mntgiOWmIg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-arn-parser": "^3.972.2", + "@smithy/core": "^3.22.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.3.tgz", + "integrity": "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.7.tgz", + "integrity": "sha512-HUD+geASjXSCyL/DHPQc/Ua7JhldTcIglVAoCV8kiVm99IaFSlAbTvEnyhZwdE6bdFyTL+uIaWLaCFSRsglZBQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.985.0", + "@smithy/core": "^3.22.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20.0.0" } }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/nested-clients": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.985.0.tgz", + "integrity": "sha512-TsWwKzb/2WHafAY0CE7uXgLj0FmnkBTgfioG9HO+7z/zCPcl1+YU+i7dW4o0y+aFxFgxTMG+ExBQpqT/k2ao8g==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/middleware-host-header": "^3.972.3", + "@aws-sdk/middleware-logger": "^3.972.3", + "@aws-sdk/middleware-recursion-detection": "^3.972.3", + "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/region-config-resolver": "^3.972.3", + "@aws-sdk/types": "^3.973.1", + "@aws-sdk/util-endpoints": "3.985.0", + "@aws-sdk/util-user-agent-browser": "^3.972.3", + "@aws-sdk/util-user-agent-node": "^3.972.5", + "@smithy/config-resolver": "^4.4.6", + "@smithy/core": "^3.22.1", + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/hash-node": "^4.2.8", + "@smithy/invalid-dependency": "^4.2.8", + "@smithy/middleware-content-length": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-retry": "^4.4.30", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.29", + "@smithy/util-defaults-mode-node": "^4.2.32", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz", + "integrity": "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.1", + "@smithy/config-resolver": "^4.4.6", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.985.0.tgz", + "integrity": "sha512-W6hTSOPiSbh4IdTYVxN7xHjpCh0qvfQU1GKGBzGQm0ZEIOaMmWqiDEvFfyGYKmfBvumT8vHKxQRTX0av9omtIg==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", - "debug": "^4.3.1" + "@aws-sdk/middleware-sdk-s3": "^3.972.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/protocol-http": "^5.3.8", + "@smithy/signature-v4": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/token-providers": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.985.0.tgz", + "integrity": "sha512-+hwpHZyEq8k+9JL2PkE60V93v2kNhUIv7STFt+EAez1UJsJOQDhc5LpzEX66pNjclI5OTwBROs/DhJjC/BtMjQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@aws-sdk/core": "^3.973.7", + "@aws-sdk/nested-clients": "3.985.0", + "@aws-sdk/types": "^3.973.1", + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=20.0.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/types": { + "version": "3.973.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", + "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=20.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.2.tgz", + "integrity": "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==", + "license": "Apache-2.0", "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.985.0.tgz", + "integrity": "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.4.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-endpoints": "^3.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", + "integrity": "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz", + "integrity": "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==", + "license": "Apache-2.0", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@aws-sdk/types": "^3.973.1", + "@smithy/types": "^4.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.5.tgz", + "integrity": "sha512-GsUDF+rXyxDZkkJxUsDxnA67FG+kc5W1dnloCFLl6fWzceevsCYzJpASBzT+BPjwUgREE6FngfJYYYMQUY5fZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.7", + "@aws-sdk/types": "^3.973.1", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz", + "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "fast-xml-parser": "5.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0" + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6.9.0" } }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", + "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", + "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", + "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", + "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/fetch-engine": "6.18.0", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", + "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", + "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0", + "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", + "@prisma/get-platform": "6.18.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", + "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.18.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.47", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", + "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.22.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.1.tgz", + "integrity": "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.9", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-stream": "^4.5.11", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", + "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", + "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", + "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", + "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", + "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", + "license": "Apache-2.0", "dependencies": { - "@jest/get-type": "30.1.0" + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.9.tgz", + "integrity": "sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/hash-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.12.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/globals": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.8.tgz", + "integrity": "sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" + "@smithy/types": "^4.12.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/reporters": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/md5-js": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.8.tgz", + "integrity": "sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/types": "^4.12.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", + "license": "Apache-2.0", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz", + "integrity": "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "@smithy/core": "^3.22.1", + "@smithy/middleware-serde": "^4.2.9", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "@smithy/url-parser": "^4.2.8", + "@smithy/util-middleware": "^4.2.8", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-retry": { + "version": "4.4.30", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz", + "integrity": "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "@smithy/node-config-provider": "^4.3.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/service-error-classification": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-retry": "^4.2.8", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-serde": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-stack": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", + "license": "Apache-2.0", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-config-provider": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", + "license": "Apache-2.0", "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" + "@smithy/property-provider": "^4.2.8", + "@smithy/shared-ini-file-loader": "^4.4.3", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-http-handler": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz", + "integrity": "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" + "@smithy/abort-controller": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/querystring-builder": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jest/transform/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/property-provider": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/protocol-http": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/querystring-builder": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@smithy/types": "^4.12.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/querystring-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/service-error-classification": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@smithy/types": "^4.12.0" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/signature-v4": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.8", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/smithy-client": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.2.tgz", + "integrity": "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@smithy/core": "^3.22.1", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/middleware-stack": "^4.2.8", + "@smithy/protocol-http": "^5.3.8", + "@smithy/types": "^4.12.0", + "@smithy/util-stream": "^4.5.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@smithy/types": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", + "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", + "license": "Apache-2.0", "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" + "node_modules/@smithy/url-parser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "license": "Apache-2.0", "dependencies": { - "@noble/hashes": "^1.1.5" + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=18.0.0" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/pkgr" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/client": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", - "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", - "hasInstallScript": true, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.29", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz", + "integrity": "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.32", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz", + "integrity": "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.6", + "@smithy/credential-provider-imds": "^4.2.8", + "@smithy/node-config-provider": "^4.3.8", + "@smithy/property-provider": "^4.2.8", + "@smithy/smithy-client": "^4.11.2", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/config": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", - "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", - "devOptional": true, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", "license": "Apache-2.0", "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" + "@smithy/node-config-provider": "^4.3.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/debug": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", - "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", - "devOptional": true, - "license": "Apache-2.0" + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@prisma/engines": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", - "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", - "devOptional": true, - "hasInstallScript": true, + "node_modules/@smithy/util-middleware": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/fetch-engine": "6.18.0", - "@prisma/get-platform": "6.18.0" + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/engines-version": { - "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", - "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", - "devOptional": true, - "license": "Apache-2.0" + "node_modules/@smithy/util-retry": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@prisma/fetch-engine": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", - "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", - "devOptional": true, + "node_modules/@smithy/util-stream": { + "version": "4.5.11", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.11.tgz", + "integrity": "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/get-platform": "6.18.0" + "@smithy/fetch-http-handler": "^5.3.9", + "@smithy/node-http-handler": "^4.4.9", + "@smithy/types": "^4.12.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@prisma/get-platform": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", - "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", - "devOptional": true, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", - "dev": true, - "license": "MIT" + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/util-waiter": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz", + "integrity": "sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/abort-controller": "^4.2.8", + "@smithy/types": "^4.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@socket.io/component-emitter": { @@ -2241,6 +3872,12 @@ "node": ">=18" } }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3454,6 +5091,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -6528,6 +8183,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/superagent": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", @@ -6802,9 +8469,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/type-detect": { "version": "4.0.8", diff --git a/package.json b/package.json index a6384c8c..e28debef 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "start": "node ./dist/main.js" }, "dependencies": { + "@aws-sdk/client-s3": "^3.985.0", "@prisma/client": "^6.18.0", "bcrypt": "^6.0.0", "cors": "^2.8.5", diff --git a/sprint9-README.md b/sprint9-README.md new file mode 100644 index 00000000..aa34f7d5 --- /dev/null +++ b/sprint9-README.md @@ -0,0 +1,21 @@ +## 미션 목표 + +- [x] Jest와 Supertest를 사용해 유닛 테스트, 통합 테스트 작성하기 + +## 기본 요구 사항 + +- [x] Jest의 테스트 커버리지 도구를 사용하도록 설정해 주세요. + +- [x] 인증이 필요하지 않은 상품 API에 대한 통합 테스트를 작성해 주세요. + +- [x] 인증이 필요하지 않은 게시글 API에 대한 통합 테스트를 작성해 주세요. + +- [x] 로그인, 회원가입 API에 대한 통합 테스트를 작성해 주세요. + +- [x] 인증이 필요한 상품 API에 대해 통합 테스트를 작성해 주세요. + +- [x] 인증이 필요한 게시글 API에 대해 통합 테스트를 작성해 주세요. + +## 심화 요구사항 + +- [x] 상품 API의 비즈니스 로직에 대해 Mock, Spy를 활용해 유닛 테스트를 작성해 주세요. diff --git a/start.sh b/start.sh new file mode 100644 index 00000000..e69de29b diff --git a/tsconfig.json b/tsconfig.json index 51c248aa..8db8faf1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,6 @@ "moduleDetection": "force", "esModuleInterop": true, "skipLibCheck": true - } + }, + "include": ["src/**/*"] } From d3511e8ff5852d496c623c27959278fb4b6503a6 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 20:42:44 +0900 Subject: [PATCH 34/53] =?UTF-8?q?dist=ED=8C=8C=EC=9D=BC=20=ED=8A=B8?= =?UTF-8?q?=EB=9E=98=ED=82=B9=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- dist/Routers/articleRouter.d.ts | 3 - dist/Routers/articleRouter.d.ts.map | 1 - dist/Routers/articleRouter.js | 21 --- dist/Routers/articleRouter.js.map | 1 - dist/Routers/commentRouter.d.ts | 3 - dist/Routers/commentRouter.d.ts.map | 1 - dist/Routers/commentRouter.js | 19 --- dist/Routers/commentRouter.js.map | 1 - dist/Routers/notificationRouter.d.ts | 3 - dist/Routers/notificationRouter.d.ts.map | 1 - dist/Routers/notificationRouter.js | 15 -- dist/Routers/notificationRouter.js.map | 1 - dist/Routers/productRouter.d.ts | 3 - dist/Routers/productRouter.d.ts.map | 1 - dist/Routers/productRouter.js | 21 --- dist/Routers/productRouter.js.map | 1 - dist/Routers/routerManager.d.ts | 2 - dist/Routers/routerManager.d.ts.map | 1 - dist/Routers/routerManager.js | 21 --- dist/Routers/routerManager.js.map | 1 - dist/Routers/uploadRouter.d.ts | 3 - dist/Routers/uploadRouter.d.ts.map | 1 - dist/Routers/uploadRouter.js | 15 -- dist/Routers/uploadRouter.js.map | 1 - dist/Routers/userRouter.d.ts | 3 - dist/Routers/userRouter.d.ts.map | 1 - dist/Routers/userRouter.js | 20 --- dist/Routers/userRouter.js.map | 1 - dist/controller/articleController.d.ts | 10 -- dist/controller/articleController.d.ts.map | 1 - dist/controller/articleController.js | 126 --------------- dist/controller/articleController.js.map | 1 - dist/controller/commentController.d.ts | 7 - dist/controller/commentController.d.ts.map | 1 - dist/controller/commentController.js | 91 ----------- dist/controller/commentController.js.map | 1 - dist/controller/notificationController.d.ts | 7 - .../notificationController.d.ts.map | 1 - dist/controller/notificationController.js | 52 ------ dist/controller/notificationController.js.map | 1 - dist/controller/productController.d.ts | 10 -- dist/controller/productController.d.ts.map | 1 - dist/controller/productController.js | 132 --------------- dist/controller/productController.js.map | 1 - dist/controller/uploadController.d.ts | 3 - dist/controller/uploadController.d.ts.map | 1 - dist/controller/uploadController.js | 12 -- dist/controller/uploadController.js.map | 1 - dist/controller/userController.d.ts | 12 -- dist/controller/userController.d.ts.map | 1 - dist/controller/userController.js | 73 --------- dist/controller/userController.js.map | 1 - dist/libs/Handler/errorHandler.d.ts | 14 -- dist/libs/Handler/errorHandler.d.ts.map | 1 - dist/libs/Handler/errorHandler.js | 91 ----------- dist/libs/Handler/errorHandler.js.map | 1 - dist/libs/catchAsync.d.ts | 4 - dist/libs/catchAsync.d.ts.map | 1 - dist/libs/catchAsync.js | 14 -- dist/libs/catchAsync.js.map | 1 - dist/libs/constants.d.ts | 11 -- dist/libs/constants.d.ts.map | 1 - dist/libs/constants.js | 48 ------ dist/libs/constants.js.map | 1 - dist/libs/corsSetUp.d.ts | 3 - dist/libs/corsSetUp.d.ts.map | 1 - dist/libs/corsSetUp.js | 15 -- dist/libs/corsSetUp.js.map | 1 - dist/libs/interfaces.d.ts | 88 ---------- dist/libs/interfaces.d.ts.map | 1 - dist/libs/interfaces.js | 3 - dist/libs/interfaces.js.map | 1 - dist/libs/removeTool.d.ts | 2 - dist/libs/removeTool.d.ts.map | 1 - dist/libs/removeTool.js | 15 -- dist/libs/removeTool.js.map | 1 - dist/main.d.ts | 3 - dist/main.d.ts.map | 1 - dist/main.js | 64 -------- dist/main.js.map | 1 - dist/middlewares/auth.d.ts | 12 -- dist/middlewares/auth.d.ts.map | 1 - dist/middlewares/auth.js | 92 ----------- dist/middlewares/auth.js.map | 1 - dist/repositories/articleLikeRepository.d.ts | 25 --- .../articleLikeRepository.d.ts.map | 1 - dist/repositories/articleLikeRepository.js | 49 ------ .../repositories/articleLikeRepository.js.map | 1 - dist/repositories/articleRepository.d.ts | 64 -------- dist/repositories/articleRepository.d.ts.map | 1 - dist/repositories/articleRepository.js | 80 ---------- dist/repositories/articleRepository.js.map | 1 - dist/repositories/commentRepository.d.ts | 67 -------- dist/repositories/commentRepository.d.ts.map | 1 - dist/repositories/commentRepository.js | 112 ------------- dist/repositories/commentRepository.js.map | 1 - dist/repositories/notificationRepository.d.ts | 30 ---- .../notificationRepository.d.ts.map | 1 - dist/repositories/notificationRepository.js | 52 ------ .../notificationRepository.js.map | 1 - dist/repositories/productLikeRepository.d.ts | 43 ----- .../productLikeRepository.d.ts.map | 1 - dist/repositories/productLikeRepository.js | 62 ------- .../repositories/productLikeRepository.js.map | 1 - dist/repositories/productRepository.d.ts | 108 ------------- dist/repositories/productRepository.d.ts.map | 1 - dist/repositories/productRepository.js | 119 -------------- dist/repositories/productRepository.js.map | 1 - dist/repositories/userRepository.d.ts | 49 ------ dist/repositories/userRepository.d.ts.map | 1 - dist/repositories/userRepository.js | 71 -------- dist/repositories/userRepository.js.map | 1 - dist/services/articleService.d.ts | 51 ------ dist/services/articleService.d.ts.map | 1 - dist/services/articleService.js | 83 ---------- dist/services/articleService.js.map | 1 - dist/services/commentService.d.ts | 57 ------- dist/services/commentService.d.ts.map | 1 - dist/services/commentService.js | 90 ----------- dist/services/commentService.js.map | 1 - dist/services/notificationService.d.ts | 22 --- dist/services/notificationService.d.ts.map | 1 - dist/services/notificationService.js | 47 ------ dist/services/notificationService.js.map | 1 - dist/services/productService.d.ts | 70 -------- dist/services/productService.d.ts.map | 1 - dist/services/productService.js | 91 ----------- dist/services/productService.js.map | 1 - dist/services/userService.d.ts | 55 ------- dist/services/userService.d.ts.map | 1 - dist/services/userService.js | 151 ------------------ dist/services/userService.js.map | 1 - dist/structs/structs.d.ts | 52 ------ dist/structs/structs.d.ts.map | 1 - dist/structs/structs.js | 65 -------- dist/structs/structs.js.map | 1 - dist/structs/userStructs.d.ts | 31 ---- dist/structs/userStructs.d.ts.map | 1 - dist/structs/userStructs.js | 59 ------- dist/structs/userStructs.js.map | 1 - 141 files changed, 3 insertions(+), 3092 deletions(-) delete mode 100644 dist/Routers/articleRouter.d.ts delete mode 100644 dist/Routers/articleRouter.d.ts.map delete mode 100644 dist/Routers/articleRouter.js delete mode 100644 dist/Routers/articleRouter.js.map delete mode 100644 dist/Routers/commentRouter.d.ts delete mode 100644 dist/Routers/commentRouter.d.ts.map delete mode 100644 dist/Routers/commentRouter.js delete mode 100644 dist/Routers/commentRouter.js.map delete mode 100644 dist/Routers/notificationRouter.d.ts delete mode 100644 dist/Routers/notificationRouter.d.ts.map delete mode 100644 dist/Routers/notificationRouter.js delete mode 100644 dist/Routers/notificationRouter.js.map delete mode 100644 dist/Routers/productRouter.d.ts delete mode 100644 dist/Routers/productRouter.d.ts.map delete mode 100644 dist/Routers/productRouter.js delete mode 100644 dist/Routers/productRouter.js.map delete mode 100644 dist/Routers/routerManager.d.ts delete mode 100644 dist/Routers/routerManager.d.ts.map delete mode 100644 dist/Routers/routerManager.js delete mode 100644 dist/Routers/routerManager.js.map delete mode 100644 dist/Routers/uploadRouter.d.ts delete mode 100644 dist/Routers/uploadRouter.d.ts.map delete mode 100644 dist/Routers/uploadRouter.js delete mode 100644 dist/Routers/uploadRouter.js.map delete mode 100644 dist/Routers/userRouter.d.ts delete mode 100644 dist/Routers/userRouter.d.ts.map delete mode 100644 dist/Routers/userRouter.js delete mode 100644 dist/Routers/userRouter.js.map delete mode 100644 dist/controller/articleController.d.ts delete mode 100644 dist/controller/articleController.d.ts.map delete mode 100644 dist/controller/articleController.js delete mode 100644 dist/controller/articleController.js.map delete mode 100644 dist/controller/commentController.d.ts delete mode 100644 dist/controller/commentController.d.ts.map delete mode 100644 dist/controller/commentController.js delete mode 100644 dist/controller/commentController.js.map delete mode 100644 dist/controller/notificationController.d.ts delete mode 100644 dist/controller/notificationController.d.ts.map delete mode 100644 dist/controller/notificationController.js delete mode 100644 dist/controller/notificationController.js.map delete mode 100644 dist/controller/productController.d.ts delete mode 100644 dist/controller/productController.d.ts.map delete mode 100644 dist/controller/productController.js delete mode 100644 dist/controller/productController.js.map delete mode 100644 dist/controller/uploadController.d.ts delete mode 100644 dist/controller/uploadController.d.ts.map delete mode 100644 dist/controller/uploadController.js delete mode 100644 dist/controller/uploadController.js.map delete mode 100644 dist/controller/userController.d.ts delete mode 100644 dist/controller/userController.d.ts.map delete mode 100644 dist/controller/userController.js delete mode 100644 dist/controller/userController.js.map delete mode 100644 dist/libs/Handler/errorHandler.d.ts delete mode 100644 dist/libs/Handler/errorHandler.d.ts.map delete mode 100644 dist/libs/Handler/errorHandler.js delete mode 100644 dist/libs/Handler/errorHandler.js.map delete mode 100644 dist/libs/catchAsync.d.ts delete mode 100644 dist/libs/catchAsync.d.ts.map delete mode 100644 dist/libs/catchAsync.js delete mode 100644 dist/libs/catchAsync.js.map delete mode 100644 dist/libs/constants.d.ts delete mode 100644 dist/libs/constants.d.ts.map delete mode 100644 dist/libs/constants.js delete mode 100644 dist/libs/constants.js.map delete mode 100644 dist/libs/corsSetUp.d.ts delete mode 100644 dist/libs/corsSetUp.d.ts.map delete mode 100644 dist/libs/corsSetUp.js delete mode 100644 dist/libs/corsSetUp.js.map delete mode 100644 dist/libs/interfaces.d.ts delete mode 100644 dist/libs/interfaces.d.ts.map delete mode 100644 dist/libs/interfaces.js delete mode 100644 dist/libs/interfaces.js.map delete mode 100644 dist/libs/removeTool.d.ts delete mode 100644 dist/libs/removeTool.d.ts.map delete mode 100644 dist/libs/removeTool.js delete mode 100644 dist/libs/removeTool.js.map delete mode 100644 dist/main.d.ts delete mode 100644 dist/main.d.ts.map delete mode 100644 dist/main.js delete mode 100644 dist/main.js.map delete mode 100644 dist/middlewares/auth.d.ts delete mode 100644 dist/middlewares/auth.d.ts.map delete mode 100644 dist/middlewares/auth.js delete mode 100644 dist/middlewares/auth.js.map delete mode 100644 dist/repositories/articleLikeRepository.d.ts delete mode 100644 dist/repositories/articleLikeRepository.d.ts.map delete mode 100644 dist/repositories/articleLikeRepository.js delete mode 100644 dist/repositories/articleLikeRepository.js.map delete mode 100644 dist/repositories/articleRepository.d.ts delete mode 100644 dist/repositories/articleRepository.d.ts.map delete mode 100644 dist/repositories/articleRepository.js delete mode 100644 dist/repositories/articleRepository.js.map delete mode 100644 dist/repositories/commentRepository.d.ts delete mode 100644 dist/repositories/commentRepository.d.ts.map delete mode 100644 dist/repositories/commentRepository.js delete mode 100644 dist/repositories/commentRepository.js.map delete mode 100644 dist/repositories/notificationRepository.d.ts delete mode 100644 dist/repositories/notificationRepository.d.ts.map delete mode 100644 dist/repositories/notificationRepository.js delete mode 100644 dist/repositories/notificationRepository.js.map delete mode 100644 dist/repositories/productLikeRepository.d.ts delete mode 100644 dist/repositories/productLikeRepository.d.ts.map delete mode 100644 dist/repositories/productLikeRepository.js delete mode 100644 dist/repositories/productLikeRepository.js.map delete mode 100644 dist/repositories/productRepository.d.ts delete mode 100644 dist/repositories/productRepository.d.ts.map delete mode 100644 dist/repositories/productRepository.js delete mode 100644 dist/repositories/productRepository.js.map delete mode 100644 dist/repositories/userRepository.d.ts delete mode 100644 dist/repositories/userRepository.d.ts.map delete mode 100644 dist/repositories/userRepository.js delete mode 100644 dist/repositories/userRepository.js.map delete mode 100644 dist/services/articleService.d.ts delete mode 100644 dist/services/articleService.d.ts.map delete mode 100644 dist/services/articleService.js delete mode 100644 dist/services/articleService.js.map delete mode 100644 dist/services/commentService.d.ts delete mode 100644 dist/services/commentService.d.ts.map delete mode 100644 dist/services/commentService.js delete mode 100644 dist/services/commentService.js.map delete mode 100644 dist/services/notificationService.d.ts delete mode 100644 dist/services/notificationService.d.ts.map delete mode 100644 dist/services/notificationService.js delete mode 100644 dist/services/notificationService.js.map delete mode 100644 dist/services/productService.d.ts delete mode 100644 dist/services/productService.d.ts.map delete mode 100644 dist/services/productService.js delete mode 100644 dist/services/productService.js.map delete mode 100644 dist/services/userService.d.ts delete mode 100644 dist/services/userService.d.ts.map delete mode 100644 dist/services/userService.js delete mode 100644 dist/services/userService.js.map delete mode 100644 dist/structs/structs.d.ts delete mode 100644 dist/structs/structs.d.ts.map delete mode 100644 dist/structs/structs.js delete mode 100644 dist/structs/structs.js.map delete mode 100644 dist/structs/userStructs.d.ts delete mode 100644 dist/structs/userStructs.d.ts.map delete mode 100644 dist/structs/userStructs.js delete mode 100644 dist/structs/userStructs.js.map diff --git a/.gitignore b/.gitignore index ee9ab89c..04a280c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules/ prisma/ .env -__http__/ \ No newline at end of file +__http__/ +.env.production +/dist diff --git a/dist/Routers/articleRouter.d.ts b/dist/Routers/articleRouter.d.ts deleted file mode 100644 index 8878a904..00000000 --- a/dist/Routers/articleRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const articleRouter: import("express-serve-static-core").Router; -export default articleRouter; -//# sourceMappingURL=articleRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.d.ts.map b/dist/Routers/articleRouter.d.ts.map deleted file mode 100644 index 8986ff63..00000000 --- a/dist/Routers/articleRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/articleRouter.js b/dist/Routers/articleRouter.js deleted file mode 100644 index ca3ed39c..00000000 --- a/dist/Routers/articleRouter.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -const catchAsync_1 = require("../libs/catchAsync"); -const auth_1 = __importDefault(require("../middlewares/auth")); -const articleController_1 = __importDefault(require("../controller/articleController")); -const articleRouter = constants_1.EXPRESS.Router(); -const articleController = new articleController_1.default(); -articleRouter.route('/') - .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.getArticles)) - .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.postArticle)); -articleRouter.route('/:id') - .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.getArticleById)) - .patch(auth_1.default.verifyAccessToken, auth_1.default.verifyArticleAuth, (0, catchAsync_1.catchAsync)(articleController.patchArticleById)) - .delete(auth_1.default.verifyAccessToken, auth_1.default.verifyArticleAuth, (0, catchAsync_1.catchAsync)(articleController.deleteArticleById)); -articleRouter.post('/:id/like', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(articleController.likeArticle)); -exports.default = articleRouter; -//# sourceMappingURL=articleRouter.js.map \ No newline at end of file diff --git a/dist/Routers/articleRouter.js.map b/dist/Routers/articleRouter.js.map deleted file mode 100644 index ab48ba28..00000000 --- a/dist/Routers/articleRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRouter.js","sourceRoot":"","sources":["../../src/Routers/articleRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,+DAAuC;AACvC,wFAAgE;AAEhE,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,2BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;KAC1E,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KACrG,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAE7G,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts b/dist/Routers/commentRouter.d.ts deleted file mode 100644 index 06b1655f..00000000 --- a/dist/Routers/commentRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const commentRouter: import("express-serve-static-core").Router; -export default commentRouter; -//# sourceMappingURL=commentRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.d.ts.map b/dist/Routers/commentRouter.d.ts.map deleted file mode 100644 index 415ffed9..00000000 --- a/dist/Routers/commentRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAYvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/commentRouter.js b/dist/Routers/commentRouter.js deleted file mode 100644 index bd24cc3b..00000000 --- a/dist/Routers/commentRouter.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -const catchAsync_1 = require("../libs/catchAsync"); -const auth_1 = __importDefault(require("../middlewares/auth")); -const commentController_1 = require("../controller/commentController"); -const commentRouter = constants_1.EXPRESS.Router(); -commentRouter.route('/') - .get((0, catchAsync_1.catchAsync)(commentController_1.GetComment)) - .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(commentController_1.PostComment)); -commentRouter.route('/:id') - .get((0, catchAsync_1.catchAsync)(commentController_1.GetCommentById)) - .patch(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsyncAll)(auth_1.default.verifyProduectAuth, commentController_1.PatchCommentById)) - .delete(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsyncAll)(auth_1.default.verifyProduectAuth, commentController_1.DeleteCommentById)); -exports.default = commentRouter; -//# sourceMappingURL=commentRouter.js.map \ No newline at end of file diff --git a/dist/Routers/commentRouter.js.map b/dist/Routers/commentRouter.js.map deleted file mode 100644 index 4d0d08f3..00000000 --- a/dist/Routers/commentRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRouter.js","sourceRoot":"","sources":["../../src/Routers/commentRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAA+D;AAC/D,+DAAuC;AACvC,uEAMyC;AAEzC,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAEvC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,IAAA,uBAAU,EAAC,8BAAU,CAAC,CAAC;KAC3B,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,+BAAW,CAAC,CAAC,CAAC;AAE3D,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,IAAA,uBAAU,EAAC,kCAAc,CAAC,CAAC;KAC/B,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAa,EAAC,cAAI,CAAC,kBAAkB,EAAE,oCAAgB,CAAC,CAAC;KACvF,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,0BAAa,EAAC,cAAI,CAAC,kBAAkB,EAAE,qCAAiB,CAAC,CAAC,CAAC;AAG/F,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/notificationRouter.d.ts b/dist/Routers/notificationRouter.d.ts deleted file mode 100644 index ec471d7f..00000000 --- a/dist/Routers/notificationRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const router: import("express-serve-static-core").Router; -export default router; -//# sourceMappingURL=notificationRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/notificationRouter.d.ts.map b/dist/Routers/notificationRouter.d.ts.map deleted file mode 100644 index 44cab9e1..00000000 --- a/dist/Routers/notificationRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/notificationRouter.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAQxB,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/dist/Routers/notificationRouter.js b/dist/Routers/notificationRouter.js deleted file mode 100644 index a765773b..00000000 --- a/dist/Routers/notificationRouter.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = require("express"); -const notificationController_1 = __importDefault(require("../controller/notificationController")); -const auth_1 = __importDefault(require("../middlewares/auth")); -const router = (0, express_1.Router)(); -const controller = new notificationController_1.default(); -router.get('/', auth_1.default.softVerifyAccessToken, controller.GetNotifications); -router.get('/count', auth_1.default.softVerifyAccessToken, controller.GetUnreadCount); -router.patch('/:id/read', auth_1.default.verifyAccessToken, controller.ReadNotification); -exports.default = router; -//# sourceMappingURL=notificationRouter.js.map \ No newline at end of file diff --git a/dist/Routers/notificationRouter.js.map b/dist/Routers/notificationRouter.js.map deleted file mode 100644 index 1240def1..00000000 --- a/dist/Routers/notificationRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationRouter.js","sourceRoot":"","sources":["../../src/Routers/notificationRouter.ts"],"names":[],"mappings":";;;;;AAAA,qCAAiC;AACjC,kGAA0E;AAC1E,+DAAuC;AAEvC,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AACxB,MAAM,UAAU,GAAG,IAAI,gCAAsB,EAAE,CAAC;AAGhD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,cAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACzE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;AAC5E,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAE/E,kBAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts b/dist/Routers/productRouter.d.ts deleted file mode 100644 index c3eadb5d..00000000 --- a/dist/Routers/productRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const productRouter: import("express-serve-static-core").Router; -export default productRouter; -//# sourceMappingURL=productRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/productRouter.d.ts.map b/dist/Routers/productRouter.d.ts.map deleted file mode 100644 index 0c9ad952..00000000 --- a/dist/Routers/productRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,aAAa,4CAAmB,CAAC;AAcvC,eAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/productRouter.js b/dist/Routers/productRouter.js deleted file mode 100644 index 3d5681ad..00000000 --- a/dist/Routers/productRouter.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -const catchAsync_1 = require("../libs/catchAsync"); -const auth_1 = __importDefault(require("../middlewares/auth")); -const productController_1 = __importDefault(require("../controller/productController")); -const productRouter = constants_1.EXPRESS.Router(); -const productController = new productController_1.default(); -productRouter.route('/') - .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(productController.GetProduct)) - .post(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.PostProduct)); -productRouter.route('/:id') - .get(auth_1.default.softVerifyAccessToken, (0, catchAsync_1.catchAsync)(productController.GetProductById)) - .patch(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.PatchProductById)) - .delete(auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.DeleteProductById)); -productRouter.post('/:id/like', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(productController.likeProduct)); -exports.default = productRouter; -//# sourceMappingURL=productRouter.js.map \ No newline at end of file diff --git a/dist/Routers/productRouter.js.map b/dist/Routers/productRouter.js.map deleted file mode 100644 index 645f04d1..00000000 --- a/dist/Routers/productRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRouter.js","sourceRoot":"","sources":["../../src/Routers/productRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,+DAAuC;AACvC,wFAAgE;AAEhE,MAAM,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,2BAAiB,EAAE,CAAC;AAElD,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;KACnB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;KACzE,IAAI,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAE7E,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;KACtB,GAAG,CAAC,cAAI,CAAC,qBAAqB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;KAC7E,KAAK,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;KAC7E,MAAM,CAAC,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErF,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnG,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts b/dist/Routers/routerManager.d.ts deleted file mode 100644 index c5d39a67..00000000 --- a/dist/Routers/routerManager.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare const RouterManager: import("express-serve-static-core").Router; -//# sourceMappingURL=routerManager.d.ts.map \ No newline at end of file diff --git a/dist/Routers/routerManager.d.ts.map b/dist/Routers/routerManager.d.ts.map deleted file mode 100644 index 92a9a935..00000000 --- a/dist/Routers/routerManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routerManager.d.ts","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,4CAAmB,CAAC"} \ No newline at end of file diff --git a/dist/Routers/routerManager.js b/dist/Routers/routerManager.js deleted file mode 100644 index 09c1df5d..00000000 --- a/dist/Routers/routerManager.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RouterManager = void 0; -const constants_1 = require("../libs/constants"); -const productRouter_1 = __importDefault(require("./productRouter")); -const articleRouter_1 = __importDefault(require("./articleRouter")); -const commentRouter_1 = __importDefault(require("./commentRouter")); -const uploadRouter_1 = __importDefault(require("./uploadRouter")); -const userRouter_1 = __importDefault(require("./userRouter")); -const notificationRouter_1 = __importDefault(require("./notificationRouter")); -exports.RouterManager = constants_1.EXPRESS.Router(); -exports.RouterManager.use('/products', productRouter_1.default); -exports.RouterManager.use('/articles', articleRouter_1.default); -exports.RouterManager.use('/comments', commentRouter_1.default); -exports.RouterManager.use('/files', uploadRouter_1.default); -exports.RouterManager.use('/auth', userRouter_1.default); -exports.RouterManager.use('/notifications', notificationRouter_1.default); -//# sourceMappingURL=routerManager.js.map \ No newline at end of file diff --git a/dist/Routers/routerManager.js.map b/dist/Routers/routerManager.js.map deleted file mode 100644 index 2ef0f797..00000000 --- a/dist/Routers/routerManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routerManager.js","sourceRoot":"","sources":["../../src/Routers/routerManager.ts"],"names":[],"mappings":";;;;;;AAAA,iDAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,oEAA4C;AAC5C,kEAA0C;AAC1C,8DAAsC;AACtC,8EAAsD;AAEzC,QAAA,aAAa,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAI9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,WAAW,EAAE,uBAAa,CAAC,CAAC;AAC9C,qBAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAY,CAAC,CAAC;AAC1C,qBAAa,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAU,CAAC,CAAC;AACvC,qBAAa,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAkB,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts b/dist/Routers/uploadRouter.d.ts deleted file mode 100644 index a036c514..00000000 --- a/dist/Routers/uploadRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const uploadRouter: import("express-serve-static-core").Router; -export default uploadRouter; -//# sourceMappingURL=uploadRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.d.ts.map b/dist/Routers/uploadRouter.d.ts.map deleted file mode 100644 index 732e2bf6..00000000 --- a/dist/Routers/uploadRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,YAAY,4CAAmB,CAAC;AAStC,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js b/dist/Routers/uploadRouter.js deleted file mode 100644 index 1788fffb..00000000 --- a/dist/Routers/uploadRouter.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -const catchAsync_1 = require("../libs/catchAsync"); -const multer_1 = __importDefault(require("multer")); -const uploadController_1 = require("../controller/uploadController"); -const uploadRouter = constants_1.EXPRESS.Router(); -const upload = (0, multer_1.default)({ dest: 'upload/' }); -uploadRouter.post('/', upload.single('attachment'), (0, catchAsync_1.catchAsync)(uploadController_1.UploadSingleImage)); -uploadRouter.use('/', constants_1.EXPRESS.static('upload')); -exports.default = uploadRouter; -//# sourceMappingURL=uploadRouter.js.map \ No newline at end of file diff --git a/dist/Routers/uploadRouter.js.map b/dist/Routers/uploadRouter.js.map deleted file mode 100644 index d94fc42d..00000000 --- a/dist/Routers/uploadRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadRouter.js","sourceRoot":"","sources":["../../src/Routers/uploadRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,oDAA4B;AAC5B,qEAEwC;AAExC,MAAM,YAAY,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AAEtC,MAAM,MAAM,GAAG,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;AAE3C,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAC9C,IAAA,uBAAU,EAAC,oCAAiB,CAAC,CAAC,CAAC;AAEnC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,mBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEhD,kBAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts b/dist/Routers/userRouter.d.ts deleted file mode 100644 index 23df20cc..00000000 --- a/dist/Routers/userRouter.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const userRouter: import("express-serve-static-core").Router; -export default userRouter; -//# sourceMappingURL=userRouter.d.ts.map \ No newline at end of file diff --git a/dist/Routers/userRouter.d.ts.map b/dist/Routers/userRouter.d.ts.map deleted file mode 100644 index 6564ec89..00000000 --- a/dist/Routers/userRouter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRouter.d.ts","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,UAAU,4CAAmB,CAAC;AAgBpC,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/Routers/userRouter.js b/dist/Routers/userRouter.js deleted file mode 100644 index 7224940d..00000000 --- a/dist/Routers/userRouter.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -const catchAsync_1 = require("../libs/catchAsync"); -const userController_1 = __importDefault(require("../controller/userController")); -const auth_1 = __importDefault(require("../middlewares/auth")); -const userRouter = constants_1.EXPRESS.Router(); -const userController = new userController_1.default(); -userRouter.post('/signup', (0, catchAsync_1.catchAsync)(userController.register)); -userRouter.post('/login', (0, catchAsync_1.catchAsync)(userController.login)); -userRouter.post('/token/refresh', auth_1.default.verifyRefreshToken, (0, catchAsync_1.catchAsync)(userController.refresh)); -userRouter.get('/me', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.GetMe)); -userRouter.patch('/me', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.updateMe)); -userRouter.patch('/me/password', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.updateMyPassword)); -userRouter.get('/me/products', auth_1.default.verifyAccessToken, (0, catchAsync_1.catchAsync)(userController.getMyProducts)); -exports.default = userRouter; -//# sourceMappingURL=userRouter.js.map \ No newline at end of file diff --git a/dist/Routers/userRouter.js.map b/dist/Routers/userRouter.js.map deleted file mode 100644 index 8ee3abe1..00000000 --- a/dist/Routers/userRouter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRouter.js","sourceRoot":"","sources":["../../src/Routers/userRouter.ts"],"names":[],"mappings":";;;;;AAAA,iDAA4C;AAC5C,mDAAgD;AAChD,kFAAiE;AACjE,+DAAuC;AAEvC,MAAM,UAAU,GAAG,mBAAO,CAAC,MAAM,EAAE,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,wBAAqB,EAAE,CAAC;AAGnD,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChE,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAC5B,cAAI,CAAC,kBAAkB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrF,UAAU,CAAC,KAAK,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACtG,UAAU,CAAC,GAAG,CAAC,cAAc,EAAE,cAAI,CAAC,iBAAiB,EAAE,IAAA,uBAAU,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAIjG,kBAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/controller/articleController.d.ts b/dist/controller/articleController.d.ts deleted file mode 100644 index dbe15c54..00000000 --- a/dist/controller/articleController.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ExpressHandler } from '../libs/constants'; -export default class ArticleController { - getArticles: ExpressHandler; - getArticleById: ExpressHandler; - postArticle: ExpressHandler; - patchArticleById: ExpressHandler; - deleteArticleById: ExpressHandler; - likeArticle: ExpressHandler; -} -//# sourceMappingURL=articleController.d.ts.map \ No newline at end of file diff --git a/dist/controller/articleController.d.ts.map b/dist/controller/articleController.d.ts.map deleted file mode 100644 index 37904518..00000000 --- a/dist/controller/articleController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleController.d.ts","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,WAAW,EAAE,cAAc,CAqCzB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAOzB;IAEF,gBAAgB,EAAE,cAAc,CAU9B;IAEF,iBAAiB,EAAE,cAAc,CAQ/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/articleController.js b/dist/controller/articleController.js deleted file mode 100644 index 4f41551f..00000000 --- a/dist/controller/articleController.js +++ /dev/null @@ -1,126 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const articleService_1 = require("../services/articleService"); -const superstruct_1 = require("superstruct"); -const structs_1 = require("../structs/structs"); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -class ArticleController { - constructor() { - this.getArticles = (req, res) => __awaiter(this, void 0, void 0, function* () { - const { offset = 0, limit = 0, order = 'newset', title = "", content = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - const parsedOffset = parseInt(offset, 10) || 0; - const parsedLimit = parseInt(limit, 10) || 0; - const findOptions = { - where: { - title: { - contains: title, - }, - content: { - contains: content, - }, - }, - orderBy, - skip: parsedOffset, - }; - if (!Number.isNaN(parsedLimit) && parsedLimit > 0) { - findOptions.take = parsedLimit; - } - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "UserId Not Found"); - const articles = yield articleService_1.articleService.getArticles(findOptions, userId); - res.send(articles); - }); - this.getArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - if (!id) - throw new errorHandler_1.CustomError(404, "id Not Found"); - const _id = parseInt(id); - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "userId Not Found"); - const article = yield articleService_1.articleService.getArticleById(_id, userId); - res.send(article); - }); - this.postArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - (0, superstruct_1.assert)(req.body, structs_1.CreateArticle); - const userFields = __rest(req.body, []); - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - const article = yield articleService_1.articleService.postArticle(Object.assign(Object.assign({}, userFields), { userId })); - res.status(201).send(article); - }); - this.patchArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - const { id } = req.params; - if (!id) - throw new errorHandler_1.CustomError(404, "id Not Found"); - const _id = parseInt(id); - (0, superstruct_1.assert)(req.body, structs_1.PatchArticle); - const userFields = __rest(req.body, []); - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - const article = yield articleService_1.articleService.patchArticleById(_id, Object.assign(Object.assign({}, userFields), { userId })); - res.send(article); - }); - this.deleteArticleById = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - const { id } = req.params; - if (!id) - throw new errorHandler_1.CustomError(404, "id Not Found"); - const _id = parseInt(id); - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - const article = yield articleService_1.articleService.deleteArticleById(_id, userId); - res.send(article); - }); - this.likeArticle = (req, res) => __awaiter(this, void 0, void 0, function* () { - if (!req.user) - throw new errorHandler_1.CustomError(404, "user Not Found"); - const userId = req.user.userId; - const articleId = req.params.id; - if (!articleId) - throw new errorHandler_1.CustomError(404, "articleId Not Found"); - const _articleId = parseInt(articleId); - const result = yield articleService_1.articleService.likeArticle(userId, _articleId); - return res.status(200).json(result); - }); - } -} -exports.default = ArticleController; -//# sourceMappingURL=articleController.js.map \ No newline at end of file diff --git a/dist/controller/articleController.js.map b/dist/controller/articleController.js.map deleted file mode 100644 index 9095f7df..00000000 --- a/dist/controller/articleController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleController.js","sourceRoot":"","sources":["../../src/controller/articleController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/commentController.d.ts b/dist/controller/commentController.d.ts deleted file mode 100644 index f5c03c08..00000000 --- a/dist/controller/commentController.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ExpressRequest, ExpressResponse } from '../libs/constants'; -export declare function GetComment(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function GetCommentById(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function PostComment(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function PatchCommentById(req: ExpressRequest, res: ExpressResponse): Promise; -export declare function DeleteCommentById(req: ExpressRequest, res: ExpressResponse): Promise; -//# sourceMappingURL=commentController.d.ts.map \ No newline at end of file diff --git a/dist/controller/commentController.d.ts.map b/dist/controller/commentController.d.ts.map deleted file mode 100644 index 188ce74f..00000000 --- a/dist/controller/commentController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentController.d.ts","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpE,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAczE;AAGD,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAM7E;AAGD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAmB1E;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAU/E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,iBAMhF"} \ No newline at end of file diff --git a/dist/controller/commentController.js b/dist/controller/commentController.js deleted file mode 100644 index e3b2854d..00000000 --- a/dist/controller/commentController.js +++ /dev/null @@ -1,91 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetComment = GetComment; -exports.GetCommentById = GetCommentById; -exports.PostComment = PostComment; -exports.PatchCommentById = PatchCommentById; -exports.DeleteCommentById = DeleteCommentById; -const superstruct_1 = require("superstruct"); -const structs_1 = require("../structs/structs"); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -const commentService_1 = require("../services/commentService"); -function GetComment(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { take = '10', cursor, productId, articleId } = req.query; - const parsedTake = parseInt(take, 10) || 0; - if (isNaN(parsedTake) || parsedTake <= 0) { - throw new errorHandler_1.CustomError(400, 'Invalid "take" parameter.'); - } - const parsedCursor = cursor ? parseInt(cursor, 10) : undefined; - const parsedProductId = productId ? Number(productId) : undefined; - const parsedArticleId = articleId ? Number(articleId) : undefined; - const result = yield commentService_1.commentService.getComments(parsedTake, parsedCursor, parsedProductId, parsedArticleId); - res.status(200).json(result); - }); -} -function GetCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const parsedId = parseInt(id, 10) || null; - if (!parsedId) - throw new errorHandler_1.CustomError(404, "id Not Found"); - const comment = yield commentService_1.commentService.getCommentById(parsedId); - res.send(comment); - }); -} -function PostComment(req, res) { - return __awaiter(this, void 0, void 0, function* () { - var _a; - (0, superstruct_1.assert)(req.body, structs_1.CreateComment); - const { content, productId, articleId } = req.body; - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - // 서비스 호출 (userId 추가됨) - const { comment, notification } = yield commentService_1.commentService.createComment(userId, content, productId, articleId); - // [알림 발송] 알림이 생성되었다면 소켓으로 전송 - if (notification) { - const io = req.app.get('io'); // app.js에서 설정한 io 객체 가져오기 - if (io) { - // 알림 받을 사람(notification.userId)에게만 전송 - io.to(notification.userId).emit('notification', notification); - } - } - // 클라이언트에는 댓글 정보만 주거나, 필요하면 알림 생성 여부도 줄 수 있음 - res.status(201).send(comment); - }); -} -function PatchCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(req.body, structs_1.PatchComment); - const { content } = req.body; - const { id } = req.params; - const parsedId = parseInt(id, 10) || null; - if (!parsedId) - throw new errorHandler_1.CustomError(404, "id Not Found"); - if (!content) - throw new errorHandler_1.CustomError(404, "Request body is empty"); - const comment = yield commentService_1.commentService.updateComment(parsedId, content); - res.send(comment); - }); -} -function DeleteCommentById(req, res) { - return __awaiter(this, void 0, void 0, function* () { - const { id } = req.params; - const parsedId = parseInt(id, 10) || null; - if (!parsedId) - throw new errorHandler_1.CustomError(404, "id Not Found"); - const comment = yield commentService_1.commentService.deleteComment(parsedId); - res.send(comment); - }); -} -//# sourceMappingURL=commentController.js.map \ No newline at end of file diff --git a/dist/controller/commentController.js.map b/dist/controller/commentController.js.map deleted file mode 100644 index e15408cd..00000000 --- a/dist/controller/commentController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentController.js","sourceRoot":"","sources":["../../src/controller/commentController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/notificationController.d.ts b/dist/controller/notificationController.d.ts deleted file mode 100644 index 06fd096d..00000000 --- a/dist/controller/notificationController.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ExpressRequest, ExpressResponse } from '../libs/constants'; -export default class NotificationController { - GetNotifications: (req: ExpressRequest, res: ExpressResponse) => Promise; - GetUnreadCount: (req: ExpressRequest, res: ExpressResponse) => Promise; - ReadNotification: (req: ExpressRequest, res: ExpressResponse) => Promise; -} -//# sourceMappingURL=notificationController.d.ts.map \ No newline at end of file diff --git a/dist/controller/notificationController.d.ts.map b/dist/controller/notificationController.d.ts.map deleted file mode 100644 index 7612e358..00000000 --- a/dist/controller/notificationController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationController.d.ts","sourceRoot":"","sources":["../../src/controller/notificationController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpE,MAAM,CAAC,OAAO,OAAO,sBAAsB;IAGvC,gBAAgB,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAMjE;IAGF,cAAc,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAM/D;IAGF,gBAAgB,GAAU,KAAK,cAAc,EAAE,KAAK,eAAe,mBAWjE;CACL"} \ No newline at end of file diff --git a/dist/controller/notificationController.js b/dist/controller/notificationController.js deleted file mode 100644 index f7290317..00000000 --- a/dist/controller/notificationController.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -const notificationService_1 = require("../services/notificationService"); -class NotificationController { - constructor() { - // GET /notifications - this.GetNotifications = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; // 미들웨어에서 userId 가져오기 - if (!userId) - throw new errorHandler_1.CustomError(401, "Unauthorized"); - const notifications = yield notificationService_1.notificationService.getNotifications(userId); - res.status(200).send(notifications); - }); - // GET /notifications/count - this.GetUnreadCount = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, "Unauthorized"); - const result = yield notificationService_1.notificationService.getUnreadCount(userId); - res.status(200).send(result); - }); - // PATCH /notifications/:id/read - this.ReadNotification = (req, res) => __awaiter(this, void 0, void 0, function* () { - var _a; - const { id } = req.params; - if (!id) - throw new errorHandler_1.CustomError(400, "Missing notification ID"); - const notificationId = parseInt(id, 10); - if (!notificationId) - throw new errorHandler_1.CustomError(400, "Invalid notification ID"); - const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.userId; - if (!userId) - throw new errorHandler_1.CustomError(401, "Unauthorized"); - const result = yield notificationService_1.notificationService.readNotification(userId, notificationId); - res.status(200).send(result); - }); - } -} -exports.default = NotificationController; -//# sourceMappingURL=notificationController.js.map \ No newline at end of file diff --git a/dist/controller/notificationController.js.map b/dist/controller/notificationController.js.map deleted file mode 100644 index 6485f199..00000000 --- a/dist/controller/notificationController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationController.js","sourceRoot":"","sources":["../../src/controller/notificationController.ts"],"names":[],"mappings":";;;;;;;;;;;AACA,+DAA2D;AAC3D,yEAAsE;AAEtE,MAAqB,sBAAsB;IAA3C;QAEI,qBAAqB;QACrB,qBAAgB,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACnE,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC,CAAC,qBAAqB;YACtD,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,aAAa,GAAG,MAAM,yCAAmB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACzE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxC,CAAC,CAAA,CAAC;QAEF,2BAA2B;QAC3B,mBAAc,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACjE,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC;YAChC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,yCAAmB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAChE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAA,CAAC;QAEF,gCAAgC;QAChC,qBAAgB,GAAG,CAAO,GAAmB,EAAE,GAAoB,EAAE,EAAE;;YACnE,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YAC/D,MAAM,cAAc,GAAG,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,cAAc;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YAE3E,MAAM,MAAM,GAAG,MAAA,GAAG,CAAC,IAAI,0CAAE,MAAM,CAAC;YAChC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,yCAAmB,CAAC,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;YAClF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAA,CAAC;IACN,CAAC;CAAA;AAjCD,yCAiCC"} \ No newline at end of file diff --git a/dist/controller/productController.d.ts b/dist/controller/productController.d.ts deleted file mode 100644 index e907b86d..00000000 --- a/dist/controller/productController.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ExpressHandler } from '../libs/constants'; -export default class ProductController { - GetProduct: ExpressHandler; - GetProductById: ExpressHandler; - PostProduct: ExpressHandler; - PatchProductById: ExpressHandler; - DeleteProductById: ExpressHandler; - likeProduct: ExpressHandler; -} -//# sourceMappingURL=productController.d.ts.map \ No newline at end of file diff --git a/dist/controller/productController.d.ts.map b/dist/controller/productController.d.ts.map deleted file mode 100644 index 6bc5f629..00000000 --- a/dist/controller/productController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productController.d.ts","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,UAAU,EAAE,cAAc,CAqCxB;IAEF,cAAc,EAAE,cAAc,CAQ5B;IAEF,WAAW,EAAE,cAAc,CAOzB;IAEF,gBAAgB,EAAE,cAAc,CAqB9B;IAEF,iBAAiB,EAAE,cAAc,CAQ/B;IAEF,WAAW,EAAE,cAAc,CAQzB;CACL"} \ No newline at end of file diff --git a/dist/controller/productController.js b/dist/controller/productController.js deleted file mode 100644 index 38201b5e..00000000 --- a/dist/controller/productController.js +++ /dev/null @@ -1,132 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const productService_1 = require("../services/productService"); -const superstruct_1 = require("superstruct"); -const structs_1 = require("../structs/structs"); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -const removeTool_1 = require("./../libs/removeTool"); -class ProductController { - constructor() { - this.GetProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { - const { offset = '0', limit = '0', order = 'newest', name = "", description = "" } = req.query; - let orderBy; - switch (order) { - case 'oldest': - orderBy = { createdAt: 'asc' }; - break; - case 'newest': - orderBy = { createdAt: 'desc' }; - break; - default: - orderBy = { createdAt: 'desc' }; - } - const parsedOffset = parseInt(offset, 10) || 0; - const parsedLimit = parseInt(limit, 10) || 0; - const findOptions = { - where: { - name: { - contains: name, - }, - description: { - contains: description, - }, - }, - orderBy, - skip: parsedOffset, - }; - if (parsedLimit > 0) { - findOptions.take = parsedLimit; - } - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "UserId Not Found"); - const products = yield productService_1.productService.getProducts(findOptions, userId); - res.send(products); - }); - this.GetProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - if (!id) - throw new errorHandler_1.CustomError(400, "id is required"); - const _id = parseInt(id); - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "userId Not Found"); - const product = yield productService_1.productService.getProductById(_id, userId); - res.send(product); - }); - this.PostProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(req.body, structs_1.CreateProduct); - const userFields = __rest(req.body, []); - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "userId Not Found"); - const product = yield productService_1.productService.createProducts(Object.assign(Object.assign({}, userFields), { userId })); - res.status(201).send(product); - }); - this.PatchProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - if (!id) - throw new errorHandler_1.CustomError(400, "id is required"); - const _id = parseInt(id); - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(401, "Unauthorized"); - (0, superstruct_1.assert)(req.body, structs_1.PatchProduct); - const userFields = (0, removeTool_1.removeUndefined)(req.body); - const { product, notifications } = yield productService_1.productService.updateProduct(_id, userFields); - if (notifications && notifications.length > 0) { - const io = req.app.get('io'); - if (io) { - notifications.forEach((noti) => { - io.to(noti.userId).emit('notification', noti); - }); - } - } - res.send(product); - }); - this.DeleteProductById = (req, res) => __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; - if (!id) - throw new errorHandler_1.CustomError(400, "id is required"); - const _id = parseInt(id); - const userId = req.user ? req.user.userId : null; - if (!userId) - throw new errorHandler_1.CustomError(404, "userId Not Found"); - const Product = yield productService_1.productService.deleteProduct(_id); - res.send(Product); - }); - this.likeProduct = (req, res) => __awaiter(this, void 0, void 0, function* () { - if (!req.user) - throw new errorHandler_1.CustomError(404, "user Not Found"); - const userId = req.user.userId; - const productId = req.params.id; - if (!productId) - throw new errorHandler_1.CustomError(400, "productId is required"); - const _productId = parseInt(productId); - const result = yield productService_1.productService.likeProduct(userId, _productId); - return res.status(200).json(result); - }); - } -} -exports.default = ProductController; -//# sourceMappingURL=productController.js.map \ No newline at end of file diff --git a/dist/controller/productController.js.map b/dist/controller/productController.js.map deleted file mode 100644 index e4dba20a..00000000 --- a/dist/controller/productController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productController.js","sourceRoot":"","sources":["../../src/controller/productController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts b/dist/controller/uploadController.d.ts deleted file mode 100644 index 03fa9134..00000000 --- a/dist/controller/uploadController.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ExpressRequest, ExpressResponse } from '../libs/constants'; -export declare function UploadSingleImage(req: ExpressRequest, res: ExpressResponse): void; -//# sourceMappingURL=uploadController.d.ts.map \ No newline at end of file diff --git a/dist/controller/uploadController.d.ts.map b/dist/controller/uploadController.d.ts.map deleted file mode 100644 index dc048139..00000000 --- a/dist/controller/uploadController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadController.d.ts","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,QAM1E"} \ No newline at end of file diff --git a/dist/controller/uploadController.js b/dist/controller/uploadController.js deleted file mode 100644 index b99c5ae1..00000000 --- a/dist/controller/uploadController.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UploadSingleImage = UploadSingleImage; -const errorHandler_1 = require("../libs/Handler/errorHandler"); -function UploadSingleImage(req, res) { - if (!req.file) - throw new errorHandler_1.CustomError(404, "file not found"); - const { filename } = req.file; - const path = `files/${filename}`; - res.json({ path }); -} -//# sourceMappingURL=uploadController.js.map \ No newline at end of file diff --git a/dist/controller/uploadController.js.map b/dist/controller/uploadController.js.map deleted file mode 100644 index 1e58faa5..00000000 --- a/dist/controller/uploadController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"uploadController.js","sourceRoot":"","sources":["../../src/controller/uploadController.ts"],"names":[],"mappings":";;AAGA,8CAMC;AAPD,+DAA2D;AAC3D,SAAgB,iBAAiB,CAAC,GAAmB,EAAE,GAAoB;IACvE,IAAI,CAAC,GAAG,CAAC,IAAI;QACT,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,SAAS,QAAQ,EAAE,CAAC;IACjC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACvB,CAAC"} \ No newline at end of file diff --git a/dist/controller/userController.d.ts b/dist/controller/userController.d.ts deleted file mode 100644 index 0f491de0..00000000 --- a/dist/controller/userController.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ExpressHandler } from '../libs/constants'; -export default class UserServiceController { - register: ExpressHandler; - login: ExpressHandler; - refresh: ExpressHandler; - GetMe: ExpressHandler; - updateMe: ExpressHandler; - updateMyPassword: ExpressHandler; - getMyProducts: ExpressHandler; - getMyLikedProducts: ExpressHandler; -} -//# sourceMappingURL=userController.d.ts.map \ No newline at end of file diff --git a/dist/controller/userController.d.ts.map b/dist/controller/userController.d.ts.map deleted file mode 100644 index a183bca4..00000000 --- a/dist/controller/userController.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userController.d.ts","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACtC,QAAQ,EAAE,cAAc,CAGtB;IAEF,KAAK,EAAE,cAAc,CAYnB;IACF,OAAO,EAAE,cAAc,CAYrB;IACF,KAAK,EAAE,cAAc,CAInB;IACF,QAAQ,EAAE,cAAc,CAItB;IAEF,gBAAgB,EAAE,cAAc,CAI9B;IAEF,aAAa,EAAE,cAAc,CAI3B;IAEF,kBAAkB,EAAE,cAAc,CAIhC;CACL"} \ No newline at end of file diff --git a/dist/controller/userController.js b/dist/controller/userController.js deleted file mode 100644 index b1992cbb..00000000 --- a/dist/controller/userController.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const userService_1 = require("../services/userService"); -class UserServiceController { - constructor() { - this.register = (req, res) => __awaiter(this, void 0, void 0, function* () { - const user = yield userService_1.userService.createUser(req.body); - return res.status(201).json(user); - }); - this.login = (req, res) => __awaiter(this, void 0, void 0, function* () { - const { email, password } = req.body; - const user = yield userService_1.userService.getUser(email, password); - const accessToken = userService_1.userService.createToken(user); - const refreshToken = userService_1.userService.createToken(user, 'refresh'); - yield userService_1.userService.updateUser(user.id, { refreshToken }); - res.cookie('refreshToken', refreshToken, { - httpOnly: true, - sameSite: 'none', - secure: true - }); - return res.status(200).json({ accessToken }); - }); - this.refresh = (req, res) => __awaiter(this, void 0, void 0, function* () { - const { refreshToken } = req.cookies; - const { userId } = req.auth; - const { accessToken, newRefreshToken } = yield userService_1.userService.refreshToken(userId, refreshToken); // 변경 - yield userService_1.userService.updateUser(userId, { refreshToken: newRefreshToken }); // 추가 - res.cookie('refreshToken', newRefreshToken, { - path: '/token/refresh', - httpOnly: true, - sameSite: 'none', - secure: true, - }); - return res.json({ accessToken }); - }); - this.GetMe = (req, res) => __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const user = yield userService_1.userService.getUserById(userId); - return res.status(200).json(user); - }); - this.updateMe = (req, res) => __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const updatedUser = yield userService_1.userService.updateProfile(userId, req.body); - return res.status(200).json(updatedUser); - }); - this.updateMyPassword = (req, res) => __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const updatedUser = yield userService_1.userService.updatePassword(userId, req.body); - return res.status(200).json(updatedUser); - }); - this.getMyProducts = (req, res) => __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const products = yield userService_1.userService.getProductsByUserId(userId); - return res.status(200).json(products); - }); - this.getMyLikedProducts = (req, res) => __awaiter(this, void 0, void 0, function* () { - const userId = req.user.userId; - const products = yield userService_1.userService.getLikedProductsByUserId(userId); - return res.status(200).json(products); - }); - } -} -exports.default = UserServiceController; -//# sourceMappingURL=userController.js.map \ No newline at end of file diff --git a/dist/controller/userController.js.map b/dist/controller/userController.js.map deleted file mode 100644 index 03cdd328..00000000 --- a/dist/controller/userController.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userController.js","sourceRoot":"","sources":["../../src/controller/userController.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts b/dist/libs/Handler/errorHandler.d.ts deleted file mode 100644 index c1e8a83b..00000000 --- a/dist/libs/Handler/errorHandler.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ExpressRequest, ExpressResponse, ExpressNextFunction } from './../constants'; -declare const errorHandler: (err: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => ExpressResponse; -export default errorHandler; -/** - * 모든 커스텀 에러의 기본이 되는 클래스 - * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. - */ -export declare class CustomError extends Error { - statusCode: number; - data: Record; - constructor(statusCode?: number, message?: string, data?: {}); -} -export declare const globalErrorHandler: (err: any, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void; -//# sourceMappingURL=errorHandler.d.ts.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.d.ts.map b/dist/libs/Handler/errorHandler.d.ts.map deleted file mode 100644 index fca1f698..00000000 --- a/dist/libs/Handler/errorHandler.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE9F,QAAA,MAAM,YAAY,GAAI,KAAK,GAAG,EACxB,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,oBAwDzE,CAAC;AAEF,eAAe,YAAY,CAAC;AAE5B;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAE3B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAExB,UAAU,SAAM,EAAE,OAAO,SAAK,EAAE,IAAI,KAAK;CASxD;AAED,eAAO,MAAM,kBAAkB,GAAI,KAAK,GAAG,EACrC,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,SAczE,CAAC"} \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js b/dist/libs/Handler/errorHandler.js deleted file mode 100644 index 4c0fdca8..00000000 --- a/dist/libs/Handler/errorHandler.js +++ /dev/null @@ -1,91 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.globalErrorHandler = exports.CustomError = void 0; -const multer_1 = __importDefault(require("multer")); -const constants_1 = require("./../constants"); -const errorHandler = (err, req, res, next) => { - // 1. Prisma 초기화 에러 (DB 연결 실패 등) - if (err instanceof constants_1.Prisma.PrismaClientInitializationError) { - return res.status(500).json({ - success: false, - message: '데이터베이스 연결에 실패했습니다. DATABASE_URL을 확인해주세요.' - }); - } - // 2. Prisma 요청 에러 (중복 키, 데이터 없음 등) - if (err instanceof constants_1.Prisma.PrismaClientKnownRequestError) { - if (err.code === 'P2002') { - return res.status(409).json({ - success: false, - message: '중복된 데이터가 존재합니다.' - }); - } - if (err.code === 'P2025') { - return res.status(404).json({ - success: false, - message: '요청한 데이터를 찾을 수 없습니다.' - }); - } - } - // 3. Multer(파일 업로드) 에러 - if (err instanceof multer_1.default.MulterError) { - if (err.code === 'LIMIT_FILE_SIZE') { - return res.status(400).json({ - success: false, - message: '파일 크기가 너무 큽니다.', - }); - } - if (err.code === 'LIMIT_FILE_COUNT') { - return res.status(400).json({ - success: false, - message: '파일 개수가 너무 많습니다.', - }); - } - } - // 4. CustomError 및 기타 에러 처리 - // any 타입이므로 안전하게 접근하기 위해 || 연산자 활용 - const statusCode = err.statusCode || 500; - const message = err.message || '서버 오류가 발생했습니다.'; - // path가 없는 에러 객체도 많으므로 안전하게 처리 - const path = err.data || null; - console.error(`[Error] ${statusCode} - ${message}`, err.stack); - return res.status(statusCode).json({ - success: false, - message, - path - }); -}; -exports.default = errorHandler; -/** - * 모든 커스텀 에러의 기본이 되는 클래스 - * HTTP 상태 코드와 메시지를 포함하여 Global Error Handler가 쉽게 처리할 수 있도록 합니다. - */ -class CustomError extends Error { - constructor(statusCode = 500, message = '', data = {}) { - super(message); - this.statusCode = statusCode; - // Error stack 추적을 위해 클래스 이름 설정 - this.name = this.constructor.name; - this.data = data; - // 생성자 함수를 호출 스택에서 제외 - Error.captureStackTrace(this, this.constructor); - } -} -exports.CustomError = CustomError; -const globalErrorHandler = (err, req, res, next) => { - // CustomError가 아닌 경우 (예: 서버 내부 오류), 500 상태 코드와 일반 메시지를 사용합니다. - const statusCode = err.statusCode || 500; - const message = err.message || 'Internal Server Error'; - // 에러를 콘솔에 기록 - console.error(`[${err.name} - ${statusCode}] ${err.message}`, err.stack); - res.status(statusCode).json({ - status: 'error', - message: message, - // 개발 환경에서만 스택 트레이스를 포함할 수 있습니다. - stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, - }); -}; -exports.globalErrorHandler = globalErrorHandler; -//# sourceMappingURL=errorHandler.js.map \ No newline at end of file diff --git a/dist/libs/Handler/errorHandler.js.map b/dist/libs/Handler/errorHandler.js.map deleted file mode 100644 index c97e8752..00000000 --- a/dist/libs/Handler/errorHandler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../src/libs/Handler/errorHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts b/dist/libs/catchAsync.d.ts deleted file mode 100644 index 6dea8364..00000000 --- a/dist/libs/catchAsync.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ExpressHandler } from './constants'; -export declare const catchAsync: (fn: ExpressHandler) => ExpressHandler; -export declare const catchAsyncAll: (...fns: ExpressHandler[]) => ExpressHandler[]; -//# sourceMappingURL=catchAsync.d.ts.map \ No newline at end of file diff --git a/dist/libs/catchAsync.d.ts.map b/dist/libs/catchAsync.d.ts.map deleted file mode 100644 index 3a40ccf6..00000000 --- a/dist/libs/catchAsync.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"catchAsync.d.ts","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwD,MAAM,aAAa,CAAC;AAEnG,eAAO,MAAM,UAAU,GAAI,IAAI,cAAc,KAAG,cAI/C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,GAAG,KAAK,cAAc,EAAE,KAAG,cAAc,EAEtE,CAAC"} \ No newline at end of file diff --git a/dist/libs/catchAsync.js b/dist/libs/catchAsync.js deleted file mode 100644 index a7d3b311..00000000 --- a/dist/libs/catchAsync.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.catchAsyncAll = exports.catchAsync = void 0; -const catchAsync = (fn) => { - return (req, res, next) => { - Promise.resolve(fn(req, res, next)).catch(next); - }; -}; -exports.catchAsync = catchAsync; -const catchAsyncAll = (...fns) => { - return fns.map(exports.catchAsync); -}; -exports.catchAsyncAll = catchAsyncAll; -//# sourceMappingURL=catchAsync.js.map \ No newline at end of file diff --git a/dist/libs/catchAsync.js.map b/dist/libs/catchAsync.js.map deleted file mode 100644 index 19d36a3f..00000000 --- a/dist/libs/catchAsync.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"catchAsync.js","sourceRoot":"","sources":["../../src/libs/catchAsync.ts"],"names":[],"mappings":";;;AAEO,MAAM,UAAU,GAAG,CAAC,EAAkB,EAAkB,EAAE;IAC7D,OAAO,CAAC,GAAmB,EAAE,GAAoB,EAAE,IAAyB,EAAE,EAAE;QAC5E,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC;AACN,CAAC,CAAC;AAJW,QAAA,UAAU,cAIrB;AAEK,MAAM,aAAa,GAAG,CAAC,GAAG,GAAqB,EAAoB,EAAE;IACxE,OAAO,GAAG,CAAC,GAAG,CAAC,kBAAU,CAAC,CAAC;AAC/B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB"} \ No newline at end of file diff --git a/dist/libs/constants.d.ts b/dist/libs/constants.d.ts deleted file mode 100644 index 71e53a24..00000000 --- a/dist/libs/constants.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PrismaClient, Prisma } from '@prisma/client'; -import express, { RequestHandler, Request, Response, NextFunction } from 'express'; -export declare const EXPRESS: typeof express; -export declare const PORT: string | number; -export declare const prismaClient: PrismaClient; -export { Prisma }; -export type ExpressHandler = RequestHandler; -export type ExpressRequest = Request; -export type ExpressResponse = Response; -export type ExpressNextFunction = NextFunction; -//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/dist/libs/constants.d.ts.map b/dist/libs/constants.d.ts.map deleted file mode 100644 index 870f744a..00000000 --- a/dist/libs/constants.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,OAAO,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAInF,eAAO,MAAM,OAAO,gBAAU,CAAC;AAC/B,eAAO,MAAM,IAAI,iBAA2B,CAAC;AAC7C,eAAO,MAAM,YAAY,uGAAmB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC;AAC5C,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AACrC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;AACvC,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/constants.js b/dist/libs/constants.js deleted file mode 100644 index 53a74ed4..00000000 --- a/dist/libs/constants.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Prisma = exports.prismaClient = exports.PORT = exports.EXPRESS = void 0; -const dotenv = __importStar(require("dotenv")); -const client_1 = require("@prisma/client"); -Object.defineProperty(exports, "Prisma", { enumerable: true, get: function () { return client_1.Prisma; } }); -const express_1 = __importDefault(require("express")); -dotenv.config(); -exports.EXPRESS = express_1.default; -exports.PORT = process.env.PORT || 3000; -exports.prismaClient = new client_1.PrismaClient; -//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/dist/libs/constants.js.map b/dist/libs/constants.js.map deleted file mode 100644 index b28f5b50..00000000 --- a/dist/libs/constants.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/libs/constants.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,2CAAsD;AAQ7C,uFARc,eAAM,OAQd;AAPf,sDAAmF;AAEnF,MAAM,CAAC,MAAM,EAAE,CAAC;AAEH,QAAA,OAAO,GAAG,iBAAO,CAAC;AAClB,QAAA,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAChC,QAAA,YAAY,GAAG,IAAI,qBAAY,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts b/dist/libs/corsSetUp.d.ts deleted file mode 100644 index 898f7ad0..00000000 --- a/dist/libs/corsSetUp.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import 'dotenv/config'; -export declare const getCorsOrigin: () => string | string[]; -//# sourceMappingURL=corsSetUp.d.ts.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.d.ts.map b/dist/libs/corsSetUp.d.ts.map deleted file mode 100644 index f9c29914..00000000 --- a/dist/libs/corsSetUp.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"corsSetUp.d.ts","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,eAAO,MAAM,aAAa,QAAO,MAAM,GAAG,MAAM,EAS/C,CAAC"} \ No newline at end of file diff --git a/dist/libs/corsSetUp.js b/dist/libs/corsSetUp.js deleted file mode 100644 index 7563d23d..00000000 --- a/dist/libs/corsSetUp.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getCorsOrigin = void 0; -require("dotenv/config"); -const getCorsOrigin = () => { - const corsOrigin = process.env.CORS_ORIGIN; - if (!corsOrigin || corsOrigin === '*') - return '*'; - if (corsOrigin.includes(',')) - return corsOrigin.split(',').map((origin) => origin.trim().replace(/\/$/, '')); - // 단일 origin (trailing slash 제거) - return corsOrigin.trim().replace(/\/$/, ''); -}; -exports.getCorsOrigin = getCorsOrigin; -//# sourceMappingURL=corsSetUp.js.map \ No newline at end of file diff --git a/dist/libs/corsSetUp.js.map b/dist/libs/corsSetUp.js.map deleted file mode 100644 index 2be0f830..00000000 --- a/dist/libs/corsSetUp.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"corsSetUp.js","sourceRoot":"","sources":["../../src/libs/corsSetUp.ts"],"names":[],"mappings":";;;AAAA,yBAAuB;AAChB,MAAM,aAAa,GAAG,GAAsB,EAAE;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IAC3C,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAEnF,gCAAgC;IAChC,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC;AATW,QAAA,aAAa,iBASxB"} \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts b/dist/libs/interfaces.d.ts deleted file mode 100644 index 050f7c2d..00000000 --- a/dist/libs/interfaces.d.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Prisma } from './../libs/constants'; -export interface ProductType { - id: number; - name: string; - userId: number; - description?: string; - price: number; - tags: string[]; - createdAt: Date; - updatedAt: Date; - comments?: CommentType[]; - productLikes?: ProductLikeType[]; -} -export interface ArticleType { - id: number; - title: string; - userId: number; - content: string; - createdAt: Date; - comments?: CommentType[]; - articleLikes?: ArticleLikeType[]; -} -export interface CommentType { - id: number; - content: string; - createdAt: Date; - updatedAt: Date; - userId: number; - productId?: number; - articleId?: number; - user: UserType; - product?: ProductType; - article?: ArticleType; -} -export interface UserType { - id: number; - email: string; - password: string; - nickname: string; - image?: string | null; - createdAt: Date; - updatedAt: Date; - refreshToken?: string | null; - productLikes?: ProductLikeType[]; - articleLikes?: ArticleLikeType[]; -} -export interface UpdateUserPasswordType { - currentPassword: string; - newPassword: string; - confirmNewPassword: string; -} -export interface ProductLikeType { - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - productId: number; - user: UserType; - product: ProductType; -} -export interface ArticleLikeType { - id: number; - userId: number; - articleId: number; - user: UserType; - article: ArticleType; - createdAt: Date; -} -export type ProductFindOptions = Prisma.ProductFindManyArgs; -export type ArticleFindOptions = Prisma.ArticleFindManyArgs; -export interface ProductWithLikes extends ProductType { - productLikes: ProductLikeType[]; -} -export interface ProductWithIsLiked extends ProductType { - isLiked: boolean; -} -export interface ArticleWithLikes extends ArticleType { - articleLikes: ArticleLikeType[]; -} -export interface ArticleWithIsLiked extends ArticleType { - isLiked: boolean; -} -export type UserPublicData = Omit; -export type ProductPublicData = Omit; -export type UpdateProductData = Partial; -export type ArticlePublicData = Omit; -export type UpdateArticleData = Partial; -//# sourceMappingURL=interfaces.d.ts.map \ No newline at end of file diff --git a/dist/libs/interfaces.d.ts.map b/dist/libs/interfaces.d.ts.map deleted file mode 100644 index e0c71028..00000000 --- a/dist/libs/interfaces.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/libs/interfaces.js b/dist/libs/interfaces.js deleted file mode 100644 index db919115..00000000 --- a/dist/libs/interfaces.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=interfaces.js.map \ No newline at end of file diff --git a/dist/libs/interfaces.js.map b/dist/libs/interfaces.js.map deleted file mode 100644 index cbb18070..00000000 --- a/dist/libs/interfaces.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/libs/interfaces.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/libs/removeTool.d.ts b/dist/libs/removeTool.d.ts deleted file mode 100644 index 840d8a3a..00000000 --- a/dist/libs/removeTool.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare const removeUndefined: (obj: any) => any; -//# sourceMappingURL=removeTool.d.ts.map \ No newline at end of file diff --git a/dist/libs/removeTool.d.ts.map b/dist/libs/removeTool.d.ts.map deleted file mode 100644 index f7f0ec74..00000000 --- a/dist/libs/removeTool.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"removeTool.d.ts","sourceRoot":"","sources":["../../src/libs/removeTool.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,GAAI,KAAK,GAAG,QAQvC,CAAC"} \ No newline at end of file diff --git a/dist/libs/removeTool.js b/dist/libs/removeTool.js deleted file mode 100644 index 1120835f..00000000 --- a/dist/libs/removeTool.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeUndefined = void 0; -// undefined인 키를 싹 지워주는 함수 -const removeUndefined = (obj) => { - const newObj = {}; - Object.keys(obj).forEach((key) => { - if (obj[key] !== undefined) { - newObj[key] = obj[key]; - } - }); - return newObj; -}; -exports.removeUndefined = removeUndefined; -//# sourceMappingURL=removeTool.js.map \ No newline at end of file diff --git a/dist/libs/removeTool.js.map b/dist/libs/removeTool.js.map deleted file mode 100644 index 4e1733c8..00000000 --- a/dist/libs/removeTool.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"removeTool.js","sourceRoot":"","sources":["../../src/libs/removeTool.ts"],"names":[],"mappings":";;;AAAA,0BAA0B;AACnB,MAAM,eAAe,GAAG,CAAC,GAAQ,EAAE,EAAE;IACxC,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7B,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AARW,QAAA,eAAe,mBAQ1B"} \ No newline at end of file diff --git a/dist/main.d.ts b/dist/main.d.ts deleted file mode 100644 index 20bc8c4a..00000000 --- a/dist/main.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare const app: import("express-serve-static-core").Express; -export default app; -//# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/main.d.ts.map b/dist/main.d.ts.map deleted file mode 100644 index 4df96fc2..00000000 --- a/dist/main.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAiEtB,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js deleted file mode 100644 index 731a45a7..00000000 --- a/dist/main.js +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("./libs/constants"); -const cors_1 = __importDefault(require("cors")); -const routerManager_1 = require("./Routers/routerManager"); -const corsSetUp_1 = require("./libs/corsSetUp"); -const errorHandler_1 = __importDefault(require("./libs/Handler/errorHandler")); -const http_1 = require("http"); -const socket_io_1 = require("socket.io"); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const app = (0, constants_1.EXPRESS)(); -const httpServer = (0, http_1.createServer)(app); -const io = new socket_io_1.Server(httpServer, { - cors: { - origin: (0, corsSetUp_1.getCorsOrigin)(), // Express에서 쓰던 것과 똑같이 맞춰주세요 - methods: ["GET", "POST"], - credentials: true - } -}); -io.use((socket, next) => { - const token = socket.handshake.auth.accessToken; - if (!token) { - return next(new Error('Authentication error')); - } - try { - const payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET); - socket.data.userId = payload.userId; - next(); - } - catch (e) { - next(new Error('Authentication error')); - } -}); -app.set('io', io); -app.use((0, cors_1.default)({ - origin: (0, corsSetUp_1.getCorsOrigin)(), - credentials: true, - methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], - allowedHeaders: ['Content-Type', 'Authorization'] -})); -app.use(constants_1.EXPRESS.static('public')); -app.use(constants_1.EXPRESS.json()); -app.use(constants_1.EXPRESS.urlencoded({ extended: true })); -app.use('/upload', constants_1.EXPRESS.static('upload')); -app.use('/', routerManager_1.RouterManager); -app.use(errorHandler_1.default); -// --- 소켓 연결 이벤트 (테스트용) --- -io.on('connection', (socket) => { - console.log('새로운 소켓 연결:', socket.id); - // 로그인하면 해당 유저의 ID로 방을 만들어줌 (알림 기능을 위해 필수) - const userId = socket.data.userId; - if (userId) { - socket.join(userId); - console.log(`User ${userId} joined room`); - } -}); -httpServer.listen(constants_1.PORT, () => { - console.log(`Server is running on port ${constants_1.PORT}`); -}); -exports.default = app; -//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map deleted file mode 100644 index accc0fe7..00000000 --- a/dist/main.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts b/dist/middlewares/auth.d.ts deleted file mode 100644 index 59c495ca..00000000 --- a/dist/middlewares/auth.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ExpressHandler, ExpressRequest, ExpressResponse, ExpressNextFunction } from '../libs/constants'; -declare function verifyProduectAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction): Promise; -declare function verifyArticleAuth(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction): Promise; -declare const _default: { - verifyAccessToken: ExpressHandler; - softVerifyAccessToken: ExpressHandler; - verifyProduectAuth: typeof verifyProduectAuth; - verifyArticleAuth: typeof verifyArticleAuth; - verifyRefreshToken: ExpressHandler; -}; -export default _default; -//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/dist/middlewares/auth.d.ts.map b/dist/middlewares/auth.d.ts.map deleted file mode 100644 index 7d975c9a..00000000 --- a/dist/middlewares/auth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAkCzG,iBAAe,kBAAkB,CAC5B,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,iBAaxE;AAED,iBAAe,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,iBAapG;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/middlewares/auth.js b/dist/middlewares/auth.js deleted file mode 100644 index 024cf9c1..00000000 --- a/dist/middlewares/auth.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_jwt_1 = require("express-jwt"); // express-jwt 라이브러리 임포트 (JWT 검증 미들웨어 생성용) -const productRepository_1 = __importDefault(require("../repositories/productRepository")); // 상품 데이터 접근을 위한 레포지토리 임포트 -const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); // 게시글 데이터 접근을 위한 레포지토리 임포트 -const errorHandler_1 = require("../libs/Handler/errorHandler"); // 커스텀 에러 핸들러 임포트 -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); // jsonwebtoken 라이브러리 및 타입 임포트 -const JWT_SECRET = process.env.JWT_SECRET; // 환경 변수에서 JWT 비밀키를 가져옴 -if (!JWT_SECRET) { // 비밀키가 설정되지 않은 경우 에러 발생 - throw new Error('JWT_SECRET is not defined in environment variables.'); // 서버 실행 중단 및 에러 메시지 출력 -} // 비밀키 확인 종료 -const verifyAccessToken = (0, express_jwt_1.expressjwt)({ - secret: JWT_SECRET, // 검증에 사용할 비밀키 설정 - algorithms: ["HS256"], // 사용할 암호화 알고리즘 지정 - requestProperty: 'user' // 검증된 페이로드를 req.user 객체에 저장하도록 설정 -}); // 액세스 토큰 검증 설정 종료 -const softVerifyAccessToken = (req, res, next) => { - var _a; - const token = (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.split(' ')[1]; // Authorization 헤더에서 'Bearer '를 제외한 토큰 문자열 추출 - if (token) { // 토큰이 존재하는 경우 - jsonwebtoken_1.default.verify(token, JWT_SECRET, (err, decoded) => { - if (!err && decoded && typeof decoded === 'object') { // 에러가 없고 유효한 객체 형태의 페이로드인 경우 - req.user = decoded; // req.user에 사용자 정보 할당 - } // 유효성 확인 종료 - next(); // 다음 미들웨어로 진행 (검증 실패해도 진행) - }); // 검증 함수 종료 - } - else { // 토큰이 없는 경우 - next(); // 바로 다음 미들웨어로 진행 - } // 토큰 존재 여부 확인 종료 -}; // softVerifyAccessToken 종료 -const verifyRefreshToken = (0, express_jwt_1.expressjwt)({ - secret: JWT_SECRET, // 검증용 비밀키 - algorithms: ['HS256'], // 알고리즘 - getToken: (req) => req.cookies.refreshToken, // 쿠키에서 리프레시 토큰을 가져오도록 설정 -}); // 리프레시 토큰 검증 설정 종료 -function verifyProduectAuth // 상품 수정/삭제 권한 확인 함수 -(req, res, next) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id || ""; // URL 파라미터에서 상품 ID 추출 - if (!id) - throw new errorHandler_1.CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 - const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 - const product = yield productRepository_1.default.findById(idtoNumber); // DB에서 해당 상품 조회 - if (!product) { // 상품이 존재하지 않는 경우 - throw new errorHandler_1.CustomError(404, 'product not found'); // 404 에러 발생 - } // 상품 존재 확인 종료 - // 사용자 정보가 없거나, 상품의 소유자 ID(현재 product.id로 되어있으나 보통 product.userId)가 요청자 ID와 다르면 권한 없음 처리 - if (typeof req.user !== 'object' || req.user.userId === undefined || product.id !== req.user.userId) { - throw new errorHandler_1.CustomError(403, 'Forbidden'); // 403 금지 에러 발생 - } // 권한 확인 종료 - return next(); // 권한이 확인되면 다음 미들웨어로 진행 - }); -} -; // verifyProduectAuth 종료 -function verifyArticleAuth(req, res, next) { - return __awaiter(this, void 0, void 0, function* () { - const id = req.params.id; // URL 파라미터에서 게시글 ID 추출 - if (!id) - throw new errorHandler_1.CustomError(404, "id가 비 정상적인 값 입니다"); // ID가 없으면 404 에러 발생 - const idtoNumber = parseInt(id); // 문자열 ID를 숫자로 변환 - const article = yield articleRepository_1.default.findById(idtoNumber); // DB에서 해당 게시글 조회 - if (!article) { // 게시글이 존재하지 않는 경우 - throw new errorHandler_1.CustomError(404, 'article not found'); // 404 에러 발생 - } // 게시글 존재 확인 종료 - // 사용자 정보가 없거나, 게시글의 소유자 ID(현재 article.id로 되어있으나 보통 article.userId)가 요청자 ID와 다르면 권한 없음 처리 - if (typeof req.user !== 'object' || req.user.userId === undefined || article.id !== req.user.userId) { - throw new errorHandler_1.CustomError(403, 'Forbidden'); // 403 금지 에러 발생 - } // 권한 확인 종료 - return next(); // 권한이 확인되면 다음 미들웨어로 진행 - }); -} // verifyArticleAuth 종료 -exports.default = { - verifyAccessToken, - softVerifyAccessToken, - verifyProduectAuth, - verifyArticleAuth, - verifyRefreshToken, -}; -//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/dist/middlewares/auth.js.map b/dist/middlewares/auth.js.map deleted file mode 100644 index c3d77be2..00000000 --- a/dist/middlewares/auth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middlewares/auth.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts b/dist/repositories/articleLikeRepository.d.ts deleted file mode 100644 index 06a022de..00000000 --- a/dist/repositories/articleLikeRepository.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -declare function find(userId: number, articleId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -} | null>; -declare function create(userId: number, articleId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -}>; -declare function remove(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - articleId: number; -}>; -declare const _default: { - find: typeof find; - create: typeof create; - remove: typeof remove; -}; -export default _default; -//# sourceMappingURL=articleLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.d.ts.map b/dist/repositories/articleLikeRepository.d.ts.map deleted file mode 100644 index 536644aa..00000000 --- a/dist/repositories/articleLikeRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;;;;;;AAED,wBAIE"} \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js b/dist/repositories/articleLikeRepository.js deleted file mode 100644 index 37052fc7..00000000 --- a/dist/repositories/articleLikeRepository.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function find(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.findUnique({ - where: { - userId_articleId: { - userId, - articleId, - }, - }, - }); - }); -} -function create(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.create({ - data: { - userId, - articleId, - }, - }); - }); -} -function remove(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.articleLike.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - find, - create, - remove, -}; -//# sourceMappingURL=articleLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleLikeRepository.js.map b/dist/repositories/articleLikeRepository.js.map deleted file mode 100644 index 3b0581d5..00000000 --- a/dist/repositories/articleLikeRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/articleLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAc,EAAE,SAAiB;;QACjD,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAc,EAAE,SAAiB;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU;;QAC5B,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts b/dist/repositories/articleRepository.d.ts deleted file mode 100644 index b4a60bf0..00000000 --- a/dist/repositories/articleRepository.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ArticlePublicData, ArticleFindOptions, UpdateArticleData } from '../libs/interfaces'; -declare function findById(id: number, userId?: number): Promise<{ - articleLikes: { - id: number; - createdAt: Date; - userId: number; - articleId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; -}>; -declare function findAll(findOptions: ArticleFindOptions, userId: number): Promise<({ - articleLikes: { - id: number; - createdAt: Date; - userId: number; - articleId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; -})[]>; -declare function create(userFields: ArticlePublicData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; -}>; -declare function update(id: number, data: UpdateArticleData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; -}>; -declare function ondelete(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; -}>; -declare const _default: { - findById: typeof findById; - findAll: typeof findAll; - create: typeof create; - update: typeof update; - ondelete: typeof ondelete; -}; -export default _default; -//# sourceMappingURL=articleRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.d.ts.map b/dist/repositories/articleRepository.d.ts.map deleted file mode 100644 index f3c5e93c..00000000 --- a/dist/repositories/articleRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":"AACA,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;MASrE;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;GAQlD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;GAQxD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;GAMjC;;;;;;;;AAED,wBAME"} \ No newline at end of file diff --git a/dist/repositories/articleRepository.js b/dist/repositories/articleRepository.js deleted file mode 100644 index 7d2e1d77..00000000 --- a/dist/repositories/articleRepository.js +++ /dev/null @@ -1,80 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findById(id, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - articleLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.article.findUniqueOrThrow({ - where: { - id, - }, - include, - }); - }); -} -function findAll(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - articleLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.article.findMany(Object.assign(Object.assign({}, findOptions), { include })); - }); -} -function create(userFields) { - return __awaiter(this, void 0, void 0, function* () { - const { comments, articleLikes } = userFields, NewuserFields = __rest(userFields, ["comments", "articleLikes"]); - return yield constants_1.prismaClient.article.create({ - data: Object.assign({}, NewuserFields), - }); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - const { comments, articleLikes } = data, updateData = __rest(data, ["comments", "articleLikes"]); - return constants_1.prismaClient.article.update({ - where: { - id, - }, - data: updateData, - }); - }); -} -function ondelete(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_1.prismaClient.article.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - findById, - findAll, - create, - update, - ondelete, -}; -//# sourceMappingURL=articleRepository.js.map \ No newline at end of file diff --git a/dist/repositories/articleRepository.js.map b/dist/repositories/articleRepository.js.map deleted file mode 100644 index 32bb34d2..00000000 --- a/dist/repositories/articleRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleRepository.js","sourceRoot":"","sources":["../../src/repositories/articleRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAOjD,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QAEF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAEhE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;CACX,CAAC"} \ No newline at end of file diff --git a/dist/repositories/commentRepository.d.ts b/dist/repositories/commentRepository.d.ts deleted file mode 100644 index 368030f3..00000000 --- a/dist/repositories/commentRepository.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -declare function findAll(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; -}[]>; -declare function findById(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; -}>; -declare function create(content: string, userId: number, productId?: number, articleId?: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; -}>; -declare function update(id: number, content: string): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; -}>; -declare function deleteComment(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; -}>; -declare function findProductOwner(productId: number): Promise<{ - name: string; - userId: number; -} | null>; -declare function findArticleOwner(articleId: number): Promise<{ - userId: number; - title: string; -} | null>; -declare function createNotification(userId: number, content: string): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; -}>; -declare const _default: { - findAll: typeof findAll; - findById: typeof findById; - create: typeof create; - update: typeof update; - findProductOwner: typeof findProductOwner; - findArticleOwner: typeof findArticleOwner; - createNotification: typeof createNotification; - delete: typeof deleteComment; -}; -export default _default; -//# sourceMappingURL=commentRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/commentRepository.d.ts.map b/dist/repositories/commentRepository.d.ts.map deleted file mode 100644 index 6abaeaad..00000000 --- a/dist/repositories/commentRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/commentRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;;;;;;;KAyB5H;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;GAIjC;AAED,iBAAe,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;GAS5F;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;GAKhD;AAED,iBAAe,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;GAItC;AAED,iBAAe,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;UAKhD;AAGD,iBAAe,gBAAgB,CAAC,SAAS,EAAE,MAAM;;;UAKhD;AAGD,iBAAe,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;GAQhE;;;;;;;;;;;AACD,wBASE"} \ No newline at end of file diff --git a/dist/repositories/commentRepository.js b/dist/repositories/commentRepository.js deleted file mode 100644 index d6322db6..00000000 --- a/dist/repositories/commentRepository.js +++ /dev/null @@ -1,112 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findAll(take, cursor, productId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - const whereClause = {}; - if (productId) { - whereClause.productId = productId; - } - else if (articleId) { - whereClause.articleId = articleId; - } - const findOptions = { - take: take, - where: whereClause, - orderBy: { - createdAt: 'desc', - }, - }; - if (cursor) { - findOptions.skip = 1; - findOptions.cursor = { - id: cursor, - }; - } - return constants_1.prismaClient.comment.findMany(findOptions); - }); -} -function findById(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.comment.findUniqueOrThrow({ - where: { id }, - }); - }); -} -function create(content, userId, productId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.comment.create({ - data: { - content, - userId, - productId, - articleId, - }, - }); - }); -} -function update(id, content) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.comment.update({ - where: { id }, - data: { content }, - }); - }); -} -function deleteComment(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.comment.delete({ - where: { id }, - }); - }); -} -// [추가] 상품 주인 찾기 -function findProductOwner(productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.product.findUnique({ - where: { id: productId }, - select: { userId: true, name: true } // 주인 ID와 상품명만 가져옴 - }); - }); -} -// [추가] 아티클 주인 찾기 -function findArticleOwner(articleId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.article.findUnique({ - where: { id: articleId }, - select: { userId: true, title: true } // 주인 ID와 제목만 가져옴 - }); - }); -} -// [추가] 알림 생성 -function createNotification(userId, content) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.create({ - data: { - userId, - content, - isRead: false, - } - }); - }); -} -exports.default = { - findAll, - findById, - create, - update, - findProductOwner, - findArticleOwner, - createNotification, - delete: deleteComment, -}; -//# sourceMappingURL=commentRepository.js.map \ No newline at end of file diff --git a/dist/repositories/commentRepository.js.map b/dist/repositories/commentRepository.js.map deleted file mode 100644 index 872acb1a..00000000 --- a/dist/repositories/commentRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentRepository.js","sourceRoot":"","sources":["../../src/repositories/commentRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAyD;AAEzD,SAAe,OAAO,CAAC,IAAY,EAAE,MAA0B,EAAE,SAA6B,EAAE,SAA6B;;QACzH,MAAM,WAAW,GAA6B,EAAE,CAAC;QAEjD,IAAI,SAAS,EAAE,CAAC;YACZ,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC;QACtC,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACnB,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC;QACtC,CAAC;QAED,MAAM,WAAW,GAA+B;YAC5C,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;gBACL,SAAS,EAAE,MAAM;aACpB;SACJ,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC;YACrB,WAAW,CAAC,MAAM,GAAG;gBACjB,EAAE,EAAE,MAAM;aACb,CAAC;QACN,CAAC;QAED,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,OAAe,EAAE,MAAc,EAAE,SAAkB,EAAE,SAAkB;;QACzF,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,IAAI,EAAE;gBACF,OAAO;gBACP,MAAM;gBACN,SAAS;gBACT,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,OAAe;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,OAAO,EAAE;SACpB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,aAAa,CAAC,EAAU;;QACnC,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AACD,gBAAgB;AAChB,SAAe,gBAAgB,CAAC,SAAiB;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,kBAAkB;SAC1D,CAAC,CAAC;IACP,CAAC;CAAA;AAED,iBAAiB;AACjB,SAAe,gBAAgB,CAAC,SAAiB;;QAC7C,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,iBAAiB;SAC1D,CAAC,CAAC;IACP,CAAC;CAAA;AAED,aAAa;AACb,SAAe,kBAAkB,CAAC,MAAc,EAAE,OAAe;;QAC7D,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,IAAI,EAAE;gBACF,MAAM;gBACN,OAAO;gBACP,MAAM,EAAE,KAAK;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AACD,kBAAe;IACX,OAAO;IACP,QAAQ;IACR,MAAM;IACN,MAAM;IACN,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,MAAM,EAAE,aAAa;CACxB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/notificationRepository.d.ts b/dist/repositories/notificationRepository.d.ts deleted file mode 100644 index 880acfdf..00000000 --- a/dist/repositories/notificationRepository.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -declare function findAll(userId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; -}[]>; -declare function countUnread(userId: number): Promise; -declare function findById(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; -} | null>; -declare function markAsRead(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; -}>; -declare const _default: { - findAll: typeof findAll; - countUnread: typeof countUnread; - findById: typeof findById; - markAsRead: typeof markAsRead; -}; -export default _default; -//# sourceMappingURL=notificationRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/notificationRepository.d.ts.map b/dist/repositories/notificationRepository.d.ts.map deleted file mode 100644 index ff96ed0c..00000000 --- a/dist/repositories/notificationRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/notificationRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,OAAO,CAAC,MAAM,EAAE,MAAM;;;;;;KAKpC;AAED,iBAAe,WAAW,CAAC,MAAM,EAAE,MAAM,mBAOxC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;UAIjC;AAED,iBAAe,UAAU,CAAC,EAAE,EAAE,MAAM;;;;;;GAKnC;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/notificationRepository.js b/dist/repositories/notificationRepository.js deleted file mode 100644 index b3ff7ddc..00000000 --- a/dist/repositories/notificationRepository.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findAll(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.findMany({ - where: { userId }, - orderBy: { createdAt: 'desc' }, - }); - }); -} -function countUnread(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.count({ - where: { - userId, - isRead: false, - }, - }); - }); -} -function findById(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.findUnique({ - where: { id }, - }); - }); -} -function markAsRead(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.update({ - where: { id }, - data: { isRead: true }, - }); - }); -} -exports.default = { - findAll, - countUnread, - findById, - markAsRead, -}; -//# sourceMappingURL=notificationRepository.js.map \ No newline at end of file diff --git a/dist/repositories/notificationRepository.js.map b/dist/repositories/notificationRepository.js.map deleted file mode 100644 index ec39f4dd..00000000 --- a/dist/repositories/notificationRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationRepository.js","sourceRoot":"","sources":["../../src/repositories/notificationRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,OAAO,CAAC,MAAc;;QACjC,OAAO,wBAAY,CAAC,YAAY,CAAC,QAAQ,CAAC;YACtC,KAAK,EAAE,EAAE,MAAM,EAAE;YACjB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SACjC,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,MAAc;;QACrC,OAAO,wBAAY,CAAC,YAAY,CAAC,KAAK,CAAC;YACnC,KAAK,EAAE;gBACH,MAAM;gBACN,MAAM,EAAE,KAAK;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,YAAY,CAAC,UAAU,CAAC;YACxC,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,UAAU,CAAC,EAAU;;QAChC,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,OAAO;IACP,WAAW;IACX,QAAQ;IACR,UAAU;CACb,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts b/dist/repositories/productLikeRepository.d.ts deleted file mode 100644 index 785af337..00000000 --- a/dist/repositories/productLikeRepository.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -declare function find(userId: number, productId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -} | null>; -declare function create(userId: number, productId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -}>; -declare function remove(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - productId: number; -}>; -declare function findLikedProductsByUserId(userId: number): Promise<({ - product: { - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }; -} & { - id: number; - createdAt: Date; - userId: number; - productId: number; -})[]>; -declare const _default: { - find: typeof find; - create: typeof create; - remove: typeof remove; - findLikedProductsByUserId: typeof findLikedProductsByUserId; -}; -export default _default; -//# sourceMappingURL=productLikeRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.d.ts.map b/dist/repositories/productLikeRepository.d.ts.map deleted file mode 100644 index 9620c392..00000000 --- a/dist/repositories/productLikeRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productLikeRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":"AAEA,iBAAe,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;UASpD;AAED,iBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;GAOtD;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM;;;;;GAM/B;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;MAStD;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js b/dist/repositories/productLikeRepository.js deleted file mode 100644 index 1aec8927..00000000 --- a/dist/repositories/productLikeRepository.js +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function find(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.productLike.findUnique({ - where: { - userId_productId: { - userId, - productId, - }, - }, - }); - }); -} -function create(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.productLike.create({ - data: { - userId, - productId, - }, - }); - }); -} -function remove(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.productLike.delete({ - where: { - id, - }, - }); - }); -} -function findLikedProductsByUserId(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.productLike.findMany({ - where: { - userId, - }, - include: { - product: true, - }, - }); - }); -} -exports.default = { - find, - create, - remove, - findLikedProductsByUserId, -}; -//# sourceMappingURL=productLikeRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productLikeRepository.js.map b/dist/repositories/productLikeRepository.js.map deleted file mode 100644 index 33e8565a..00000000 --- a/dist/repositories/productLikeRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productLikeRepository.js","sourceRoot":"","sources":["../../src/repositories/productLikeRepository.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,iDAAiD;AAEjD,SAAe,IAAI,CAAC,MAAc,EAAE,SAAiB;;QACjD,OAAO,wBAAY,CAAC,WAAW,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE;gBACH,gBAAgB,EAAE;oBACd,MAAM;oBACN,SAAS;iBACZ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,MAAc,EAAE,SAAiB;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,MAAM;gBACN,SAAS;aACZ;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU;;QAC5B,OAAO,wBAAY,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,yBAAyB,CAAC,MAAc;;QACnD,OAAO,wBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE;gBACH,MAAM;aACT;YACD,OAAO,EAAE;gBACL,OAAO,EAAE,IAAI;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,IAAI;IACJ,MAAM;IACN,MAAM;IACN,yBAAyB;CAC5B,CAAC"} \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts b/dist/repositories/productRepository.d.ts deleted file mode 100644 index 9f6a2055..00000000 --- a/dist/repositories/productRepository.d.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { ProductPublicData, ProductFindOptions, UpdateProductData } from './../libs/interfaces'; -declare function findByIdSimple(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -} | null>; -declare function findLikers(productId: number): Promise<{ - userId: number; -}[]>; -declare function createNotification(userId: number, content: string): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; -}>; -declare function findByUserId(userId: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -}[]>; -declare function findById(id: number, userId?: number): Promise<{ - productLikes: { - id: number; - createdAt: Date; - userId: number; - productId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -}>; -declare function findAll(findOptions: ProductFindOptions, userId: number): Promise<({ - productLikes: { - id: number; - createdAt: Date; - userId: number; - productId: number; - }[]; -} & { - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -})[]>; -declare function update(id: number, data: UpdateProductData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -}>; -declare function create(userFields: ProductPublicData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -}>; -declare function ondelete(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; -}>; -declare const _default: { - findById: typeof findById; - findAll: typeof findAll; - update: typeof update; - create: typeof create; - ondelete: typeof ondelete; - findByUserId: typeof findByUserId; - findByIdSimple: typeof findByIdSimple; - findLikers: typeof findLikers; - createNotification: typeof createNotification; -}; -export default _default; -//# sourceMappingURL=productRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/productRepository.d.ts.map b/dist/repositories/productRepository.d.ts.map deleted file mode 100644 index d1492e92..00000000 --- a/dist/repositories/productRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGhG,iBAAe,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAIvC;AACD,iBAAe,UAAU,CAAC,SAAS,EAAE,MAAM;;KAK1C;AACD,iBAAe,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;GAQhE;AACD,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM;;;;;;;;;KAMzC;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;GAUlD;AAED,iBAAe,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;MAQrE;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;;;;;;;;;GAQxD;AAED,iBAAe,MAAM,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;;GAOlD;AAED,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;GAMjC;;;;;;;;;;;;AAED,wBAUE"} \ No newline at end of file diff --git a/dist/repositories/productRepository.js b/dist/repositories/productRepository.js deleted file mode 100644 index d808f87f..00000000 --- a/dist/repositories/productRepository.js +++ /dev/null @@ -1,119 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findByIdSimple(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.product.findUnique({ - where: { id }, - }); - }); -} -function findLikers(productId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.productLike.findMany({ - where: { productId }, - select: { userId: true } // 유저 ID만 쏙 뽑아옵니다. - }); - }); -} -function createNotification(userId, content) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.notification.create({ - data: { - userId, - content, - isRead: false, // 안 읽음 상태로 생성 - } - }); - }); -} -function findByUserId(userId) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.product.findMany({ - where: { - id: userId, - }, - }); - }); -} -function findById(id, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - productLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.product.findUniqueOrThrow({ - where: { - id, - }, - include, - }); - }); -} -function findAll(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const include = { - productLikes: userId ? { where: { userId } } : false, - }; - return constants_1.prismaClient.product.findMany(Object.assign(Object.assign({}, findOptions), { include })); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - const { comments, productLikes } = data, updateData = __rest(data, ["comments", "productLikes"]); - return constants_1.prismaClient.product.update({ - where: { - id, - }, - data: updateData, - }); - }); -} -function create(userFields) { - return __awaiter(this, void 0, void 0, function* () { - const { comments, productLikes } = userFields, NewuserFields = __rest(userFields, ["comments", "productLikes"]); - return yield constants_1.prismaClient.product.create({ - data: Object.assign({}, NewuserFields), - }); - }); -} -function ondelete(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_1.prismaClient.product.delete({ - where: { - id, - }, - }); - }); -} -exports.default = { - findById, - findAll, - update, - create, - ondelete, - findByUserId, - findByIdSimple, - findLikers, - createNotification -}; -//# sourceMappingURL=productRepository.js.map \ No newline at end of file diff --git a/dist/repositories/productRepository.js.map b/dist/repositories/productRepository.js.map deleted file mode 100644 index b172031f..00000000 --- a/dist/repositories/productRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productRepository.js","sourceRoot":"","sources":["../../src/repositories/productRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAIjD,SAAe,cAAc,CAAC,EAAU;;QACpC,OAAO,wBAAY,CAAC,OAAO,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE;SAChB,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,UAAU,CAAC,SAAiB;;QACvC,OAAO,wBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,kBAAkB;SAC9C,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,kBAAkB,CAAC,MAAc,EAAE,OAAe;;QAC7D,OAAO,wBAAY,CAAC,YAAY,CAAC,MAAM,CAAC;YACpC,IAAI,EAAE;gBACF,MAAM;gBACN,OAAO;gBACP,MAAM,EAAE,KAAK,EAAE,cAAc;aAChC;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AACD,SAAe,YAAY,CAAC,MAAc;;QACtC,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,KAAK,EAAE;gBACH,EAAE,EAAE,MAAM;aACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU,EAAE,MAAe;;QAC/C,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC;YAC1C,KAAK,EAAE;gBACH,EAAE;aACL;YACD,OAAO;SACV,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,OAAO,CAAC,WAA+B,EAAE,MAAc;;QAClE,MAAM,OAAO,GAAG;YACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;SACvD,CAAC;QACF,OAAO,wBAAY,CAAC,OAAO,CAAC,QAAQ,iCAC7B,WAAW,KACd,OAAO,IACT,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAoB,IAAI,EAAnB,UAAU,UAAK,IAAI,EAAhD,4BAAyC,CAAO,CAAC;QACvD,OAAO,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,UAA6B;;QAC/C,MAAM,EAAE,QAAQ,EAAE,YAAY,KAAuB,UAAU,EAA5B,aAAa,UAAK,UAAU,EAAzD,4BAA4C,CAAa,CAAC;QAChE,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,IAAI,oBACG,aAAa,CACnB;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,MAAM,wBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;YACrC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,UAAU;IACV,kBAAkB;CACrB,CAAC"} \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts b/dist/repositories/userRepository.d.ts deleted file mode 100644 index 82d71f6b..00000000 --- a/dist/repositories/userRepository.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { UserType } from "./../libs/interfaces"; -declare function findById(id: number): Promise<{ - password: string; - refreshToken: string | null; - id: number; - email: string; - nickname: string; - image: string | null; - createdAt: Date; - updatedAt: Date; -} | null>; -declare function findByEmail(email: string): Promise<{ - password: string; - refreshToken: string | null; - id: number; - email: string; - nickname: string; - image: string | null; - createdAt: Date; - updatedAt: Date; -} | null>; -declare function save(user: UserType): Promise<{ - password: string; - refreshToken: string | null; - id: number; - email: string; - nickname: string; - image: string | null; - createdAt: Date; - updatedAt: Date; -}>; -declare function update(id: number, data: Partial): Promise<{ - password: string; - refreshToken: string | null; - id: number; - email: string; - nickname: string; - image: string | null; - createdAt: Date; - updatedAt: Date; -}>; -declare const _default: { - findById: typeof findById; - findByEmail: typeof findByEmail; - save: typeof save; - update: typeof update; -}; -export default _default; -//# sourceMappingURL=userRepository.d.ts.map \ No newline at end of file diff --git a/dist/repositories/userRepository.d.ts.map b/dist/repositories/userRepository.d.ts.map deleted file mode 100644 index ec82ac69..00000000 --- a/dist/repositories/userRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,iBAAe,QAAQ,CAAC,EAAE,EAAE,MAAM;;;;;;;;;UAMjC;AAED,iBAAe,WAAW,CAAC,KAAK,EAAE,MAAM;;;;;;;;;UAMvC;AAED,iBAAe,IAAI,CAAC,IAAI,EAAE,QAAQ;;;;;;;;;GAQjC;AAED,iBAAe,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;GAWxD;;;;;;;AAED,wBAKE"} \ No newline at end of file diff --git a/dist/repositories/userRepository.js b/dist/repositories/userRepository.js deleted file mode 100644 index 498af72e..00000000 --- a/dist/repositories/userRepository.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("../libs/constants"); -function findById(id) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.user.findUnique({ - where: { - id, - }, - }); - }); -} -function findByEmail(email) { - return __awaiter(this, void 0, void 0, function* () { - return yield constants_1.prismaClient.user.findUnique({ - where: { - email, - }, - }); - }); -} -; -function save(user) { - return __awaiter(this, void 0, void 0, function* () { - return constants_1.prismaClient.user.create({ - data: { - email: user.email, - nickname: user.nickname, - password: user.password, - }, - }); - }); -} -function update(id, data) { - return __awaiter(this, void 0, void 0, function* () { - const { createdAt, updatedAt, articleLikes, productLikes } = data, Newdata = __rest(data, ["createdAt", "updatedAt", "articleLikes", "productLikes"]); - return constants_1.prismaClient.user.update({ - where: { - id, - }, - data: Object.assign({}, Newdata), - }); - }); -} -exports.default = { - findById, - findByEmail, - save, - update -}; -//# sourceMappingURL=userRepository.js.map \ No newline at end of file diff --git a/dist/repositories/userRepository.js.map b/dist/repositories/userRepository.js.map deleted file mode 100644 index 1ede5f90..00000000 --- a/dist/repositories/userRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userRepository.js","sourceRoot":"","sources":["../../src/repositories/userRepository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAiD;AAGjD,SAAe,QAAQ,CAAC,EAAU;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,KAAK,EAAE;gBACH,EAAE;aACL;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,WAAW,CAAC,KAAa;;QACpC,OAAO,MAAM,wBAAY,CAAC,IAAI,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE;gBACH,KAAK;aACR;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAAA,CAAC;AAEF,SAAe,IAAI,CAAC,IAAc;;QAC9B,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,IAAI,EAAE;gBACF,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aAC1B;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,SAAe,MAAM,CAAC,EAAU,EAAE,IAAuB;;QACrD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,KAAiB,IAAI,EAAhB,OAAO,UAAK,IAAI,EAAvE,0DAAgE,CAAO,CAAC;QAE9E,OAAO,wBAAY,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE;gBACH,EAAE;aACL;YACD,IAAI,oBACG,OAAO,CACb;SACJ,CAAC,CAAC;IACP,CAAC;CAAA;AAED,kBAAe;IACX,QAAQ;IACR,WAAW;IACX,IAAI;IACJ,MAAM;CACT,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.d.ts b/dist/services/articleService.d.ts deleted file mode 100644 index 6b6e94e7..00000000 --- a/dist/services/articleService.d.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ArticleFindOptions, ArticlePublicData, UpdateArticleData } from './../libs/interfaces'; -declare class ArticleService { - likeArticle(userId: number, articleId: number): Promise<{ - liked: boolean; - }>; - getArticleById(articleId: number, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; - }>; - getArticles(findOptions: ArticleFindOptions, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; - }[]>; - postArticle(userFields: ArticlePublicData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; - }>; - patchArticleById(id: number, userFields: UpdateArticleData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; - }>; - deleteArticleById(id: number, userId: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - userId: number; - title: string; - content: string; - }>; -} -export declare const articleService: ArticleService; -export {}; -//# sourceMappingURL=articleService.d.ts.map \ No newline at end of file diff --git a/dist/services/articleService.d.ts.map b/dist/services/articleService.d.ts.map deleted file mode 100644 index e648b3ce..00000000 --- a/dist/services/articleService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleService.d.ts","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGhG,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;IAO3D,WAAW,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;IAGzC,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB;;;;;;;;IAO1D,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;CAQrD;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/articleService.js b/dist/services/articleService.js deleted file mode 100644 index d1c21267..00000000 --- a/dist/services/articleService.js +++ /dev/null @@ -1,83 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.articleService = void 0; -const articleLikeRepository_1 = __importDefault(require("../repositories/articleLikeRepository")); -const articleRepository_1 = __importDefault(require("../repositories/articleRepository")); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -class ArticleService { - likeArticle(userId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - const existingLike = yield articleLikeRepository_1.default.find(userId, articleId); - if (existingLike) { - yield articleLikeRepository_1.default.remove(existingLike.id); - return { liked: false }; - } - yield articleLikeRepository_1.default.create(userId, articleId); - return { liked: true }; - }); - } - getArticleById(articleId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const article = yield articleRepository_1.default.findById(articleId, userId); - const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); - }); - } - getArticles(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const articles = yield articleRepository_1.default.findAll(findOptions, userId); - return articles.map((article) => { - const { articleLikes } = article, rest = __rest(article, ["articleLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: articleLikes.length > 0 }); - }); - }); - } - postArticle(userFields) { - return __awaiter(this, void 0, void 0, function* () { - return yield articleRepository_1.default.create(userFields); - }); - } - patchArticleById(id, userFields) { - return __awaiter(this, void 0, void 0, function* () { - const article = yield articleRepository_1.default.findById(id); - if (article.userId !== userFields.userId) { - throw new errorHandler_1.CustomError(403, "권한이 없습니다."); - } - return yield articleRepository_1.default.update(id, userFields); - }); - } - deleteArticleById(id, userId) { - return __awaiter(this, void 0, void 0, function* () { - const article = yield articleRepository_1.default.findById(id); - if (article.userId !== userId) { - throw new errorHandler_1.CustomError(403, "권한이 없습니다."); - } - return yield articleRepository_1.default.ondelete(id); - }); - } -} -exports.articleService = new ArticleService(); -//# sourceMappingURL=articleService.js.map \ No newline at end of file diff --git a/dist/services/articleService.js.map b/dist/services/articleService.js.map deleted file mode 100644 index e3ccd723..00000000 --- a/dist/services/articleService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"articleService.js","sourceRoot":"","sources":["../../src/services/articleService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,kGAA0E;AAC1E,0FAAkE;AAElE,+DAA2D;AAE3D,MAAM,cAAc;IACV,WAAW,CAAC,MAAc,EAAE,SAAiB;;YAC/C,MAAM,YAAY,GAAG,MAAM,+BAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,+BAAqB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;YAED,MAAM,+BAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,cAAc,CAAC,SAAiB,EAAE,MAAc;;YAClD,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;YAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;QACzD,CAAC;KAAA;IAEK,WAAW,CAAC,WAA+B,EAAE,MAAc;;YAC7D,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACtE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC5B,MAAM,EAAE,YAAY,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAAnC,gBAAyB,CAAU,CAAC;gBAC1C,uCAAY,IAAI,KAAE,OAAO,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,IAAG;YACzD,CAAC,CAAC,CAAC;QACP,CAAC;KAAA;IACK,WAAW,CAAC,UAA6B;;YAC3C,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtD,CAAC;KAAA;IACK,gBAAgB,CAAC,EAAU,EAAE,UAA6B;;YAC5D,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrD,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;gBACvC,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC1D,CAAC;KAAA;IACK,iBAAiB,CAAC,EAAU,EAAE,MAAc;;YAC9C,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC5C,CAAC;YAED,OAAO,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/commentService.d.ts b/dist/services/commentService.d.ts deleted file mode 100644 index d169e641..00000000 --- a/dist/services/commentService.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -declare class CommentService { - getComments(take: number, cursor: number | undefined, productId: number | undefined, articleId: number | undefined): Promise<{ - comments: { - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; - }[]; - nextCursor: number | null; - }>; - getCommentById(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; - }>; - createComment(userId: number, content: string, productId?: number, articleId?: number): Promise<{ - comment: { - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; - }; - notification: { - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; - } | null; - }>; - updateComment(id: number, content: string): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; - }>; - deleteComment(id: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - productId: number | null; - articleId: number | null; - }>; -} -export declare const commentService: CommentService; -export {}; -//# sourceMappingURL=commentService.d.ts.map \ No newline at end of file diff --git a/dist/services/commentService.d.ts.map b/dist/services/commentService.d.ts.map deleted file mode 100644 index 554d0e1f..00000000 --- a/dist/services/commentService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentService.d.ts","sourceRoot":"","sources":["../../src/services/commentService.ts"],"names":[],"mappings":"AAGA,cAAM,cAAc;IACV,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;;;;;;;;;;;IAUlH,cAAc,CAAC,EAAE,EAAE,MAAM;;;;;;;;IAIzB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;IA8CrF,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;IAQzC,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;;CAGjC;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/commentService.js b/dist/services/commentService.js deleted file mode 100644 index aa1e6bd2..00000000 --- a/dist/services/commentService.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commentService = void 0; -const commentRepository_1 = __importDefault(require("../repositories/commentRepository")); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -class CommentService { - getComments(take, cursor, productId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - const comments = yield commentRepository_1.default.findAll(take, cursor, productId, articleId); - let nextCursor = null; - if (comments.length > 0 && comments.length === take) { - nextCursor = comments[comments.length - 1].id; - } - return { comments, nextCursor }; - }); - } - getCommentById(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield commentRepository_1.default.findById(id); - }); - } - createComment(userId, content, productId, articleId) { - return __awaiter(this, void 0, void 0, function* () { - if ((productId && articleId) || (!productId && !articleId)) { - throw new errorHandler_1.CustomError(400, 'Comment must belong to EITHER a Product OR an Article.'); - } - if (!content || content.trim() === '') { - throw new errorHandler_1.CustomError(400, 'Content cannot be empty.'); - } - // 2. 댓글 생성 (DB 저장) - const comment = yield commentRepository_1.default.create(content, userId, productId, articleId); - // 3. 알림 로직 시작 - let notification = null; - let ownerId; - let targetName = ""; - // 상품 댓글인 경우 - if (productId) { - const product = yield commentRepository_1.default.findProductOwner(productId); - if (product) { - ownerId = product.userId; // 상품 주인 ID - targetName = product.name; - } - } - // 아티클 댓글인 경우 - else if (articleId) { - const article = yield commentRepository_1.default.findArticleOwner(articleId); - if (article) { - ownerId = article.userId; // 아티클 주인 ID - targetName = article.title; - } - } - // 4. 작성자가 본인이 아닐 경우에만 알림 생성 - // (ownerId가 존재하고, 댓글 쓴 사람(userId)과 다를 때) - if (ownerId && ownerId !== userId) { - const message = `작성하신 글 '${targetName}'에 새로운 댓글이 달렸습니다: ${content}`; - // DB에 알림 저장 - notification = yield commentRepository_1.default.createNotification(ownerId, message); - } - // 5. 댓글 정보와 알림 정보(있으면)를 함께 반환 - return { comment, notification }; - }); - } - updateComment(id, content) { - return __awaiter(this, void 0, void 0, function* () { - if (content !== undefined && content.trim() === '') { - throw new errorHandler_1.CustomError(400, 'Content cannot be empty.'); - } - return yield commentRepository_1.default.update(id, content); - }); - } - deleteComment(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield commentRepository_1.default.delete(id); - }); - } -} -exports.commentService = new CommentService(); -//# sourceMappingURL=commentService.js.map \ No newline at end of file diff --git a/dist/services/commentService.js.map b/dist/services/commentService.js.map deleted file mode 100644 index 7bbc664d..00000000 --- a/dist/services/commentService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commentService.js","sourceRoot":"","sources":["../../src/services/commentService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,0FAAkE;AAClE,+DAA2D;AAE3D,MAAM,cAAc;IACV,WAAW,CAAC,IAAY,EAAE,MAA0B,EAAE,SAA6B,EAAE,SAA6B;;YACpH,MAAM,QAAQ,GAAG,MAAM,2BAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YACrF,IAAI,UAAU,GAAG,IAAI,CAAC;YACtB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;gBAClD,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,EAAE,CAAC;YACnD,CAAC;YAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QACpC,CAAC;KAAA;IAEK,cAAc,CAAC,EAAU;;YAC3B,OAAO,MAAM,2BAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;KAAA;IAEK,aAAa,CAAC,MAAc,EAAE,OAAe,EAAE,SAAkB,EAAE,SAAkB;;YACvF,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,wDAAwD,CAAC,CAAC;YACzF,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACpC,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC;YAC3D,CAAC;YAED,mBAAmB;YACnB,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YAEtF,cAAc;YACd,IAAI,YAAY,GAAG,IAAI,CAAC;YACxB,IAAI,OAA2B,CAAC;YAChC,IAAI,UAAU,GAAG,EAAE,CAAC;YAEpB,YAAY;YACZ,IAAI,SAAS,EAAE,CAAC;gBACZ,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;gBACpE,IAAI,OAAO,EAAE,CAAC;oBACV,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW;oBACrC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;gBAC9B,CAAC;YACL,CAAC;YACD,aAAa;iBACR,IAAI,SAAS,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,MAAM,2BAAiB,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;gBACpE,IAAI,OAAO,EAAE,CAAC;oBACV,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY;oBACtC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC;gBAC/B,CAAC;YACL,CAAC;YAED,4BAA4B;YAC5B,yCAAyC;YACzC,IAAI,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;gBAChC,MAAM,OAAO,GAAG,WAAW,UAAU,qBAAqB,OAAO,EAAE,CAAC;gBAEpE,YAAY;gBACZ,YAAY,GAAG,MAAM,2BAAiB,CAAC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAChF,CAAC;YAED,8BAA8B;YAC9B,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QACrC,CAAC;KAAA;IAEK,aAAa,CAAC,EAAU,EAAE,OAAe;;YAC3C,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACjD,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC;YAC3D,CAAC;YAED,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;KAAA;IAEK,aAAa,CAAC,EAAU;;YAC1B,OAAO,MAAM,2BAAiB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9C,CAAC;KAAA;CACJ;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/notificationService.d.ts b/dist/services/notificationService.d.ts deleted file mode 100644 index 3b260f15..00000000 --- a/dist/services/notificationService.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -declare class NotificationService { - getNotifications(userId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; - }[]>; - getUnreadCount(userId: number): Promise<{ - count: number; - }>; - readNotification(userId: number, notificationId: number): Promise<{ - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; - }>; -} -export declare const notificationService: NotificationService; -export {}; -//# sourceMappingURL=notificationService.d.ts.map \ No newline at end of file diff --git a/dist/services/notificationService.d.ts.map b/dist/services/notificationService.d.ts.map deleted file mode 100644 index df8954bc..00000000 --- a/dist/services/notificationService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationService.d.ts","sourceRoot":"","sources":["../../src/services/notificationService.ts"],"names":[],"mappings":"AAGA,cAAM,mBAAmB;IACf,gBAAgB,CAAC,MAAM,EAAE,MAAM;;;;;;;IAI/B,cAAc,CAAC,MAAM,EAAE,MAAM;;;IAK7B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;;;;;;;CAiBhE;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"} \ No newline at end of file diff --git a/dist/services/notificationService.js b/dist/services/notificationService.js deleted file mode 100644 index 018f4c83..00000000 --- a/dist/services/notificationService.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.notificationService = void 0; -const notificationRepository_1 = __importDefault(require("../repositories/notificationRepository")); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -class NotificationService { - getNotifications(userId) { - return __awaiter(this, void 0, void 0, function* () { - return yield notificationRepository_1.default.findAll(userId); - }); - } - getUnreadCount(userId) { - return __awaiter(this, void 0, void 0, function* () { - const count = yield notificationRepository_1.default.countUnread(userId); - return { count }; - }); - } - readNotification(userId, notificationId) { - return __awaiter(this, void 0, void 0, function* () { - const notification = yield notificationRepository_1.default.findById(notificationId); - if (!notification) { - throw new errorHandler_1.CustomError(404, 'Notification not found'); - } - if (notification.userId !== userId) { - throw new errorHandler_1.CustomError(403, 'Forbidden: Not your notification'); - } - if (notification.isRead) { - return notification; - } - return yield notificationRepository_1.default.markAsRead(notificationId); - }); - } -} -exports.notificationService = new NotificationService(); -//# sourceMappingURL=notificationService.js.map \ No newline at end of file diff --git a/dist/services/notificationService.js.map b/dist/services/notificationService.js.map deleted file mode 100644 index 7381a48a..00000000 --- a/dist/services/notificationService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notificationService.js","sourceRoot":"","sources":["../../src/services/notificationService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,oGAA4E;AAC5E,+DAA2D;AAE3D,MAAM,mBAAmB;IACf,gBAAgB,CAAC,MAAc;;YACjC,OAAO,MAAM,gCAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;KAAA;IAEK,cAAc,CAAC,MAAc;;YAC/B,MAAM,KAAK,GAAG,MAAM,gCAAsB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC/D,OAAO,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAEK,gBAAgB,CAAC,MAAc,EAAE,cAAsB;;YACzD,MAAM,YAAY,GAAG,MAAM,gCAAsB,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;YAE3E,IAAI,CAAC,YAAY,EAAE,CAAC;gBAChB,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;YACzD,CAAC;YAED,IAAI,YAAY,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACjC,MAAM,IAAI,0BAAW,CAAC,GAAG,EAAE,kCAAkC,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;gBACtB,OAAO,YAAY,CAAC;YACxB,CAAC;YAED,OAAO,MAAM,gCAAsB,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QACnE,CAAC;KAAA;CACJ;AAEY,QAAA,mBAAmB,GAAG,IAAI,mBAAmB,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.d.ts b/dist/services/productService.d.ts deleted file mode 100644 index 73aa51e1..00000000 --- a/dist/services/productService.d.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ProductFindOptions, ProductPublicData } from '../libs/interfaces'; -declare class ProductService { - likeProduct(userId: number, productId: number): Promise<{ - liked: boolean; - }>; - getProductById(productId: number, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }>; - getProducts(findOptions: ProductFindOptions, userId: number): Promise<{ - isLiked: boolean; - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }[]>; - createProducts(userFields: ProductPublicData): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }>; - updateProduct(id: number, userFields: ProductPublicData): Promise<{ - product: { - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }; - notifications: { - id: number; - createdAt: Date; - userId: number; - content: string; - isRead: boolean; - }[]; - }>; - deleteProduct(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }>; -} -export declare const productService: ProductService; -export {}; -//# sourceMappingURL=productService.d.ts.map \ No newline at end of file diff --git a/dist/services/productService.d.ts.map b/dist/services/productService.d.ts.map deleted file mode 100644 index 0135af6c..00000000 --- a/dist/services/productService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productService.d.ts","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI3E,cAAM,cAAc;IACV,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;IAY7C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;IAMhD,WAAW,CAAC,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;IAO3D,cAAc,CAAC,UAAU,EAAE,iBAAiB;;;;;;;;;;IAI5C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;IAuBvD,aAAa,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;CAIjC;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/services/productService.js b/dist/services/productService.js deleted file mode 100644 index 86408470..00000000 --- a/dist/services/productService.js +++ /dev/null @@ -1,91 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.productService = void 0; -const productRepository_1 = __importDefault(require("../repositories/productRepository")); -const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); -class ProductService { - likeProduct(userId, productId) { - return __awaiter(this, void 0, void 0, function* () { - const existingLike = yield productLikeRepository_1.default.find(userId, productId); - if (existingLike) { - yield productLikeRepository_1.default.remove(existingLike.id); - return { liked: false }; - } - else { - yield productLikeRepository_1.default.create(userId, productId); - return { liked: true }; - } - }); - } - getProductById(productId, userId) { - return __awaiter(this, void 0, void 0, function* () { - const product = yield productRepository_1.default.findById(productId, userId); - const { productLikes } = product, rest = __rest(product, ["productLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); - }); - } - getProducts(findOptions, userId) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productRepository_1.default.findAll(findOptions, userId); - return products.map((product) => { - const { productLikes } = product, rest = __rest(product, ["productLikes"]); - return Object.assign(Object.assign({}, rest), { isLiked: productLikes.length > 0 }); - }); - }); - } - createProducts(userFields) { - return __awaiter(this, void 0, void 0, function* () { - const product = yield productRepository_1.default.create(userFields); - return product; - }); - } - updateProduct(id, userFields) { - return __awaiter(this, void 0, void 0, function* () { - const oldProduct = yield productRepository_1.default.findByIdSimple(id); - const updatedProduct = yield productRepository_1.default.update(id, userFields); - // 3. 알림 로직: 가격이 존재하고, 이전 가격과 다를 때 - let notifications = []; // 컨트롤러로 보낼 알림 목록 - if (oldProduct && userFields.price !== undefined && oldProduct.price !== userFields.price) { - // 3-1. 찜한 유저들 찾기 - const likers = yield productRepository_1.default.findLikers(id); - // 3-2. 각 유저에게 알림 DB 저장 (Promise.all로 병렬 처리) - notifications = yield Promise.all(likers.map((liker) => __awaiter(this, void 0, void 0, function* () { - const message = `찜한 상품 '${updatedProduct.name}'의 가격이 변경되었습니다. (${oldProduct.price}원 -> ${updatedProduct.price}원)`; - return yield productRepository_1.default.createNotification(liker.userId, message); - }))); - } - // 4. 결과 반환 (기존에는 product만 줬지만, 이제 알림 목록도 같이 줌) - return { product: updatedProduct, notifications }; - }); - } - deleteProduct(id) { - return __awaiter(this, void 0, void 0, function* () { - return yield productRepository_1.default.ondelete(id); - }); - } -} -exports.productService = new ProductService(); -//# sourceMappingURL=productService.js.map \ No newline at end of file diff --git a/dist/services/productService.js.map b/dist/services/productService.js.map deleted file mode 100644 index ca70692a..00000000 --- a/dist/services/productService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"productService.js","sourceRoot":"","sources":["../../src/services/productService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/services/userService.d.ts b/dist/services/userService.d.ts deleted file mode 100644 index bbb78c22..00000000 --- a/dist/services/userService.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { UserType, UpdateUserPasswordType, UserPublicData } from "./../libs/interfaces"; -declare class UserService { - createUser(user: UserType): Promise; - filterSensitivceUserData(user: UserType): UserPublicData; - getUser(email: string, password: string): Promise; - getUserById(id: number): Promise; - updateProfile(id: number, data: Partial): Promise; - updatePassword(id: number, data: UpdateUserPasswordType): Promise; - getProductsByUserId(id: number): Promise<{ - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }[]>; - getLikedProductsByUserId(id: number): Promise<({ - product: { - id: number; - createdAt: Date; - updatedAt: Date; - name: string; - userId: number; - description: string | null; - price: number; - tags: string[]; - }; - } & { - id: number; - createdAt: Date; - userId: number; - productId: number; - })[]>; - updateUser(id: number, data: Partial): Promise<{ - password: string; - refreshToken: string | null; - id: number; - email: string; - nickname: string; - image: string | null; - createdAt: Date; - updatedAt: Date; - }>; - verifyPassword(inputPassword: string, savedPassword: string): Promise; - createToken(user: UserPublicData | UserType, type?: string): string; - refreshToken(userId: number, refreshToken: string): Promise<{ - accessToken: string; - newRefreshToken: string; - }>; -} -export declare const userService: UserService; -export {}; -//# sourceMappingURL=userService.d.ts.map \ No newline at end of file diff --git a/dist/services/userService.d.ts.map b/dist/services/userService.d.ts.map deleted file mode 100644 index a30eb9f9..00000000 --- a/dist/services/userService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userService.d.ts","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQxF,cAAM,WAAW;IACP,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAezD,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc;IAKlD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IASjE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAQhD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAS3E,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC;IAiBjF,mBAAmB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;IAK9B,wBAAwB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;IAG9C,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIjE,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM;IAUpD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;CAS1D;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"} \ No newline at end of file diff --git a/dist/services/userService.js b/dist/services/userService.js deleted file mode 100644 index f9f6bf0c..00000000 --- a/dist/services/userService.js +++ /dev/null @@ -1,151 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.userService = void 0; -const superstruct_1 = require("superstruct"); -const errorHandler_1 = require("../libs/Handler/errorHandler"); -const userRepository_1 = __importDefault(require("../repositories/userRepository")); -const productRepository_1 = __importDefault(require("../repositories/productRepository")); -const productLikeRepository_1 = __importDefault(require("../repositories/productLikeRepository")); -const bcrypt_1 = __importDefault(require("bcrypt")); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const userStructs_1 = require("../structs/userStructs"); -function hashingPassword(password) { - return __awaiter(this, void 0, void 0, function* () { - // 함수 추가 - return bcrypt_1.default.hash(password, 12); - }); -} -class UserService { - createUser(user) { - return __awaiter(this, void 0, void 0, function* () { - const existedUser = yield userRepository_1.default.findByEmail(user.email); - if (existedUser) { - throw new errorHandler_1.CustomError(422, 'User already exists', { - email: user.email, - }); - } - const hashedPassword = yield hashingPassword(user.password); - const createdUser = yield userRepository_1.default.save(Object.assign(Object.assign({}, user), { password: hashedPassword })); - return this.filterSensitivceUserData(createdUser); - }); - } - filterSensitivceUserData(user) { - const { password, refreshToken } = user, rest = __rest(user, ["password", "refreshToken"]); - return rest; - } - getUser(email, password) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findByEmail(email); - if (!user) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - yield this.verifyPassword(password, user.password); - return this.filterSensitivceUserData(user); - }); - } - getUserById(id) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findById(id); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - return this.filterSensitivceUserData(user); - }); - } - updateProfile(id, data) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(data, userStructs_1.PatchUser); - const user = yield userRepository_1.default.update(id, data); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - return this.filterSensitivceUserData(user); - }); - } - updatePassword(id, data) { - return __awaiter(this, void 0, void 0, function* () { - (0, superstruct_1.assert)(data, userStructs_1.ChangePassword); - const { currentPassword, newPassword, confirmNewPassword } = data; - if (newPassword !== confirmNewPassword) { - throw new errorHandler_1.CustomError(400, "Passwords don't match"); - } - const user = yield userRepository_1.default.findById(id); - if (!user) { - throw new errorHandler_1.CustomError(404, 'User not found'); - } - yield this.verifyPassword(currentPassword, user.password); - const hashedPassword = yield hashingPassword(newPassword); - const updatedUser = yield userRepository_1.default.update(id, { password: hashedPassword }); - return this.filterSensitivceUserData(updatedUser); - }); - } - getProductsByUserId(id) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productRepository_1.default.findByUserId(id); - return products; - }); - } - getLikedProductsByUserId(id) { - return __awaiter(this, void 0, void 0, function* () { - const products = yield productLikeRepository_1.default.findLikedProductsByUserId(id); - return products; - }); - } - updateUser(id, data) { - return __awaiter(this, void 0, void 0, function* () { - return yield userRepository_1.default.update(id, data); - }); - } - verifyPassword(inputPassword, savedPassword) { - return __awaiter(this, void 0, void 0, function* () { - const isValid = yield bcrypt_1.default.compare(inputPassword, savedPassword); - if (!isValid) - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - }); - } - createToken(user, type) { - const JWTsecretKey = process.env.JWT_SECRET; - if (!JWTsecretKey) - throw new errorHandler_1.CustomError(500, 'JWT_SECRET is not defined'); - const payload = { userId: user.id }; - const options = { - expiresIn: type === 'refresh' ? '1d' : '10m', - }; - return jsonwebtoken_1.default.sign(payload, JWTsecretKey, options); - } - refreshToken(userId, refreshToken) { - return __awaiter(this, void 0, void 0, function* () { - const user = yield userRepository_1.default.findById(userId); - if (!user || user.refreshToken !== refreshToken) { - throw new errorHandler_1.CustomError(401, 'Unauthorized'); - } - const accessToken = this.createToken(user); // 변경 - const newRefreshToken = this.createToken(user, 'refresh'); // 추가 - return { accessToken, newRefreshToken }; // 변경 - }); - } -} -exports.userService = new UserService(); -//# sourceMappingURL=userService.js.map \ No newline at end of file diff --git a/dist/services/userService.js.map b/dist/services/userService.js.map deleted file mode 100644 index 341834c2..00000000 --- a/dist/services/userService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userService.js","sourceRoot":"","sources":["../../src/services/userService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/structs/structs.d.ts b/dist/structs/structs.d.ts deleted file mode 100644 index abb29fad..00000000 --- a/dist/structs/structs.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as s from "superstruct"; -export declare const CreateProduct: s.Struct<{ - name: string; - description: string; - price: number; - tags: string[]; -}, { - name: s.Struct; - description: s.Struct; - price: s.Struct; - tags: s.Struct>; -}>; -export declare const CreateArticle: s.Struct<{ - title: string; - content: string; -}, { - title: s.Struct; - content: s.Struct; -}>; -export declare const CreateComment: s.Struct<{ - content: string; - productId?: number | undefined; - articleId?: number | undefined; -}, { - content: s.Struct; - productId: s.Struct; - articleId: s.Struct; -}>; -export declare const PatchProduct: s.Struct<{ - name?: string | undefined; - description?: string | undefined; - price?: number | undefined; - tags?: string[] | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - name: s.Struct; - description: s.Struct; - price: s.Struct; - tags: s.Struct>; -}>>; -export declare const PatchArticle: s.Struct<{ - title?: string | undefined; - content?: string | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - title: s.Struct; - content: s.Struct; -}>>; -export declare const PatchComment: s.Struct<{ - content?: string | undefined; -}, { - content: s.Struct; -}>; -//# sourceMappingURL=structs.d.ts.map \ No newline at end of file diff --git a/dist/structs/structs.d.ts.map b/dist/structs/structs.d.ts.map deleted file mode 100644 index 40033255..00000000 --- a/dist/structs/structs.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"structs.d.ts","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAGjC,eAAO,MAAM,aAAa;;;;;;;;;;EAKxB,CAAC;AAGH,eAAO,MAAM,aAAa;;;;;;EAGxB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;EAepB,CAAC;AAEP,eAAO,MAAM,YAAY;;;;;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;;;GAA2B,CAAC;AACrD,eAAO,MAAM,YAAY;;;;EAAiD,CAAC"} \ No newline at end of file diff --git a/dist/structs/structs.js b/dist/structs/structs.js deleted file mode 100644 index 2f15113f..00000000 --- a/dist/structs/structs.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PatchComment = exports.PatchArticle = exports.PatchProduct = exports.CreateComment = exports.CreateArticle = exports.CreateProduct = void 0; -const s = __importStar(require("superstruct")); -exports.CreateProduct = s.object({ - name: s.size(s.string(), 1, 100), - description: s.string(), - price: s.min(s.number(), 0), - tags: s.array(s.string()), -}); -exports.CreateArticle = s.object({ - title: s.size(s.string(), 1, 100), - content: s.string(), -}); -exports.CreateComment = s.refine(s.object({ - content: s.string(), - productId: s.optional(s.number()), - articleId: s.optional(s.number()), -}), 'EitherProductIdOrArticleId', //검증 규칙 이름 -(value) => { - const { productId, articleId } = value; - // 실패 케이스: (둘 다 있거나) OR (둘 다 없거나) - if ((productId && articleId) || (!productId && !articleId)) { - return 'Comment must have *either* a productId *or* an articleId, but not both.'; - } - // 성공 케이스: 둘 중 하나만 존재함 - return true; -}); -exports.PatchProduct = s.partial(exports.CreateProduct); -exports.PatchArticle = s.partial(exports.CreateArticle); -exports.PatchComment = s.object({ content: s.optional(s.string()), }); -//# sourceMappingURL=structs.js.map \ No newline at end of file diff --git a/dist/structs/structs.js.map b/dist/structs/structs.js.map deleted file mode 100644 index ad9a7c5d..00000000 --- a/dist/structs/structs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"structs.js","sourceRoot":"","sources":["../../src/structs/structs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AAGpB,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAGU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAEU,QAAA,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACpC,CAAC,EACE,4BAA4B,EAAC,UAAU;AACvC,CAAC,KAAK,EAAE,EAAE;IACN,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACvC,iCAAiC;IACjC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,yEAAyE,CAAC;IACrF,CAAC;IAED,sBAAsB;IACtB,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEM,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAa,CAAC,CAAC;AACxC,QAAA,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts b/dist/structs/userStructs.d.ts deleted file mode 100644 index 6c0e51d3..00000000 --- a/dist/structs/userStructs.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as s from "superstruct"; -export declare const CreateUser: s.Struct<{ - password: string; - email: string; - nickname: string; - image?: string | undefined; -}, { - email: s.Struct; - nickname: s.Struct; - image: s.Struct; - password: s.Struct; -}>; -export declare const PatchUser: s.Struct<{ - email?: string | undefined; - nickname?: string | undefined; - image?: string | undefined; -}, import("superstruct/dist/utils").PartialObjectSchema<{ - email: s.Struct; - nickname: s.Struct; - image: s.Struct; -}>>; -export declare const ChangePassword: s.Struct<{ - currentPassword: string; - newPassword: string; - confirmNewPassword: string; -}, { - currentPassword: s.Struct; - newPassword: s.Struct; - confirmNewPassword: s.Struct; -}>; -//# sourceMappingURL=userStructs.d.ts.map \ No newline at end of file diff --git a/dist/structs/userStructs.d.ts.map b/dist/structs/userStructs.d.ts.map deleted file mode 100644 index c3a5c9b7..00000000 --- a/dist/structs/userStructs.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userStructs.d.ts","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,aAAa,CAAC;AAKjC,eAAO,MAAM,UAAU;;;;;;;;;;EAKrB,CAAC;AAGH,eAAO,MAAM,SAAS;;;;;;;;GAInB,CAAC;AAEJ,eAAO,MAAM,cAAc;;;;;;;;EAIzB,CAAC"} \ No newline at end of file diff --git a/dist/structs/userStructs.js b/dist/structs/userStructs.js deleted file mode 100644 index bde1f424..00000000 --- a/dist/structs/userStructs.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ChangePassword = exports.PatchUser = exports.CreateUser = void 0; -const s = __importStar(require("superstruct")); -const is_email_1 = __importDefault(require("is-email")); -const email = s.refine(s.string(), 'is_email', (v) => (0, is_email_1.default)(v)); -exports.CreateUser = s.object({ - email: email, - nickname: s.string(), - image: s.optional(s.string()), - password: s.string(), -}); -exports.PatchUser = s.partial(s.object({ - email: email, - nickname: s.string(), - image: s.optional(s.string()), -})); -exports.ChangePassword = s.object({ - currentPassword: s.string(), - newPassword: s.string(), - confirmNewPassword: s.string(), -}); -//# sourceMappingURL=userStructs.js.map \ No newline at end of file diff --git a/dist/structs/userStructs.js.map b/dist/structs/userStructs.js.map deleted file mode 100644 index 662473fb..00000000 --- a/dist/structs/userStructs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userStructs.js","sourceRoot":"","sources":["../../src/structs/userStructs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,wDAA+B;AAE/B,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,kBAAO,EAAC,CAAC,CAAC,CAAC,CAAC;AAErD,QAAA,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGU,QAAA,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC,CAAC;AAES,QAAA,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;IAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC"} \ No newline at end of file From 1ac28d91431adaae53e1ea10dbc11a731ff3929a Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 20:46:59 +0900 Subject: [PATCH 35/53] =?UTF-8?q?=EC=88=98=EC=A0=95=20:=20dist=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=ED=8A=B8=EB=9E=98=ED=82=B9=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 04a280c6..889c92f5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ prisma/ .env __http__/ .env.production -/dist +dist/ From bd8986e9c37213b280fb9d2e31477533426d76fb Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 20:58:09 +0900 Subject: [PATCH 36/53] =?UTF-8?q?prisma=20=ED=8F=B4=EB=8D=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - .../migrations/20251204074829/migration.sql | 95 +++++ .../migration.sql | 13 + .../migration.sql | 11 + .../migration.sql | 18 + prisma/migrations/migration_lock.toml | 3 + prisma/mock.js | 362 ++++++++++++++++++ prisma/schema.prisma | 110 ++++++ prisma/seed.js | 24 ++ 9 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 prisma/migrations/20251204074829/migration.sql create mode 100644 prisma/migrations/20260119011521_add_notification/migration.sql create mode 100644 prisma/migrations/20260119060929_addcommentinuser_id/migration.sql create mode 100644 prisma/migrations/20260119061251_add_user_on_product_n_ariticle/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/mock.js create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.js diff --git a/.gitignore b/.gitignore index 889c92f5..0b22256c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules/ -prisma/ .env __http__/ .env.production diff --git a/prisma/migrations/20251204074829/migration.sql b/prisma/migrations/20251204074829/migration.sql new file mode 100644 index 00000000..5747d6e5 --- /dev/null +++ b/prisma/migrations/20251204074829/migration.sql @@ -0,0 +1,95 @@ +-- CreateTable +CREATE TABLE "product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "price" DOUBLE PRECISION NOT NULL, + "tags" TEXT[], + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Article" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Article_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Comment" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "productId" INTEGER, + "articleId" INTEGER, + + CONSTRAINT "Comment_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "nickname" TEXT NOT NULL, + "image" TEXT, + "password" TEXT NOT NULL, + "refreshToken" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "product_likes" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "productId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "product_likes_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "article_likes" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "articleId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "article_likes_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "product_likes_userId_productId_key" ON "product_likes"("userId", "productId"); + +-- CreateIndex +CREATE UNIQUE INDEX "article_likes_userId_articleId_key" ON "article_likes"("userId", "articleId"); + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "product"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "Article"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "product_likes" ADD CONSTRAINT "product_likes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "product_likes" ADD CONSTRAINT "product_likes_productId_fkey" FOREIGN KEY ("productId") REFERENCES "product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "article_likes" ADD CONSTRAINT "article_likes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "article_likes" ADD CONSTRAINT "article_likes_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "Article"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20260119011521_add_notification/migration.sql b/prisma/migrations/20260119011521_add_notification/migration.sql new file mode 100644 index 00000000..3c5454cb --- /dev/null +++ b/prisma/migrations/20260119011521_add_notification/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "Notification" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "isRead" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "userId" INTEGER NOT NULL, + + CONSTRAINT "Notification_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20260119060929_addcommentinuser_id/migration.sql b/prisma/migrations/20260119060929_addcommentinuser_id/migration.sql new file mode 100644 index 00000000..b5f13a65 --- /dev/null +++ b/prisma/migrations/20260119060929_addcommentinuser_id/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `userId` to the `Comment` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Comment" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260119061251_add_user_on_product_n_ariticle/migration.sql b/prisma/migrations/20260119061251_add_user_on_product_n_ariticle/migration.sql new file mode 100644 index 00000000..43db422a --- /dev/null +++ b/prisma/migrations/20260119061251_add_user_on_product_n_ariticle/migration.sql @@ -0,0 +1,18 @@ +/* + Warnings: + + - Added the required column `userId` to the `Article` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `product` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Article" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "product" ADD COLUMN "userId" INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE "product" ADD CONSTRAINT "product_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Article" ADD CONSTRAINT "Article_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 00000000..044d57cd --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/mock.js b/prisma/mock.js new file mode 100644 index 00000000..6dbb36a3 --- /dev/null +++ b/prisma/mock.js @@ -0,0 +1,362 @@ +export const PRODUCTS = [ + { + id: 'f8013040-b076-4dc4-8677-11be9a17162f', + name: '랑방 샤워젤 세트', + description: '랑방의 향기로운 샤워젤 세트입니다. 피부를 부드럽게 케어하며, 향기로운 샤워 시간을 선사합니다.', + price: 38000, + tags: ['뷰티', '화장품', '바디케어', '샤워젤'], + createdAt: new Date('2023-07-14T10:00:00Z'), + updatedAt: new Date('2023-07-14T10:00:00Z'), + }, + { + id: 'd2ff3048-83bc-425a-8ad3-d6d9af1c7c6d', + name: '나이키 테크조그거팬츠', + description: '나이키의 테크조그거팬츠입니다. 운동이나 일상 생활에서 편안하게 착용할 수 있으며, 스타일과 기능을 모두 갖추고 있습니다.', + price: 75000, + tags: ['패션', '스포츠웨어', '나이키', '팬츠'], + createdAt: new Date('2023-07-14T10:30:00Z'), + updatedAt: new Date('2023-07-14T10:30:00Z'), + }, + { + id: '7f70481b-784d-4b0e-bc3e-f05eefc17951', + name: 'Apple AirPods 프로', + description: 'Apple의 AirPods 프로는 탁월한 사운드 품질과 노이즈 캔슬링 기능을 갖춘 무선 이어폰입니다. 간편한 터치 컨트롤과 긴 배터리 수명을 제공합니다.', + price: 320000, + tags: ['전자제품', '이어폰', 'Apple', '무선'], + createdAt: new Date('2023-07-14T11:00:00Z'), + updatedAt: new Date('2023-07-14T11:00:00Z'), + }, + { + id: '4e0d9424-3a16-4a5e-9725-0e9d2f9722b3', + name: '베르사체 화장품 세트', + description: '베르사체의 화장품 세트로 화려하고 특별한 분위기를 연출할 수 있습니다. 다양한 아이템으로 구성되어 있으며, 고품질 성분을 사용하여 피부에 부드럽고 안정적인 관리를 제공합니다.', + price: 65000, + tags: ['뷰티', '화장품', '럭셔리', '세트'], + createdAt: new Date('2023-07-14T11:30:00Z'), + updatedAt: new Date('2023-07-14T11:30:00Z'), + }, + { + id: '39c3fd4a-dbd5-4ab1-8e0b-58ea31b8a2d3', + name: '아이언맨 골프 클럽 세트', + description: '아이언맨 디자인으로 유명한 골프 클럽 세트입니다. 고품질 소재와 최신 기술로 제작되어 정밀한 스윙과 멋진 샷을 도와줍니다.', + price: 850000, + tags: ['스포츠', '골프', '클럽', '프리미엄'], + createdAt: new Date('2023-07-14T12:00:00Z'), + updatedAt: new Date('2023-07-14T12:00:00Z'), + }, + { + id: 'c2d4a2e3-7c2a-4f80-bff7-8ebcbacccf2c', + name: '삼성 갤럭시 S21 울트라 5G 스마트폰', + description: '삼성 갤럭시 S21 울트라 5G 스마트폰은 최신 플래그십 기술을 갖춘 뛰어난 성능을 제공합니다. 화려한 디스플레이, 고화질 카메라, 빠른 프로세서 등으로 사용자들에게 탁월한 사용자 경험을 선사합니다.', + price: 1249000, + tags: ['전자제품', '스마트폰', '삼성', '5G'], + createdAt: new Date('2023-07-14T14:00:00Z'), + updatedAt: new Date('2023-07-14T14:00:00Z'), + }, + { + id: '43c62d5b-6e66-4d1c-9f47-14d1a1970fd1', + name: 'LG 그램 14형 노트북', + description: 'LG 그램 14형 노트북은 가볍고 휴대성이 뛰어난 디자인과 우수한 성능을 제공합니다. 긴 배터리 수명과 선명한 디스플레이로 사용자들에게 편리한 사용자 경험을 선사합니다.', + price: 1399000, + tags: ['전자제품', '노트북', 'LG', '경량'], + createdAt: new Date('2023-07-14T14:30:00Z'), + updatedAt: new Date('2023-07-14T14:30:00Z'), + }, + { + id: '6e9d4e63-7246-4fc5-bf33-3e7f32fe9c02', + name: '나이키 에어맥스 270 운동화', + description: '나이키 에어맥스 270 운동화는 경량 디자인과 탁월한 고무 밑창으로 편안한 착용감과 훌륭한 트랙션을 제공합니다. 스포티한 디자인과 풍부한 컬러로 다양한 스타일에 어울립니다.', + price: 169000, + tags: ['패션', '운동화', '나이키', '스니커즈'], + createdAt: new Date('2023-07-14T15:00:00Z'), + updatedAt: new Date('2023-07-14T15:00:00Z'), + }, + { + id: '80e7b377-df80-4410-9a14-622ea3cc58e0', + name: '애플 맥북 프로 13형 노트북', + description: '애플 맥북 프로 13형 노트북은 탁월한 성능과 우수한 디스플레이 품질로 유명합니다. 가벼운 무게와 편리한 사용성으로 사용자들에게 탁월한 컴퓨팅 경험을 제공합니다.', + price: 1999000, + tags: ['전자제품', '노트북', 'Apple', '프리미엄'], + createdAt: new Date('2023-07-14T15:30:00Z'), + updatedAt: new Date('2023-07-14T15:30:00Z'), + }, + { + id: 'a4ff201c-48f7-4963-b317-2e9e4e3e43b7', + name: '랑방 매트 틴트', + description: '랑방 매트 틴트는 풍부한 컬러와 지속력을 제공하는 제품입니다. 입술에 부드럽게 발리며 오래 지속되는 매트한 마무리를 선사합니다.', + price: 35000, + tags: ['뷰티', '립메이크업', '틴트', '매트'], + createdAt: new Date('2023-07-14T16:00:00Z'), + updatedAt: new Date('2023-07-14T16:00:00Z'), + }, + { + id: 'c6a5975a-42e7-4f7f-8b7c-72714d59f44a', + name: '루이비통 클래식 지갑', + description: '루이비통의 클래식 지갑은 고품질 가죽과 세련된 디자인으로 제작되었습니다. 실용적인 구성과 품질로 오랜 기간 동안 사용할 수 있습니다.', + price: 750000, + tags: ['패션', '지갑', '럭셔리', '가죽'], + createdAt: new Date('2023-07-14T16:30:00Z'), + updatedAt: new Date('2023-07-14T16:30:00Z'), + }, + { + id: 'a33d441f-57a9-4618-8f46-07e7418ef3c9', + name: '아이폰 13 프로 맥스 스마트폰', + description: '아이폰 13 프로 맥스는 탁월한 카메라 성능과 강력한 성능으로 유명한 스마트폰입니다. 선명한 디스플레이와 편리한 사용성을 제공합니다.', + price: 1799000, + tags: ['전자제품', '스마트폰', 'Apple', '프리미엄'], + createdAt: new Date('2023-07-14T17:00:00Z'), + updatedAt: new Date('2023-07-14T17:00:00Z'), + }, + { + id: 'f6c2a70e-32a4-4bcf-b183-8a33d97fb587', + name: '헤라 UV 미스트 선스틱 SPF50+ PA+++', + description: '헤라의 UV 미스트 선스틱은 SPF50+ PA+++로 강력한 자외선 차단과 함께 피부 보습에 도움을 주는 제품입니다. 휴대하기 편리한 스틱 형태로 사용이 간편합니다.', + price: 28000, + tags: ['뷰티', '선케어', '자외선차단', 'SPF50'], + createdAt: new Date('2023-07-14T17:30:00Z'), + updatedAt: new Date('2023-07-14T17:30:00Z'), + }, + { + id: 'e700c4be-6e0c-40fd-bb47-2ab2b2e9270d', + name: '올리비아 버튼 니트', + description: '올리비아 버튼 니트는 스타일리시한 디자인과 편안한 착용감으로 유명한 제품입니다. 다양한 의상에 매치하기 좋으며, 따뜻한 니트 소재로 겨울철에도 활용할 수 있습니다.', + price: 59000, + tags: ['패션', '니트', '상의', '겨울'], + createdAt: new Date('2023-07-14T18:00:00Z'), + updatedAt: new Date('2023-07-14T18:00:00Z'), + }, + { + id: 'be13a617-8f0e-4806-82f7-149a6e12f1a7', + name: '삼성 55인치 QLED 4K 스마트 TV', + description: '삼성의 55인치 QLED 4K 스마트 TV는 고품질 화질과 풍부한 컬러 표현력으로 몰입감 있는 시청 경험을 제공합니다. 스마트 기능과 음성 인식 기능을 지원하여 편리한 사용자 경험을 선사합니다.', + price: 1999000, + tags: ['전자제품', 'TV', '4K', 'QLED'], + createdAt: new Date('2023-07-14T18:30:00Z'), + updatedAt: new Date('2023-07-14T18:30:00Z'), + }, + { + id: 'd3f74179-2cb5-4e51-8f3a-57f65c1d4e6e', + name: '루이스 카스텔리 미니 프라이팬', + description: '루이스 카스텔리의 미니 프라이팬은 작고 휴대하기 편한 사이즈로 다양한 요리에 활용할 수 있습니다. 내열성과 내마모성이 우수하며 손쉬운 청소가 가능합니다.', + price: 29000, + tags: ['주방용품', '프라이팬', '조리도구', '미니'], + createdAt: new Date('2023-07-15T10:00:00Z'), + updatedAt: new Date('2023-07-15T10:00:00Z'), + }, + { + id: 'dc74c065-5237-4d5b-86d1-d4eeb8a4d1fd', + name: '델롱기 헤어 드라이어', + description: '델롱기의 헤어 드라이어는 강력한 풍력과 다양한 모드로 빠른 건조와 스타일링을 도와줍니다. 손쉬운 조작과 휴대성을 갖추고 있습니다.', + price: 69000, + tags: ['전자제품', '헤어드라이어', '뷰티가전', '스타일링'], + createdAt: new Date('2023-07-15T10:30:00Z'), + updatedAt: new Date('2023-07-15T10:30:00Z'), + }, + { + id: '93a1c570-ee10-4961-bbdf-68001efddc49', + name: '보노홈 스탠드 믹서기', + description: '보노홈의 스탠드 믹서기는 강력한 모터와 다양한 속도 조절로 원하는 믹싱과 반죽 작업을 할 수 있습니다. 내구성이 뛰어나며 안정적인 성능을 제공합니다.', + price: 99000, + tags: ['주방용품', '믹서기', '베이킹', '주방가전'], + createdAt: new Date('2023-07-15T11:00:00Z'), + updatedAt: new Date('2023-07-15T11:00:00Z'), + }, + { + id: 'd5d60337-ccf6-404f-b615-982f2b223ab3', + name: '나이키 에어 포스 1 운동화', + description: '나이키의 에어 포스 1 운동화는 클래식한 디자인과 탁월한 편안함으로 유명합니다. 내구성이 우수하며 다양한 스타일과 컬러로 선택할 수 있습니다.', + price: 129000, + tags: ['패션', '운동화', '나이키', '클래식'], + createdAt: new Date('2023-07-15T11:30:00Z'), + updatedAt: new Date('2023-07-15T11:30:00Z'), + }, + { + id: '209b2a1c-c6e9-4461-a17a-c8a8f141f9f9', + name: '삼성 컬러 레이저 프린터', + description: '삼성의 컬러 레이저 프린터는 고품질 컬러 출력과 빠른 인쇄 속도로 탁월한 인쇄 품질을 제공합니다. 다양한 용지 크기와 고효율 인쇄 기능을 지원합니다.', + price: 499000, + tags: ['전자제품', '프린터', '레이저', '사무용품'], + createdAt: new Date('2023-07-15T12:00:00Z'), + updatedAt: new Date('2023-07-15T12:00:00Z'), + }, + { + id: 'b1782c1e-8ad2-45a7-90e6-50f61c4a8de9', + name: '코멧 피크닉 매트', + description: '코멧 피크닉 매트는 내열성과 내충격성이 뛰어나며 편리한 보관과 휴대를 위한 접이식 디자인입니다. 야외 활동에 최적화되어 있습니다.', + price: 35000, + tags: ['스포츠', '캠핑', '야외용품', '매트'], + createdAt: new Date('2023-07-15T12:30:00Z'), + updatedAt: new Date('2023-07-15T12:30:00Z'), + }, + { + id: 'b2593dbd-5ea3-4376-9378-6d6e7a17e232', + name: '루미에어 드라이 바디 수건', + description: '루미에어의 드라이 바디 수건은 퀵 드라이와 산뜻한 사용감을 제공하는 혁신적인 수건입니다. 속건성이 뛰어나며 휴대하기 편리합니다.', + price: 19900, + tags: ['뷰티', '수건', '속건', '여행용품'], + createdAt: new Date('2023-07-15T13:00:00Z'), + updatedAt: new Date('2023-07-15T13:00:00Z'), + }, + { + id: 'c28a2eaf-4d87-4f9f-ae5b-cbcf73e24253', + name: '쿠진앤에이 오믈렛 팬', + description: '쿠진앤에이의 오믈렛 팬은 오믈렛을 쉽고 빠르게 만들 수 있는 전용 팬입니다. 내열성이 뛰어나며 논스틱 처리로 편리한 사용과 청소가 가능합니다.', + price: 25000, + tags: ['주방용품', '팬', '논스틱', '조리도구'], + createdAt: new Date('2023-07-15T13:30:00Z'), + updatedAt: new Date('2023-07-15T13:30:00Z'), + }, + { + id: 'e48d9e8b-6712-48ed-9ea2-2f7e150a2e48', + name: '네스프레소 커피머신', + description: '네스프레소 커피머신은 간편한 사용과 다양한 커피 음료 제공으로 인기 있는 제품입니다. 커피 품질과 편리한 관리 기능을 동시에 갖추고 있습니다.', + price: 149000, + tags: ['전자제품', '커피머신', '네스프레소', '주방가전'], + createdAt: new Date('2023-07-15T14:00:00Z'), + updatedAt: new Date('2023-07-15T14:00:00Z'), + }, + { + id: '64e7c9fc-49b6-4b20-9ce3-11a9487ed1e1', + name: '스완스톤 운동 요가 매트', + description: '스완스톤의 운동 요가 매트는 편안하고 안정적인 기준면과 내충격성으로 안전한 운동 활동을 도와줍니다. 내마모성이 우수하며 휴대하기 편리합니다.', + price: 69000, + tags: ['스포츠', '요가', '매트', '운동용품'], + createdAt: new Date('2023-07-15T14:30:00Z'), + updatedAt: new Date('2023-07-15T14:30:00Z'), + }, + { + id: 'e3f34fb9-2c4b-45c0-94b1-610e6cfe13a7', + name: '샤넬 커버 파운데이션', + description: '샤넬의 커버 파운데이션은 자연스럽게 피부를 커버하고 매끈한 마무리를 선사합니다. 오랜 지속력과 촉촉한 사용감을 제공합니다.', + price: 65000, + tags: ['뷰티', '파운데이션', '럭셔리', '베이스메이크업'], + createdAt: new Date('2023-07-15T15:00:00Z'), + updatedAt: new Date('2023-07-15T15:00:00Z'), + }, + { + id: '4be1e6fd-2271-4f15-96e3-577c4a057c8b', + name: '올바로 10단 스텐레스 냄비세트', + description: '올바로의 10단 스텐레스 냄비세트는 내열성과 내식성이 우수한 제품입니다. 다양한 크기와 기능으로 요리를 즐길 수 있습니다.', + price: 179000, + tags: ['주방용품', '냄비세트', '스텐레스', '조리도구'], + createdAt: new Date('2023-07-15T15:30:00Z'), + updatedAt: new Date('2023-07-15T15:30:00Z'), + }, + { + id: 'a4468fc1-3cfc-4c46-9ff9-2469b5f9e88a', + name: '아디다스 운동바지', + description: '아디다스의 운동바지는 신축성과 편안한 착용감으로 활동성을 높여줍니다. 퀄리티한 소재와 다양한 스타일로 선택의 폭이 넓습니다.', + price: 59000, + tags: ['패션', '스포츠웨어', '아디다스', '바지'], + createdAt: new Date('2023-07-15T16:00:00Z'), + updatedAt: new Date('2023-07-15T16:00:00Z'), + }, + { + id: 'be07b27a-93e9-4d1a-8a54-133c0f04541e', + name: 'LG 울트라 HD 4K TV', + description: 'LG의 울트라 HD 4K TV는 생생하고 선명한 화질로 몰입감 있는 시청 경험을 제공합니다. 스마트 기능과 다양한 연결 옵션을 갖추고 있습니다.', + price: 2299000, + tags: ['전자제품', 'TV', '4K', 'LG'], + createdAt: new Date('2023-07-15T16:30:00Z'), + updatedAt: new Date('2023-07-15T16:30:00Z'), + }, + { + id: 'de573676-2a27-4cfa-85a1-0e9dd3ed5315', + name: '삼성 갤럭시 워치4', + description: '삼성의 갤럭시 워치4는 스타일리시한 디자인과 다양한 기능으로 탁월한 착용 경험을 선사합니다. 편리한 스마트 기능과 건강 관리 기능을 갖추고 있습니다.', + price: 399000, + tags: ['전자제품', '스마트워치', '삼성', '웨어러블'], + createdAt: new Date('2023-07-15T17:00:00Z'), + updatedAt: new Date('2023-07-15T17:00:00Z'), + }, + { + id: '9c6ebf9b-f666-44da-b79f-5f62517f55fc', + name: '나이스컷 고급 칼세트', + description: '나이스컷의 고급 칼세트는 품질 높은 강화 스테인리스 블레이드로 효율적인 커팅을 가능하게 합니다. 다양한 칼 종류로 다양한 요리를 할 수 있습니다.', + price: 99000, + tags: ['주방용품', '칼세트', '조리도구', '스텐레스'], + createdAt: new Date('2023-07-15T17:30:00Z'), + updatedAt: new Date('2023-07-15T17:30:00Z'), + }, + { + id: '17cc4f9c-1e71-4782-914a-c8a12dc6f94b', + name: '아벤크롬비 스니커즈', + description: '아벤크롬비의 스니커즈는 스포티한 디자인과 편안한 착용감으로 인기 있는 제품입니다. 고품질 소재와 세련된 스타일로 멋진 캐주얼 룩을 완성할 수 있습니다.', + price: 89000, + tags: ['패션', '운동화', '스니커즈', '캐주얼'], + createdAt: new Date('2023-07-15T18:00:00Z'), + updatedAt: new Date('2023-07-15T18:00:00Z'), + }, + { + id: 'c1b31e2b-6d6a-4b24-b3ea-d0746f9cc6ea', + name: '피오라 러브포이즌 향수', + description: '피오라의 러브포이즌 향수는 상쾌하고 여성스러운 향기로 매력적인 분위기를 연출합니다. 오래 지속되는 향과 우아한 디자인으로 사랑받는 제품입니다.', + price: 79000, + tags: ['뷰티', '향수', '프래그런스', '여성용'], + createdAt: new Date('2023-07-15T18:30:00Z'), + updatedAt: new Date('2023-07-15T18:30:00Z'), + }, + { + id: '5ae4a823-0076-4ae0-af49-7f3eb0002c0a', + name: '니스 퍼퓸 캔들', + description: '니스 퍼퓸의 캔들은 아로마 향으로 휴식과 힐링을 선사합니다. 품질 좋은 원료로 만들어진 캔들은 안전하고 오랜 시간 향기를 유지합니다.', + price: 35000, + tags: ['홈인테리어', '캔들', '아로마', '힐링'], + createdAt: new Date('2023-07-15T19:00:00Z'), + updatedAt: new Date('2023-07-15T19:00:00Z'), + }, +]; + + +// Article 목 데이터 +export const ARTICLES = [ + { + id: '8f3d9e1a-4b2c-4d8f-9a7e-5c6b8d2e9f1a', + title: '2024년 가을 패션 트렌드: 빈티지 무드가 돌아온다', + content: `레이어드 룩을 통해 빈티지 느낌도 살릴 수 있겠네요!`, + createdAt: new Date('2024-09-15T10:00:00Z'), + updatedAt: new Date('2024-09-15T10:00:00Z'), + }, + { + id: '7e2c8d1b-3a4f-4c9e-8b6d-4a5c7e8f9d2b', + title: '스마트 홈 기기 선택 가이드: 초보자를 위한 완벽 가이드', + content: `좋은 정보 감사합니다. 스마트 도어락과 CCTV를 고려해봐야겠네요.`, + createdAt: new Date('2024-08-22T14:30:00Z'), + updatedAt: new Date('2024-08-22T14:30:00Z'), + }, + { + id: '6d1b9c2a-5e3f-4a8d-7c6b-3d4e8f9a1c2d', + title: '홈 카페 만들기: 바리스타가 알려주는 커피 추출 비법', + content: `대박! 다음 글에서 말차라뗴 만드는 법도 알려주시나요?`, + createdAt: new Date('2024-07-10T09:15:00Z'), + updatedAt: new Date('2024-07-10T09:15:00Z'), + }, + { + id: '5c9a8d1e-4b2f-3c7d-6e5a-2d3f7e8c9a1b', + title: '피부 타입별 스킨케어 루틴: 피부과 전문의 추천', + content: `지성 피부는 과도한 유분 제거가 지양된다니.. 지금까지 매일 빡빡 닦았는데요..`, + createdAt: new Date('2024-06-18T16:45:00Z'), + updatedAt: new Date('2024-06-18T16:45:00Z'), + }, + { + id: '4b8c7d2e-3a1f-5d6c-7e9a-1c2d8f3e4a5b', + title: '주방 도구 필수템 TOP 10: 요리 초보자를 위한 가이드', + content: `요즘 좋은 주방도구 사기가 어렵더라구요ㅜ`, + createdAt: new Date('2024-05-25T11:20:00Z'), + updatedAt: new Date('2024-05-25T11:20:00Z'), + }, + { + id: '3a7b6c1d-2e9f-4c8d-5a6e-7b8c9d1e2f3a', + title: '운동 초보자를 위한 홈트레이닝 가이드', + content: `초보자 루틴 추천 감사합니다`, + createdAt: new Date('2024-04-12T08:00:00Z'), + updatedAt: new Date('2024-04-12T08:00:00Z'), + }, + { + id: '2f6e5d9c-1a8b-3c7d-4e9f-5a6b7c8d9e1f', + title: '친환경 생활용품으로 바꾸기: 지구를 위한 작은 실천', + content: `저 고체 샴푸바, 면 행주 사용하고 있어요. 불편하긴 해도 환경을 위해서라면!`, + createdAt: new Date('2024-03-08T13:30:00Z'), + updatedAt: new Date('2024-03-08T13:30:00Z'), + }, +]; diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000..7589b2b8 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,110 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Product { + id Int @id @default(autoincrement()) + name String + description String? + price Float + tags String[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + comments Comment[] + productLikes ProductLike[] + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("product") +} + +model Article { + id Int @id @default(autoincrement()) + title String + content String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + comments Comment[] + articleLikes ArticleLike[] + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("Article") +} + +model Comment { + id Int @id @default(autoincrement()) + content String + createdAt DateTime @default(now()) + product Product? @relation(fields: [productId], references: [id], onDelete: Cascade) + productId Int? + Article Article? @relation(fields: [articleId], references: [id], onDelete: Cascade) + articleId Int? + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("Comment") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + nickname String + image String? + password String + refreshToken String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + productLikes ProductLike[] + articleLikes ArticleLike[] + notification Notification[] + comments Comment[] + products Product[] + articles Article[] + + @@map("User") +} + +model ProductLike { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int + product Product @relation(fields: [productId], references: [id]) + productId Int + createdAt DateTime @default(now()) + + @@unique([userId, productId]) + @@map("product_likes") +} + +model ArticleLike { + id Int @id @default(autoincrement()) + user User @relation(fields: [userId], references: [id]) + userId Int + article Article @relation(fields: [articleId], references: [id]) + articleId Int + createdAt DateTime @default(now()) + + @@unique([userId, articleId]) + @@map("article_likes") +} + +model Notification { + id Int @id @default(autoincrement()) + content String + isRead Boolean @default(false) + createdAt DateTime @default(now()) + userId Int + user User @relation(fields: [userId], references: [id]) +} diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 00000000..31bf4ec5 --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,24 @@ +import { PrismaClient } from "@prisma/client"; +import { PRODUCTS } from './mock.js'; + +const prisma = new PrismaClient(); + +async function main() { + await prisma.product.deleteMany({}); + await prisma.product.createMany({ + data: PRODUCTS, + skipDuplicates: true, + }); + await prisma.article.deleteMany({}); + await prisma.articleproduct.createMany({ + data: ARTICLES, + skipDuplicates: true, + }); +} + +main().then(async () => { await prisma.$disconnect(); }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); \ No newline at end of file From a8c56abc29c6ebb62ec8e0727744019de7a3e1db Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Sun, 8 Feb 2026 12:28:32 +0000 Subject: [PATCH 37/53] =?UTF-8?q?infra=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/ec2/ecosystem.config.js | 11 +++++++++++ infra/ec2/nginx.conf | 13 +++++++++++++ infra/ec2/start.sh | 5 +++++ 3 files changed, 29 insertions(+) create mode 100644 infra/ec2/ecosystem.config.js create mode 100644 infra/ec2/nginx.conf create mode 100755 infra/ec2/start.sh diff --git a/infra/ec2/ecosystem.config.js b/infra/ec2/ecosystem.config.js new file mode 100644 index 00000000..2087c012 --- /dev/null +++ b/infra/ec2/ecosystem.config.js @@ -0,0 +1,11 @@ +module.exports ={ + apps: [{ + name: "yoo-pandamarket", + script: "./dist/main.js", + instances: 1, + exec_mode: "fork", + env_production:{ + NODE_ENV: "production", + } + }] +} diff --git a/infra/ec2/nginx.conf b/infra/ec2/nginx.conf new file mode 100644 index 00000000..f5e813fb --- /dev/null +++ b/infra/ec2/nginx.conf @@ -0,0 +1,13 @@ +server { + listen 80; + server_name 52.79.184.244; + + location / { + proxy_pass http://localhost:3000; # Express 서버 포트 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} diff --git a/infra/ec2/start.sh b/infra/ec2/start.sh new file mode 100755 index 00000000..9337133b --- /dev/null +++ b/infra/ec2/start.sh @@ -0,0 +1,5 @@ +cd /home/ec2-user/6-sprint-mission + +pm2 start /home/ec2-user/6-sprint-mission/infra/ec2/ecosystem.config.js --env production + +pm2 status From aa01be55892538569cb3dde21e3a21790c00ec53 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 22:09:33 +0900 Subject: [PATCH 38/53] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B0=8F=20=EC=9D=BC=EB=B6=80=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- package-lock.json | 180 +++++++++++++++++++++++++++++ package.json | 5 +- public/api-test.html | 60 ++++++++++ src/Routers/uploadRouter.ts | 22 +++- src/controller/uploadController.ts | 7 +- src/main.ts | 8 +- test/upload.test.ts | 23 ++++ 8 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 test/upload.test.ts diff --git a/.gitignore b/.gitignore index 0b22256c..12115c6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ node_modules/ .env __http__/ -.env.production dist/ +coverage/ +infra/**/*.png +infra/**/*.jpg \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bf7379dc..2a0248b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "is-email": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", + "multer-s3": "^3.0.1", "socket.io": "^4.8.3", "superstruct": "^2.0.2", "ws": "^8.19.0" @@ -32,6 +33,7 @@ "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", + "@types/multer-s3": "^3.0.3", "@types/node": "^24.10.1", "@types/socket.io": "^3.0.1", "@types/supertest": "^6.0.3", @@ -557,6 +559,27 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.985.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.985.0.tgz", + "integrity": "sha512-EnqXf2E+5+7e5zuz8mPl+3Dk/DI0ObSL8s7Tsf6H8FL/p2BJw2LhgtxPnFZUmpanpoMO0hC9/qjXNd+aGPV0/w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.8", + "@smithy/middleware-endpoint": "^4.4.13", + "@smithy/smithy-client": "^4.11.2", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.985.0" + } + }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { "version": "3.972.3", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.3.tgz", @@ -3177,6 +3200,18 @@ "@types/express": "*" } }, + "node_modules/@types/multer-s3": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/multer-s3/-/multer-s3-3.0.3.tgz", + "integrity": "sha512-VgWygI9UwyS7loLithUUi0qAMIDWdNrERS2Sb06UuPYiLzKuIFn2NgL7satyl4v8sh/LLoU7DiPanvbQaRg9Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aws-sdk/client-s3": "^3.0.0", + "@types/multer": "*", + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -3806,6 +3841,26 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -3959,6 +4014,16 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4926,6 +4991,15 @@ "node": ">= 0.6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5119,6 +5193,15 @@ "bser": "2.1.1" } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -5536,6 +5619,12 @@ "node": ">= 0.4" } }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -5590,6 +5679,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -6902,6 +7011,24 @@ "node": ">= 10.16.0" } }, + "node_modules/multer-s3": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-3.0.1.tgz", + "integrity": "sha512-BFwSO80a5EW4GJRBdUuSHblz2jhVSAze33ZbnGpcfEicoT0iRolx4kWR+AJV07THFRCQ78g+kelKFdjkCCaXeQ==", + "license": "MIT", + "dependencies": { + "@aws-sdk/lib-storage": "^3.46.0", + "file-type": "^3.3.0", + "html-comment-regex": "^1.1.2", + "run-parallel": "^1.1.6" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.0.0" + } + }, "node_modules/multer/node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7491,6 +7618,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7625,6 +7772,29 @@ "node": ">= 18" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7992,6 +8162,16 @@ "node": ">= 0.8" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", diff --git a/package.json b/package.json index e28debef..27cdda60 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "scripts": { "build": "tsc", "dev": "nodemon --watch src --exec ts-node ./src/main.ts", - "start": "node ./dist/main.js" + "start": "node ./dist/main.js", + "test": "jest" }, "dependencies": { "@aws-sdk/client-s3": "^3.985.0", @@ -21,6 +22,7 @@ "is-email": "^1.0.2", "jsonwebtoken": "^9.0.2", "multer": "^2.0.2", + "multer-s3": "^3.0.1", "socket.io": "^4.8.3", "superstruct": "^2.0.2", "ws": "^8.19.0" @@ -34,6 +36,7 @@ "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^2.0.0", + "@types/multer-s3": "^3.0.3", "@types/node": "^24.10.1", "@types/socket.io": "^3.0.1", "@types/supertest": "^6.0.3", diff --git a/public/api-test.html b/public/api-test.html index fabff80f..00246189 100644 --- a/public/api-test.html +++ b/public/api-test.html @@ -289,6 +289,25 @@

💬 Comments (댓글)

+ +
+

🖼️ Image Upload (이미지)

+
+ + +
+

Uploaded URL

+
+ + +
+
+

🔔 Notifications (알림)

@@ -479,6 +498,47 @@

📡 Logs & Response

req("/comments", "POST", body); }; + // Image Upload + async function uploadImage() { + const fileInput = document.getElementById("upload-file"); + const file = fileInput.files[0]; + if (!file) { + log("No file selected", "error", "Upload"); + return; + } + + const formData = new FormData(); + formData.append("attachment", file); // 서버 라우터 설정(upload.single('attachment'))과 일치해야 함 + + const token = document.getElementById("access-token").value; + const headers = {}; + if (token) headers["Authorization"] = `Bearer ${token}`; + + log(`POST /upload (${file.name})`, "info", "Request"); + + try { + const res = await fetch(API_URL + "/upload", { + method: "POST", + headers, // FormData 전송 시 Content-Type은 브라우저가 자동으로 설정함 + body: formData, + }); + const data = await res.json(); + log(data, res.ok ? "success" : "error", `Response (${res.status})`); + if (res.ok && data.url) { + document.getElementById("uploaded-url").value = data.url; + } + } catch (err) { + log(err.message, "error", "Network Error"); + } + } + + function copyUrl() { + const urlInput = document.getElementById("uploaded-url"); + urlInput.select(); + document.execCommand("copy"); + log("URL copied to clipboard", "success", "System"); + } + // Notifications const getNotifications = () => req("/notifications"); const getUnreadCount = () => req("/notifications/count"); diff --git a/src/Routers/uploadRouter.ts b/src/Routers/uploadRouter.ts index 960e8455..b7c7a304 100644 --- a/src/Routers/uploadRouter.ts +++ b/src/Routers/uploadRouter.ts @@ -1,17 +1,33 @@ import { EXPRESS } from '../libs/constants'; import { catchAsync } from '../libs/catchAsync'; import multer from 'multer'; +import { S3Client } from '@aws-sdk/client-s3'; +import multerS3 from 'multer-s3'; import { UploadSingleImage } from '../controller/uploadController'; const uploadRouter = EXPRESS.Router(); -const upload = multer({ dest: 'upload/' }); +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID as string, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string, + }, +}); + +const upload = multer({ + storage: multerS3({ + s3: s3, + bucket: process.env.AWS_S3_BUCKET_NAME as string, + key: function (req, file, cb) { + cb(null, `${Date.now()}_${file.originalname}`); + } + }) +}); uploadRouter.post('/', upload.single('attachment'), catchAsync(UploadSingleImage)); -uploadRouter.use('/', EXPRESS.static('upload')); - export default uploadRouter; diff --git a/src/controller/uploadController.ts b/src/controller/uploadController.ts index 00ddda00..48ac8203 100644 --- a/src/controller/uploadController.ts +++ b/src/controller/uploadController.ts @@ -4,7 +4,8 @@ import { CustomError } from '../libs/Handler/errorHandler'; export function UploadSingleImage(req: ExpressRequest, res: ExpressResponse) { if (!req.file) throw new CustomError(404, "file not found"); - const { filename } = req.file; - const path = `files/${filename}`; - res.json({ path }); + + // multer-s3를 사용하면 req.file 객체에 location 속성(S3 URL)이 추가됩니다. + const { location } = req.file as any; + res.status(201).json({ url: location }); } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 2e1abb90..367fb04d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -69,8 +69,10 @@ io.on('connection', (socket) => { } }); -httpServer.listen(PORT, () => { - console.log(`Server is running on port ${PORT}`); -}); +if (process.env.NODE_ENV !== 'test') { + httpServer.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); + }); +} export default app; \ No newline at end of file diff --git a/test/upload.test.ts b/test/upload.test.ts new file mode 100644 index 00000000..3439078c --- /dev/null +++ b/test/upload.test.ts @@ -0,0 +1,23 @@ +import request from 'supertest'; +import app from '../src/main'; +import path from 'path'; + +describe('Upload API Integration Tests', () => { + it('POST /upload - should upload image to S3 and return URL', async () => { + // 테스트에 사용할 이미지 파일 경로 + const filePath = path.join(__dirname, 'testimage.jpg'); + + // API 요청 (라우터 경로가 /upload 라고 가정) + const res = await request(app) + .post('/upload') + .attach('attachment', filePath); // uploadRouter에서 설정한 필드명 'attachment' + + // 검증 + expect(res.status).toBe(201); + expect(res.body).toHaveProperty('url'); + // 반환된 URL이 http 또는 https로 시작하는지 확인 (S3 URL 형식) + expect(res.body.url).toMatch(/^https?:\/\//); + + console.log('Uploaded S3 URL:', res.body.url); + }); +}); From a04db02876a4c96697dadfd4961cb931cc1b8014 Mon Sep 17 00:00:00 2001 From: YooInHak Date: Sun, 8 Feb 2026 22:14:48 +0900 Subject: [PATCH 39/53] =?UTF-8?q?apitest=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=82=B4=20api=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/api-test.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/api-test.html b/public/api-test.html index 00246189..c3ca95ce 100644 --- a/public/api-test.html +++ b/public/api-test.html @@ -338,7 +338,8 @@

📡 Logs & Response

f3}XsqAqYE|vf@vM-1d4thC4 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