From 81a733d6e5ae94ab0170c06749373f5fc2b8c290 Mon Sep 17 00:00:00 2001 From: Son JuneYeong Date: Mon, 10 Nov 2025 10:48:09 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20=EA=B0=81=EC=A2=85=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- package-lock.json | 236 +++++++++++++++++- package.json | 12 +- prisma/schema.prisma | 99 -------- prisma/seed.js | 75 ------ sprint3-sprint4/.env | 11 - .../UploadedFile/admin.png-1754548438586 | Bin 28000 -> 0 bytes .../UploadedFile/car.jpg-1754547331047 | Bin 14884 -> 0 bytes .../controller/article-controller.js | 205 --------------- sprint3-sprint4/controller/like-controller.js | 48 ---- .../controller/product-controller.js | 207 --------------- sprint3-sprint4/controller/user-controller.js | 115 --------- sprint3-sprint4/file.js | 68 ----- sprint3-sprint4/lib/constants.js | 15 -- sprint3-sprint4/lib/json-web-token.js | 36 --- sprint3-sprint4/lib/passport-lib.js | 67 ----- sprint3-sprint4/lib/prisma.js | 5 - sprint3-sprint4/main.js | 37 --- .../middleware/article-middleware.js | 53 ---- sprint3-sprint4/middleware/auth-middleware.js | 89 ------- .../middleware/comment-middleware.js | 15 -- .../middleware/product-middleware.js | 62 ----- .../migrations/20250804090823_1/migration.sql | 51 ---- .../migrations/20250805005425_/migration.sql | 11 - .../prisma/migrations/migration_lock.toml | 3 - sprint3-sprint4/prisma/schema.prisma | 99 -------- sprint3-sprint4/prisma/seed.js | 75 ------ sprint3-sprint4/router/article-router.js | 76 ------ sprint3-sprint4/router/product-router.js | 84 ------- sprint3-sprint4/router/user-router.js | 49 ---- sprint3-sprint4/service/article-service.js | 70 ------ sprint3-sprint4/service/product-service.js | 55 ---- sprint3-sprint4/service/user-service.js | 80 ------ sprint4-TS | 1 + tsconfig.json | 46 ++++ 35 files changed, 291 insertions(+), 1866 deletions(-) delete mode 100644 prisma/schema.prisma delete mode 100644 prisma/seed.js delete mode 100644 sprint3-sprint4/.env delete mode 100644 sprint3-sprint4/UploadedFile/admin.png-1754548438586 delete mode 100644 sprint3-sprint4/UploadedFile/car.jpg-1754547331047 delete mode 100644 sprint3-sprint4/controller/article-controller.js delete mode 100644 sprint3-sprint4/controller/like-controller.js delete mode 100644 sprint3-sprint4/controller/product-controller.js delete mode 100644 sprint3-sprint4/controller/user-controller.js delete mode 100644 sprint3-sprint4/file.js delete mode 100644 sprint3-sprint4/lib/constants.js delete mode 100644 sprint3-sprint4/lib/json-web-token.js delete mode 100644 sprint3-sprint4/lib/passport-lib.js delete mode 100644 sprint3-sprint4/lib/prisma.js delete mode 100644 sprint3-sprint4/main.js delete mode 100644 sprint3-sprint4/middleware/article-middleware.js delete mode 100644 sprint3-sprint4/middleware/auth-middleware.js delete mode 100644 sprint3-sprint4/middleware/comment-middleware.js delete mode 100644 sprint3-sprint4/middleware/product-middleware.js delete mode 100644 sprint3-sprint4/prisma/migrations/20250804090823_1/migration.sql delete mode 100644 sprint3-sprint4/prisma/migrations/20250805005425_/migration.sql delete mode 100644 sprint3-sprint4/prisma/migrations/migration_lock.toml delete mode 100644 sprint3-sprint4/prisma/schema.prisma delete mode 100644 sprint3-sprint4/prisma/seed.js delete mode 100644 sprint3-sprint4/router/article-router.js delete mode 100644 sprint3-sprint4/router/product-router.js delete mode 100644 sprint3-sprint4/router/user-router.js delete mode 100644 sprint3-sprint4/service/article-service.js delete mode 100644 sprint3-sprint4/service/product-service.js delete mode 100644 sprint3-sprint4/service/user-service.js create mode 160000 sprint4-TS create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 04ce3e4a0..273760f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /.env -/prisma/migrations +/sprint3-sprint4/prisma/migrations /sprint1 diff --git a/package-lock.json b/package-lock.json index a02465eaa..4322e53a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,11 @@ "license": "ISC", "dependencies": { "@prisma/client": "^6.13.0", + "@types/express": "^5.0.3", "axios": "^1.10.0", "bcrypt": "^6.0.0", + "cookie-parser": "^1.4.7", + "cookieparser": "^0.1.0", "cors": "^2.8.5", "express": "^5.1.0", "image-resize": "^1.4.1", @@ -22,7 +25,14 @@ "passport-local": "^1.0.0" }, "devDependencies": { - "prisma": "^6.13.0" + "@tsconfig/recommended": "^1.0.10", + "@types/bcrypt": "^6.0.0", + "@types/multer": "^2.0.0", + "@types/node": "^24.4.0", + "@types/passport": "^1.0.17", + "@types/passport-jwt": "^4.0.1", + "prisma": "^6.13.0", + "typescript": "^5.9.2" } }, "node_modules/@babel/code-frame": { @@ -142,6 +152,114 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@tsconfig/recommended": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.10.tgz", + "integrity": "sha512-cGvydvg03lONp5Z9yaplW493Vw9/um7k588mvDkm+VFPF2PZUVPx0uswq4PFpeEySsLbQRETrDRhzh4Dmxaslw==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", + "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "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==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "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==", + "dev": true, + "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.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", + "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.11.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -149,6 +267,71 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/passport": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -175,9 +358,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "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", @@ -409,6 +592,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -418,6 +620,12 @@ "node": ">=6.6.0" } }, + "node_modules/cookieparser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cookieparser/-/cookieparser-0.1.0.tgz", + "integrity": "sha512-+eHibHast+i+CEoFMQ7y9e4l/SBdWTHhWH6yidaPCYW2S/PmxRfbT1fAle/5WvbzQ6u092qX8XvfmICNtVestQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -1941,6 +2149,26 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", + "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", + "license": "MIT" + }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", diff --git a/package.json b/package.json index 07182bbd0..d5ab91f0e 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,11 @@ "type": "module", "dependencies": { "@prisma/client": "^6.13.0", + "@types/express": "^5.0.3", "axios": "^1.10.0", "bcrypt": "^6.0.0", + "cookie-parser": "^1.4.7", + "cookieparser": "^0.1.0", "cors": "^2.8.5", "express": "^5.1.0", "image-resize": "^1.4.1", @@ -24,7 +27,14 @@ "passport-local": "^1.0.0" }, "devDependencies": { - "prisma": "^6.13.0" + "@tsconfig/recommended": "^1.0.10", + "@types/bcrypt": "^6.0.0", + "@types/multer": "^2.0.0", + "@types/node": "^24.4.0", + "@types/passport": "^1.0.17", + "@types/passport-jwt": "^4.0.1", + "prisma": "^6.13.0", + "typescript": "^5.9.2" }, "prisma": { "seed": "node prisma/seed.js" diff --git a/prisma/schema.prisma b/prisma/schema.prisma deleted file mode 100644 index e063b8c11..000000000 --- a/prisma/schema.prisma +++ /dev/null @@ -1,99 +0,0 @@ -// 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" - previewFeatures = ["fullTextSearchPostgres"] -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -model User { - id Int @id @default(autoincrement()) - email String @unique - nickname String - image String - password String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - product Product[] - article Article[] - articleLike ArticleLike[] - productLike ProductLike[] - ArticleComment ArticleComment[] - ProductComment ProductComment[] -} - -model Product { - id Int @id @default(autoincrement()) - name String - description String - price Decimal - tags String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - comment ProductComment[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - like ProductLike[] -} - -model Article { - id Int @id @default(autoincrement()) - title String - articleContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - comment ArticleComment[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - like ArticleLike[] -} - -model ProductComment { - id Int @id @default(autoincrement()) - commentContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - product Product @relation(fields: [productId], references: [id], onDelete: Cascade) - productId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int -} - -model ArticleComment { - id Int @id @default(autoincrement()) - commentContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - article Article @relation(fields: [articleId], references: [id], onDelete: Cascade) - articleId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int -} - -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()) - updatedAt DateTime @updatedAt -} - -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()) - updatedAt DateTime @updatedAt -} diff --git a/prisma/seed.js b/prisma/seed.js deleted file mode 100644 index 4c854a2f1..000000000 --- a/prisma/seed.js +++ /dev/null @@ -1,75 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - - - -async function main(){ - //product 10개, 댓글 각각 3개씩 생성 - for (let i = 0; i < 10; i++){ - let name = `product${i}`; - let description = `${i}`; - let price = 1000; - let tags = `${i}st number`; - - let productInstance = await prisma.Product.create({ - data: { - name, - description, - price, - tags - } - }); - let productId = productInstance.id ; - - for (let j = 0; j < 3; j++){ - let commentContent = `comment ${j}` - await prisma.ProductComment.create({ - data: { - commentContent, - productId - } - }); - } - console.log(productInstance); - } - - //article 10개, 댓글 각각 3개씩 생성 - for(let x = 0; x < 10; x++){ - let title = `title ${x+1}`; - let articleContent = `text ${x+1} ` - - let commentContent = `conmment ${y}`; - - let articleInstance = await prisma.Article.create({ - data:{ - title, - articleContent - } - }); - for (let y = 0 ; y < 3; y++){ - - - let articleId = articleInstance.id; - - await prisma.articleComment.create({ - data:{ - commentContent, - articleId - } - }) - - } - }; -} - - - -main() -.then( () =>{ - console.log(`seeding 끝`) -}).catch( (e) =>{ - console.error(e); -}).finally(async () => { - await prisma.$disconnect(); -}) \ No newline at end of file diff --git a/sprint3-sprint4/.env b/sprint3-sprint4/.env deleted file mode 100644 index 76b04a8a9..000000000 --- a/sprint3-sprint4/.env +++ /dev/null @@ -1,11 +0,0 @@ -# Environment variables declared in this file are automatically made available to Prisma. -# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema - -# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. -# See the documentation for all the connection string options: https://pris.ly/d/connection-strings - -# The following `prisma+postgres` URL is similar to the URL produced by running a local Prisma Postgres -# server with the `prisma dev` CLI command, when not choosing any non-default ports or settings. The API key, unlike the -# one found in a remote Prisma Postgres URL, does not contain any sensitive information. - -DATABASE_URL="postgresql://sonj0407:enjoysonj4718!@localhost:5432/article?schema=public" \ No newline at end of file diff --git a/sprint3-sprint4/UploadedFile/admin.png-1754548438586 b/sprint3-sprint4/UploadedFile/admin.png-1754548438586 deleted file mode 100644 index a8677e7956083055ede5406715bf8b308da8c880..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28000 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 Bt&RWy diff --git a/sprint3-sprint4/UploadedFile/car.jpg-1754547331047 b/sprint3-sprint4/UploadedFile/car.jpg-1754547331047 deleted file mode 100644 index 46b19a30b4812f239771ea1cbdd0992e2e36c064..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14884 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 b42p!`AD4fCyGqd}(bh!Qh7U@>zt#T*9L0{r diff --git a/sprint3-sprint4/controller/article-controller.js b/sprint3-sprint4/controller/article-controller.js deleted file mode 100644 index c38d3f2b5..000000000 --- a/sprint3-sprint4/controller/article-controller.js +++ /dev/null @@ -1,205 +0,0 @@ -import express from 'express' -import prisma from '../lib/prisma.js' -import ArticleService from '../service/article-service.js'; - - -//모든 게시글 불러오기, 댓글 미포함 - - -export class ArticleController{ - - getArticles = async (req,res,next) =>{ - let {sort='recent', skip='0', take='30', searchtitle, searchcontent} = req.query; - - skip = parseInt(skip); - take = parseInt(take); - const data = {sort, skip, take, searchtitle,searchcontent} - - const user = req.user - console.log(user) - - try{ - let articles = await ArticleService.getArticles(data) - for (let article of articles){ - ArticleService.addIsLiked(user, article) - } - res.status(200).send(articles); - } catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - getOneArticle = async (req,res,next) =>{ - try{ - let id = req.params.id; - id = parseInt(id); - const user = req.user; - console.log(user) - let article = await prisma.Article.findUnique({ - where: {id}, - include : {comment: true} - }); - - article = ArticleService.addIsLiked(user, article); - - return res.status(200).send(article); - - } catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - postArticle = async (req,res,next) =>{ - const {title, articleContent} = req.body; - - try{ - let Article = await prisma.Article.create({ - data: {title,articleContent} - }); - console.log("post Article success"); - return res.status(201).send(Article); - }catch(error){ - console.log("post Article failed because of server"); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - - } - - patchArticle = async (req,res,next) =>{ - try{ - const id = Number(req.params.id); - const {title, articleContent} = req.body; - - const Article = await prisma.Article.update({ - where:{id}, - data: {title,articleContent} - }) - console.log("patch Article success") - return res.status(200).send(Article); - - } catch(error){ - console.log("patch Article failed because of server"); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - deleteArticle = async(req,res,next) =>{ - try{ - const id = Number(req.params.id); - - await prisma.Article.delete({ - where:{id} - }); - - console.log("deleting article success"); - return res.status(204).send("deleting completed"); - } catch(error){ - console.log("delete Article failed because of server"); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - getComments = async(req,res,next) =>{ - try{ - let {take = '10',skip= '1',commentId = '1'} = req.query; - - data = {take, skip, commentId} - const articleComment =await ArticleService.getComment(data); - - return res.status(200).send(articleComment); - - }catch(error){ - console.error(error) - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - postComment = async (req,res,next) =>{ - - const id = Number(req.params.id) ; - - const commentContent = req.body.commentContent; - if (!commentContent|| commentContent.length>500){ - next(new Error("invalid body")) - } - - try{ - const newComment = await prisma.ArticleComment.create({ - data: { - commentContent, - article: { - connect: {id: id} - } - } - }); - return res.status(201).send(newComment); - } catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - - } - - patchComment = async (req,res,next) =>{ - - const id = Number(req.params.id) ; - try{ - const CommentId = Number(req.body.id); - const commentContent = req.body.commentContent; - - if(!commentContent){ - const err = new Error("invalid body data"); - err.status = 400; - return next(err) - } - - const newComment = await prisma.ArticleComment.update({ - where:{id:CommentId}, - data: {commentContent} - }); - - return res.status(200).send(newComment); - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err);; - - } - } - - deleteComment = async (req,res,next) =>{ - try{ - await prisma.ArticleComment.delete({ - where:{id:CommentId} - }) - return res.status(204).send("deleting success"); - - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err);; - - } - } - -} - -export default new ArticleController(); \ No newline at end of file diff --git a/sprint3-sprint4/controller/like-controller.js b/sprint3-sprint4/controller/like-controller.js deleted file mode 100644 index c5d5d8f97..000000000 --- a/sprint3-sprint4/controller/like-controller.js +++ /dev/null @@ -1,48 +0,0 @@ - -import prisma from '../lib/prisma.js' - -class LikeController{ - ArticleLike = async(req,res,next) => { - const userId = Number(req.user.id); - const articleId = Number(req.params.id); - const like = await prisma.ArticleLike.create({ - data:{userId,articleId} - }) - return like - } - - ArticleDislike = async(req,res,next) => { - const userId = Number(req.user.id); - const articleId = Number(req.params.id); - const findedLike = await prisma.ArticleLike.findFirst({ - data:{userId,articleId} - }) - await prisma.ArticleLike.delete({ - where:{id: findedLike.id} - }) - return 'success' - } - - ProductLike = async(req,res,next) => { - const userId = Number(req.user.id); - const productId = Number(req.params.id); - const like = await prisma.ProductLike.create({ - data:{userId,productId} - }) - return like - } - - ProductDislike = async(req,res,next) => { - const userId = Number(req.user.id); - const productId = Number(req.params.id); - const findLike = await prisma.ProductLike.findFirst({ - data:{userId,articleId} - }) - await prisma.ProductLike.delete({ - where:{id: findLike.id} - }) - return 'success' - } -} - -export default new LikeController(); \ No newline at end of file diff --git a/sprint3-sprint4/controller/product-controller.js b/sprint3-sprint4/controller/product-controller.js deleted file mode 100644 index ed2078fbc..000000000 --- a/sprint3-sprint4/controller/product-controller.js +++ /dev/null @@ -1,207 +0,0 @@ - -import productService from '../service/product-service.js'; -import prisma from '../lib/prisma.js' - - -export class ProductController{ - getProducts = async (req,res,next) =>{ - let {sort = 'recent', skip = 0, take= 10, searchName, searchDescription} = req.query; - - const data = {sort, skip, take, searchName, searchDescription}; - try{ - let products = await productService.getProducts(data); - - for (let product of products){ - product = await productService.addIsLiked(product); - } - - return res.status(200).send(products); - - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - - } - } - - getOneProduct = async (req,res,next) =>{ - const id = Number(req.params.id); - - try{ - let product = await prisma.product.findUnique({ - where:{id}, - include: {comment:true} - }); - product = productService.addIsLiked(product); - return res.status(200).send(product); - - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - postProduct = async (req,res,next) =>{ - const {name,description, price, tags} = req.body; - - try{ - const Product = await prisma.product.create({ - data:{ - name, - description, - price, - tags, - } - }); - console.log("post success"); - return res.status(201).send(Product); - - }catch(error){ - console.log('post product failed because of server error'); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - - patchProduct = async (req,res,next) =>{ - const {name, description, price, tags} = req.body; - const id = Number(req.params.id) ; - - try{ - const product= await prisma.product.update({ - where: {id}, - data: { - name, - description, - price, - tags - } - }); - console.log("patch success"); - return res.status(200).send(product); - - }catch(error){ - console.log('patch product failed because of server error'); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - deleteProduct = async (req,res,next) =>{ - const id = Number(req.params.id) ; - try{ - await prisma.Product.delete({ - where:{id} - }); - console.log("deleting success"); - return res.status(204).send("deleting successed"); - - }catch(error){ - console.log('deleting product failed because of server error'); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - getComments = async (req,res,next) =>{ - try{ - let {take = '10',skip= '1',commentId = '1'} = req.query; - take = parseInt(take); - skip = parseInt(skip); - commentId = parseInt(commentId); - - const comments= await prisma.ProductComment.findMany({ - take, - skip, - cursor: {id: commentId}, - orderBy:{id: 'asc'} - }); - - if (!comments){ - return res.status(300).send("There isn't comment. Write the first comment!"); - } - - return res.status(200).send(comments); - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - postComment = async (req,res,next) =>{ - let id; - id = Number(req.params.id) ; - - try{ - const commentContent = req.body.commentContent; - if(!commentContent || commentContent.length>1000){ - const err = new Error("invalid body data"); - err.status = 400; - return next(err); - } - const newComment = await prisma.ProductComment.create({ - data: { - commentContent, - product:{connect: {id}} - } - }); - res.status(201).send(newComment); - }catch(error){ - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - - - } - - patchComment = async (req,res,next) =>{ - const id = Number(req.params.id) ; - - try{ - const commentContent = req.body.commentContent; - - const newComment = await prisma.ProductComment.update({ - where:{id:CommentId}, - data: {commentContent} - }); - return res.status(200).send(newComment); - - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } - - deleteComment = async (req,res,next) =>{ - - const id = Number(req.params.id) ; - - try{ - await prisma.ProductComment.delete({ - where:{id:CommentId} - }); - return res.status(204).send("delete success"); - - }catch(error){ - console.error(error); - const err = new Error("Server Error"); - err.status = 500; - return next(err); - } - } -} - -export default new ProductController(); \ No newline at end of file diff --git a/sprint3-sprint4/controller/user-controller.js b/sprint3-sprint4/controller/user-controller.js deleted file mode 100644 index 8df4380c4..000000000 --- a/sprint3-sprint4/controller/user-controller.js +++ /dev/null @@ -1,115 +0,0 @@ -import bcrypt from 'bcrypt' -import prisma from '../lib/prisma.js' -import jsonWebToken from '../lib/json-web-token.js'; -import userService from '../service/user-service.js'; - -export class UserController{ - //회원가입 유효성 검사 필요 - register = async(req,res,next) => { - try{ - const {email, nickname, password, image} = req.body; - - const newUser = await userService.createUser({ - email,nickname,password, image}) - const formatUser = await userService.formatUser(newUser) - return res.send(formatUser) - }catch(error){ - console.error(error) - res.send(error) - } - - } - - login = async(req,res,next) => { - try{ - const {email, password} = req.body; - const user = await prisma.user.findUnique({ - where:{email} - }); - if (!user){ - throw new Error("no user") - } - - const accessToken = await userService.loginAndGiveToken({email, password}) - console.log('access token: ', accessToken) - res.send(accessToken) - }catch(error){ - console.error(error) - res.send(error) - } - - } - - getUser = async(req,res,next) => { - try{ - const user = req.user ; - return res.send(userService.formatUser(user)) - }catch(error){ - console.error(error) - return res.send(error) - } - - } - - patchUser = async(req,res,next) => { - try{ - const {nickname, image} = req.body; - const userId = Number(req.user.id); - const patchUser = await prisma.user.update({ - where:{id:userId}, - data:{nickname,image} - }) - return res.send(userService.formatUser(patchUser)) - }catch(error){ - console.error(error) - return res.send(error) - } - - } - - patchPassword = async(req,res,next) => { - try{ - const {password} = req.body; - const userId = Number(req.user.id); - const patchUser = await prisma.user.update({ - where:{id:userId}, - data:{password} - }) - return res.send(userService.formatUser(patchUser)) - }catch(error){ - console.error(error) - return res.send(error) - } - } - - getUserProduct = async(req,res, next) => { - const userId = Number(req.params.userId); - try{ - const user = prisma.user.findUnique({ - where:{id: userId}, - include:{product:true} - }) - const userProduct = user.product - - return res.send(userProduct) - }catch(error){ - console.error(error) - return res.send(error) - } - - - } - - getLikedProduct = async(req, res, next) => { - const user = req.user - - //likeModels는 유저, product의 정보를 가진 좋아요 모델의 list - const likedModels = user.productLike; - //like models를 바탕으로 현재 로그인 한 유저가 좋아요 한 product들을 가져옵니다 - const likedProducList = await userService.likedProdcut(likedModels) - - return res.send(likedProducList) - } -} - -export default new UserController(); \ No newline at end of file diff --git a/sprint3-sprint4/file.js b/sprint3-sprint4/file.js deleted file mode 100644 index 0c7eb4e26..000000000 --- a/sprint3-sprint4/file.js +++ /dev/null @@ -1,68 +0,0 @@ -import express from 'express'; -import multer from 'multer'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import fs from 'fs'; -import imageResize from 'image-resize'; - -const app = express(); -const fileRouter = express.Router(); - - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const uploadPath = path.join(__dirname, 'UploadedFile'); -if (!fs.existsSync(uploadPath)) { - fs.mkdirSync(uploadPath); -} - -const storage = multer.diskStorage({ - destination: function (req,file,cb){ - cb(null, uploadPath) - }, - filename: function(req,file,cb){ - cb(null, file.originalname + '-' + Date.now()) - } -}) - -const upload = multer({ storage: storage}) - -fileRouter.get('',(req,res,next) =>{ - try{ - return res.status(200).send("file upload site"); - }catch(error){ - console.error(error); - return next(error); - } -}) - -fileRouter.post('/upload', upload.single('FormName'), async (req,res,next) =>{ - try{ - console.log(req.file) - if (!req.file){ - return res.status(400).json({error: "no file uploaded"}); - } - - return res.send(`${req.file.path}`); - } catch(error){ - console.error(error); - return next(error); - } - -}); - -async function resizeImage(file){ - const newImage= await imageResize(file,{ - format:'png', - width: 640 , - height: 640 - } - ) -} - -const ImagePath = 'sprint3/UploadedFile' - - - -export default fileRouter; diff --git a/sprint3-sprint4/lib/constants.js b/sprint3-sprint4/lib/constants.js deleted file mode 100644 index 2dbf9c29e..000000000 --- a/sprint3-sprint4/lib/constants.js +++ /dev/null @@ -1,15 +0,0 @@ - - -const ACCESS_SECRET_KEY = 'qwer'; -const REFRESH_SECRET_KEY = 'asdf'; -const JWT_SECRET_KEY = 'zxcv'; -const ACCESS_TOKEN_COOKIE_NAME = 'AccessToken'; -const REFRESH_TOKEN_COOKIE_NAME = 'asdf'; - -export { - ACCESS_SECRET_KEY, - REFRESH_SECRET_KEY, - JWT_SECRET_KEY, - ACCESS_TOKEN_COOKIE_NAME, - REFRESH_TOKEN_COOKIE_NAME -} \ No newline at end of file diff --git a/sprint3-sprint4/lib/json-web-token.js b/sprint3-sprint4/lib/json-web-token.js deleted file mode 100644 index b2fede294..000000000 --- a/sprint3-sprint4/lib/json-web-token.js +++ /dev/null @@ -1,36 +0,0 @@ -import jwt from 'jsonwebtoken'; -import { REFRESH_SECRET_KEY, ACCESS_SECRET_KEY} from './constants.js' - -export class jsonWebToken{ - /* - payload = { - userId: Int - } - */ - generateAccess = (user) => { - const SecretKey = ACCESS_SECRET_KEY; - const payload = { - userId: user.id - } - const accesToken = jwt.sign(payload, SecretKey, - {expiresIn: '10h'} ) - console.log("at json-web-token.js, acceessToken: ", accesToken) - return accesToken - } - - generateRefresh = (payload) => { - const SecretKey = REFRESH_SECRET_KEY; - const refreshToken = jwt.sign(payload.userId, secretKey, - {expiresIn: '1d'}) - - return refreshToken - } - - verify = (token, secretKey) => { - const decoded = jwt.verify(token,secretKey) - - return decoded - } -} - -export default new jsonWebToken(); \ No newline at end of file diff --git a/sprint3-sprint4/lib/passport-lib.js b/sprint3-sprint4/lib/passport-lib.js deleted file mode 100644 index 8d17c08c8..000000000 --- a/sprint3-sprint4/lib/passport-lib.js +++ /dev/null @@ -1,67 +0,0 @@ -import passport from 'passport' - -import { Strategy as JwtStrategy } from 'passport-jwt'; - -import prisma from './prisma.js'; - - -import {ACCESS_SECRET_KEY, - REFRESH_SECRET_KEY, - ACCESS_TOKEN_COOKIE_NAME, - REFRESH_TOKEN_COOKIE_NAME - }from './constants.js' - - - -//request로 부터 token을 받고, 해석하는 부분(options) -const accessJwtOptions = { - jwtFromRequest : (req) => - req.cookies[ACCESS_TOKEN_COOKIE_NAME], - secretOrKey: ACCESS_SECRET_KEY -} -const refreshJwtOptions = { - jwtFromRequest : (req) => - req.cookies[REFRESH_TOKEN_COOKIE_NAME], - secretOrKey: REFRESH_SECRET_KEY -} - - -//passport에서 인증이 이루어지는 부분(verify) -async function jwtVerify(payload, done){ - const userId = payload.userId; - if (!userId){ - done(error,null); - } - const user = await prisma.user.findUnique({id:userId}) - if (!user){ - done(error,null); - } - done(null,user); -} - -//Access Token을 검증하는 전략 -export const accessJwtStrategy = new JwtStrategy(accessJwtOptions, (payload, done) => { - accessJwtOptions, - jwtVerify -}) - -//Refresh Token을 검증하는 전략 -export const refreshJwtStrategy = new JwtStrategy(refreshJwtOptions, (payload, done) => { - refreshJwtOptions, - jwtVerify -}) - - -passport.use('AccessToken', accessJwtStrategy) -passport.use('RefreshToken', refreshJwtStrategy) - -export const authUserWithParmaId = passport.authenticate("Access Token", (req,res) =>{ - const paramId = req.params.id; - const tokenId = req.user.id - - if (pramaId != tokenId){ - return res.send("no authorization") - }else{ - return next() - } -}) \ No newline at end of file diff --git a/sprint3-sprint4/lib/prisma.js b/sprint3-sprint4/lib/prisma.js deleted file mode 100644 index a11c808f6..000000000 --- a/sprint3-sprint4/lib/prisma.js +++ /dev/null @@ -1,5 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -export const prisma = new PrismaClient(); - -export default prisma; \ No newline at end of file diff --git a/sprint3-sprint4/main.js b/sprint3-sprint4/main.js deleted file mode 100644 index c74e98496..000000000 --- a/sprint3-sprint4/main.js +++ /dev/null @@ -1,37 +0,0 @@ -import articleRouter from "./router/article-router.js"; -import productRouter from "./router/product-router.js"; -import userRouter from "./router/user-router.js"; -import fileRouter from "./file.js"; - -import cookieParser from "cookie-parser"; -import cors from 'cors'; -import express from 'express'; -import 'dotenv/config'; -import passport from "passport"; - -import { refreshJwtStrategy, accessJwtStrategy } from "./lib/passport-lib.js"; -const app = express(); - -app.use(cors()); -app.use(express.json()); -app.use(cookieParser()); -app.use(passport.initialize()); -passport.use('AccessToken', accessJwtStrategy) -passport.use('RefreshToken', refreshJwtStrategy) - - - -app.use('/user', userRouter); -app.use('/article', articleRouter); -app.use('/product', productRouter); -app.use('/upload',fileRouter); - -app.use((err, req, res, next) =>{ - if (err){ - res.json( err.message|| "Server Error Occured"); - } -}) - -app.listen(3000, () =>{ - console.log("server is running at http://localhost:3000") -}) \ No newline at end of file diff --git a/sprint3-sprint4/middleware/article-middleware.js b/sprint3-sprint4/middleware/article-middleware.js deleted file mode 100644 index 39d6df386..000000000 --- a/sprint3-sprint4/middleware/article-middleware.js +++ /dev/null @@ -1,53 +0,0 @@ - -import prisma from "../lib/prisma.js"; - -class ArticleMiddleware{ - ArticleValid = async (req,res,next) =>{ - const {title, articleContent} = req.body; - if (!title ||!articleContent){ - res.send("no title or articleContent"); - } - - if (title.length>50 ||articleContent.length>800 ){ - res.send("too long or too short"); - } - - next(); - } - - ValidateId = async (req,res,next) => { - const id = Number(req.params.id); - - if (!id){ - const err = new Error("invalid parameter") - err.status = 400; - return next(err); - } - - const Article = await prisma.Article.findUnique({ - where: {id}, - include : {comment: true} - }); - - if (!Article){ - const err = new Error("No content") - err.status = 404 - return next(err); - } - - next() - } - - ValidateForm = async (req,res,next) => { - const {title, articleContent} = req.body; - - if (!title || !articleContent){ - const err = new Error("invalid body data"); - err.status = 400; - return next(err) - } - next(); - } - -} -export default new ArticleMiddleware(); \ No newline at end of file diff --git a/sprint3-sprint4/middleware/auth-middleware.js b/sprint3-sprint4/middleware/auth-middleware.js deleted file mode 100644 index e41278e70..000000000 --- a/sprint3-sprint4/middleware/auth-middleware.js +++ /dev/null @@ -1,89 +0,0 @@ - - -import prisma from '../lib/prisma.js' - - -export function checkAuthenticated(req,res,next){ - if (req.isAuthenticated()){ - return next() - }else{ - res.redirect('/login') - } -} - -export function checkAccessToken(req,res,next){ - -} - -export async function checkProductAuthorize(req,res,next){ - const productId = Number(req.params.id); - const user = req.user; - const product = await prisma.product.findUnique({ - where:{id:productId} - }) - if (product.userId==user.id){ - return next() - }else{ - const error = new Error("401 unathorized") - error.status = 401 - throw error - } -} - -export async function checkArticleAuthorize(req,res,next){ - const articleId = Number(req.params.id); - const user = req.user; - const article = await prisma.article.findUnique({ - where:{id:articleId} - }) - if (article.userId==user.id){ - return next() - }else{ - const error = new Error("401 unathorized") - error.status = 401 - throw error - } -} - - -export async function checkArticleCommentAuth(req,res,next){ - const commentId = Number(req.params.commentId); - const comment = await prisma.article.findUnique({ - where:{id:commentId} - }) - const userId = Number(req.user.id); - - if (Number(comment.userId)===userId){ - return next() - }else{ - const error = new Error("401 unathorized") - error.status = 401 - throw error - } -} - -export async function checkProductCommentAuth(req,res,next){ - const commentId = Number(req.params.commentId); - const comment = await prisma.product.findUnique({ - where:{id:commentId} - }) - const userId = Number(req.user.id); - - if (Number(comment.userId)===userId){ - return next() - }else{ - const error = new Error("401 unathorized") - error.status = 401 - throw error - } -} - -export async function checkUserAuth(req,res,next){ - const paramId = Number(req.params.id); - const userId = Number(req.user.id); - if (paramId == userId){ - return next(); - }else{ - throw new Error("no auth") - } -} \ No newline at end of file diff --git a/sprint3-sprint4/middleware/comment-middleware.js b/sprint3-sprint4/middleware/comment-middleware.js deleted file mode 100644 index 080d09b58..000000000 --- a/sprint3-sprint4/middleware/comment-middleware.js +++ /dev/null @@ -1,15 +0,0 @@ - - - -export async function ValidCommentForm(req,res,next){ - const id = Number(req.params.id) ; - const CommentId= Number(req.params.commentId); - - const commentContent = req.body.commentContent; - if (!commentContent|| commentContent.length>500){ - next(new Error("invalid body")) - } - next() -} - -export default ValidCommentForm; \ No newline at end of file diff --git a/sprint3-sprint4/middleware/product-middleware.js b/sprint3-sprint4/middleware/product-middleware.js deleted file mode 100644 index 7218b2fda..000000000 --- a/sprint3-sprint4/middleware/product-middleware.js +++ /dev/null @@ -1,62 +0,0 @@ -import multer from "multer"; -import express from 'express'; - - -export function ProductValid(req,res,next){ - const {name,description, price, tags} = req.body; - try{ - if (typeof(name) =='undefined' || typeof(description) =='undefined'|| - typeof(price) == 'undefined' || typeof(tags)=='undefined'){ - throw Error; - }else{ - next(); - } - } catch(error){ - return res.status(400).send("400 bad request"); - } -} - -export class ProductMiddleware{ - validateId = async(req,res,next) => { - const id = number(req.params.id); - if (!id){ - const err = new Error("invalid parameter") - err.status = 400; - return next(err); - } - - const Product = await prisma.product.findUnique({ - where:{id}, - include: {comment:true} - }); - if (!product){ - const err = new Error("No content") - err.status = 404; - return next(err); - } - - next() - } - - validateForm = async(req,res,next) => { - const {name,description, price, tags} = req.body; - if (!name|| !description ||!price || !tags){ - const err = new Error("invalid body data"); - err.status = 400; - return next(err) - } - next() - } - - validateCommentId = async(req,res,next) => { - const CommentId = Number(req.params.commentId); - if (!CommentId){ - const err = new Error("invalid parameter") - err.status = 400; - return next(err); - } - next() - } - -} -export default new ProductMiddleware(); \ No newline at end of file diff --git a/sprint3-sprint4/prisma/migrations/20250804090823_1/migration.sql b/sprint3-sprint4/prisma/migrations/20250804090823_1/migration.sql deleted file mode 100644 index 4bb555a81..000000000 --- a/sprint3-sprint4/prisma/migrations/20250804090823_1/migration.sql +++ /dev/null @@ -1,51 +0,0 @@ --- CreateTable -CREATE TABLE "public"."Product" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT NOT NULL, - "price" DECIMAL(65,30) NOT NULL, - "tags" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "Product_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."Article" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "articleContent" 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 "public"."ProductComment" ( - "id" SERIAL NOT NULL, - "commentContent" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "productId" INTEGER NOT NULL, - - CONSTRAINT "ProductComment_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."ArticleComment" ( - "id" SERIAL NOT NULL, - "commentContent" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "articleId" INTEGER NOT NULL, - - CONSTRAINT "ArticleComment_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "public"."ProductComment" ADD CONSTRAINT "ProductComment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "public"."Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "public"."ArticleComment" ADD CONSTRAINT "ArticleComment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "public"."Article"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/sprint3-sprint4/prisma/migrations/20250805005425_/migration.sql b/sprint3-sprint4/prisma/migrations/20250805005425_/migration.sql deleted file mode 100644 index 5b4a356de..000000000 --- a/sprint3-sprint4/prisma/migrations/20250805005425_/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ --- DropForeignKey -ALTER TABLE "public"."ArticleComment" DROP CONSTRAINT "ArticleComment_articleId_fkey"; - --- DropForeignKey -ALTER TABLE "public"."ProductComment" DROP CONSTRAINT "ProductComment_productId_fkey"; - --- AddForeignKey -ALTER TABLE "public"."ProductComment" ADD CONSTRAINT "ProductComment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "public"."Product"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "public"."ArticleComment" ADD CONSTRAINT "ArticleComment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "public"."Article"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/sprint3-sprint4/prisma/migrations/migration_lock.toml b/sprint3-sprint4/prisma/migrations/migration_lock.toml deleted file mode 100644 index 044d57cdb..000000000 --- a/sprint3-sprint4/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (e.g., Git) -provider = "postgresql" diff --git a/sprint3-sprint4/prisma/schema.prisma b/sprint3-sprint4/prisma/schema.prisma deleted file mode 100644 index e063b8c11..000000000 --- a/sprint3-sprint4/prisma/schema.prisma +++ /dev/null @@ -1,99 +0,0 @@ -// 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" - previewFeatures = ["fullTextSearchPostgres"] -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -model User { - id Int @id @default(autoincrement()) - email String @unique - nickname String - image String - password String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - product Product[] - article Article[] - articleLike ArticleLike[] - productLike ProductLike[] - ArticleComment ArticleComment[] - ProductComment ProductComment[] -} - -model Product { - id Int @id @default(autoincrement()) - name String - description String - price Decimal - tags String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - comment ProductComment[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - like ProductLike[] -} - -model Article { - id Int @id @default(autoincrement()) - title String - articleContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - comment ArticleComment[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - like ArticleLike[] -} - -model ProductComment { - id Int @id @default(autoincrement()) - commentContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - product Product @relation(fields: [productId], references: [id], onDelete: Cascade) - productId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int -} - -model ArticleComment { - id Int @id @default(autoincrement()) - commentContent String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - article Article @relation(fields: [articleId], references: [id], onDelete: Cascade) - articleId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int -} - -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()) - updatedAt DateTime @updatedAt -} - -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()) - updatedAt DateTime @updatedAt -} diff --git a/sprint3-sprint4/prisma/seed.js b/sprint3-sprint4/prisma/seed.js deleted file mode 100644 index a4796e72b..000000000 --- a/sprint3-sprint4/prisma/seed.js +++ /dev/null @@ -1,75 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - - - -// async function main(){ -// //product 10개, 댓글 각각 3개씩 생성 -// for (let i = 0; i < 10; i++){ -// let name = `product${i}`; -// let description = `${i}`; -// let price = 1000; -// let tags = `${i}st number`; - -// let productInstance = await prisma.Product.create({ -// data: { -// name, -// description, -// price, -// tags -// } -// }); -// let productId = productInstance.id ; - -// for (let j = 0; j < 3; j++){ -// let commentContent = `comment ${j}` -// await prisma.ProductComment.create({ -// data: { -// commentContent, -// productId -// } -// }); -// } -// console.log(productInstance); -// } - -// //article 10개, 댓글 각각 3개씩 생성 -// for(let x = 0; x < 10; x++){ -// let title = `title ${x+1}`; -// let articleContent = `text ${x+1} ` - -// let commentContent = `conmment ${y}`; - -// let articleInstance = await prisma.Article.create({ -// data:{ -// title, -// articleContent -// } -// }); -// for (let y = 0 ; y < 3; y++){ - - -// let articleId = articleInstance.id; - -// await prisma.articleComment.create({ -// data:{ -// commentContent, -// articleId -// } -// }) - -// } -// }; -// } - - - -// main() -// .then( () =>{ -// console.log(`seeding 끝`) -// }).catch( (e) =>{ -// console.error(e); -// }).finally(async () => { -// await prisma.$disconnect(); -// }) \ No newline at end of file diff --git a/sprint3-sprint4/router/article-router.js b/sprint3-sprint4/router/article-router.js deleted file mode 100644 index 0fc3b9bd4..000000000 --- a/sprint3-sprint4/router/article-router.js +++ /dev/null @@ -1,76 +0,0 @@ -import express from 'express' - -import LikeController from '../controller/like-controller.js'; - -import articleController from '../controller/article-controller.js'; -import articleMiddleware from '../middleware/article-middleware.js'; - -import { checkAuthenticated, checkArticleAuthorize, checkArticleCommentAuth} from '../middleware/auth-middleware.js'; - -import passport from 'passport'; - - -const ArticleRouter = express.Router(); - -//article API routing -ArticleRouter.get('/', articleController.getArticles) - -ArticleRouter.get('/detail/:id', - passport.authenticate('AccessToken', {session:false}), - articleMiddleware.ValidateId, - articleController.getOneArticle) - -ArticleRouter.post('/', - articleMiddleware.ArticleValid, - articleMiddleware.ValidateForm, - passport.authenticate('AccessToken', {session:false}) , - checkAuthenticated, - articleController.postArticle) - -ArticleRouter.patch('detail/:id', - articleMiddleware.ValidateId, - articleMiddleware.ValidateForm, - checkArticleAuthorize, - articleController.patchArticle) - -ArticleRouter.delete('/detail/:id', - articleMiddleware.ValidateId, - checkArticleAuthorize, - articleController.deleteArticle ) - - -//like feature -ArticleRouter.post('detail/:id', - passport.authenticate('AccessToken', {session:false}) , - LikeController.ArticleLike -) - -ArticleRouter.delete('detail/:id', - passport.authenticate('AccessToken', {session:false}) , - LikeController.ArticleDislike -) - - -//article comments API routing -ArticleRouter.get('/comments', - articleController.getComments) - -ArticleRouter.post('/detail/:id/comments', - articleMiddleware.ValidateId, - passport.authenticate('AccessToken', {session:false}) , - articleController.postComment) - -ArticleRouter.patch('/detail/:id/comments', - articleMiddleware.ValidateId, - passport.authenticate('AccessToken', {session:false}) , - checkArticleCommentAuth, - articleController.patchComment) - -ArticleRouter.delete('/detail/:id/comments/:commentId', - articleMiddleware.ValidateId, - passport.authenticate('AccessToken', {session:false}) , - checkArticleCommentAuth, - articleController.deleteComment) - -export default ArticleRouter; - diff --git a/sprint3-sprint4/router/product-router.js b/sprint3-sprint4/router/product-router.js deleted file mode 100644 index 219acb6b1..000000000 --- a/sprint3-sprint4/router/product-router.js +++ /dev/null @@ -1,84 +0,0 @@ -import express from 'express' - -import LikeController from '../controller/like-controller.js'; - -import productController from '../controller/product-controller.js'; - -import productMiddleware from '../middleware/product-middleware.js'; - -import { checkProductCommentAuth ,checkProductAuthorize } from '../middleware/auth-middleware.js' - -import passport from 'passport' - -const ProductRouter = express.Router() - - -//Product API 라우팅 -ProductRouter.get('/', - productController.getProducts) - -ProductRouter.get('/detail/:id', - productMiddleware.validateId, - productController.getOneProduct) - -ProductRouter.post('/', - // ProductValid, - productMiddleware.validateId, - productMiddleware.validateForm, - passport.authenticate('AccessToken', {session:false}) , - productController.postProduct ) - -ProductRouter.patch('/detail/:id', - productMiddleware.validateId, - passport.authenticate('AccessToken', {session:false}) , - checkProductAuthorize, - productController.patchProduct ) - -ProductRouter.delete('/detail/:id', - productMiddleware.validateId , - passport.authenticate('AccessToken', {session:false}) , - checkProductAuthorize, - productController.deleteProduct) - - - -//like feature -ProductRouter.post('detail/:id', - productMiddleware.validateId, - passport.authenticate('AccessToken', {session:false}) , - LikeController.ProductLike -) - -ProductRouter.delete('detail/:id', - productMiddleware.validateId, - passport.authenticate('AccessToken', {session:false}) , - LikeController.ProductDislike -) - - - -//Product Comment API 라우팅 -ProductRouter.get('/comments', - productController.getComments) - -ProductRouter.post('/detail/:id/comment', - productMiddleware.validateId, - passport.authenticate('AccessToken', {session:false}) , - productController.postComment ) - -ProductRouter.patch('/detail/:id/comment/:commentId', - productMiddleware.validateId, - productMiddleware.validateCommentId, - passport.authenticate('AccessToken', {session:false}) , - checkProductCommentAuth, - productController.patchComment ) - -ProductRouter.delete('/detail/:id/comment/:commentId', - productMiddleware.validateId, - productMiddleware.validateCommentId, - passport.authenticate('AccessToken', {session:false}) , - checkProductCommentAuth, - productController.deleteComment) - - -export default ProductRouter; \ No newline at end of file diff --git a/sprint3-sprint4/router/user-router.js b/sprint3-sprint4/router/user-router.js deleted file mode 100644 index b86bd6be5..000000000 --- a/sprint3-sprint4/router/user-router.js +++ /dev/null @@ -1,49 +0,0 @@ -import express from 'express'; -import userController from '../controller/user-controller.js'; -import passport from 'passport'; -import { checkUserAuth } from '../middleware/auth-middleware.js'; -import { authUserWithParmaId } from '../lib/passport-lib.js'; -import { REFRESH_TOKEN_COOKIE_NAME } from '../lib/constants.js'; - -const userRouter = express.Router(); - -// register, login, get login user information -userRouter.post('/login', - userController.login) -userRouter.post('/register', - userController.register) -userRouter.get('/:id', - passport.authenticate('AccessToken', {session:false}) , - userController.getUser) - -//modify user information -userRouter.patch('/:id', - passport.authenticate('AccessToken', {session:false}) , - userController.patchUser) -userRouter.patch('/:id', - passport.authenticate('AccessToken', {session:false}) , - userController.patchPassword) - -//get products of login user -userRouter.get('/:id/products', - passport.authenticate("Access Token", {session:false}), - checkUserAuth, - userController.getUserProduct) - -//get liked products of login user -userRouter.get(':id/like', - passport.authenticate('AccessToken', {session:false}) , - -) - -//refresh token -userRouter.post('auth/refresh', - passport.authenticate('RefreshToken', {session:false}) -) - - -// passport.authenticate('local'), -// passport.authenticate('jwt') - - -export default userRouter; \ No newline at end of file diff --git a/sprint3-sprint4/service/article-service.js b/sprint3-sprint4/service/article-service.js deleted file mode 100644 index 1d2662ecd..000000000 --- a/sprint3-sprint4/service/article-service.js +++ /dev/null @@ -1,70 +0,0 @@ -import prisma from '../lib/prisma.js' - - -export class ArticleService{ - getArticles = async({skip, take, sort, searchtitle, searchcontent}) =>{ - - let orderBy ; - skip = parseInt(skip); - take = parseInt(take); - - if (sort == 'oldest'){ - orderBy = {createdAt : 'desc'}; - }else if (sort == 'recent'){ - orderBy = {createdAt : 'asc'}; - }else{ - orderBy = {createdAt : 'desc'}; - } - - const Articles = await prisma.article.findMany({ - skip, - take, - orderBy, - where: { - AND:[{title: - {contains : searchtitle , - mode : 'insensitive'}}, - {articleContent:{contains : searchcontent}}]} - }) - return Articles - } - - getComment = async({take,skip,commentId}) => { - take = parseInt(take); - skip = parseInt(skip); - commentId = parseInt(commentId); - - - const articleComment =await prisma.ArticleComment.findMany({ - take, - skip, - cursor: {id: commentId}, - orderBy:{id: 'asc'} - }); - return articleComment; - } - - addIsLiked = async(user, article) => { - - const articleLikeList = user.articleLike; - const likedArticleIds = []; - if (!articleLikeList){ - article.isLiked = false; - }else{ - for (const articleLike of articleLikeList){ - let articleId = articleLike.articleId; - likedArticles.push(article) - } - - const articleId = Number(article.id) - if (likedArticleIds.includes(articleId)){ - article.isLiked = true; - }else { - article.isLiked = false; - } - } - return article - } -} - -export default new ArticleService(); \ No newline at end of file diff --git a/sprint3-sprint4/service/product-service.js b/sprint3-sprint4/service/product-service.js deleted file mode 100644 index aaf0bc981..000000000 --- a/sprint3-sprint4/service/product-service.js +++ /dev/null @@ -1,55 +0,0 @@ - -import prisma from '../lib/prisma.js' - -export class productService{ - - getProducts = async(data) => { - - let {sort, skip, take, searchName, searchDescription} = data; - let orderBy ; - - skip = Number(skip); - take = Number(skip); - - if (sort === 'oldest'){ - orderBy = {createdAt : 'desc'}; - }else if (sort == 'recent'){ - orderBy = {createdAt : 'asc'}; - }else{ - orderBy = {createdAt : 'desc'}; - } - - const product = await prisma.product.findMany({ - skip, - take, - where: { - AND:[ - searchName? {name: {contains : searchName}} : undefined, - searchDescription? {content: {contains : searchDescription}} : undefined - ].filter(Boolean) - }, - orderBy - }); - return product - } - - addIsLiked = async(user, product) => { - const productLikeList = user.productLike; - const likedProductIds = []; - for (const productLike of productLikeList){ - let productId = productLike.productId; - likedProductIds.push(productId) - } - - const productId = Number(product.id) - if (likedProductIds.includes(productId)){ - product.isLiked = true; - }else { - product.isLiked = false; - } - return product - } - -} - -export default new productService; \ No newline at end of file diff --git a/sprint3-sprint4/service/user-service.js b/sprint3-sprint4/service/user-service.js deleted file mode 100644 index 64ea78df8..000000000 --- a/sprint3-sprint4/service/user-service.js +++ /dev/null @@ -1,80 +0,0 @@ - -import bcrypt from 'bcrypt' -import prisma from '../lib/prisma.js' -import jsonWebToken from '../lib/json-web-token.js'; - -class userService{ - createUser = async({password,email,nickname,image}) => { - try{ - const salt = await bcrypt.genSalt(10); - password = String(password) - const hashedPassword = await bcrypt.hash(password, salt); - const data= { - password: hashedPassword, - email, - nickname, - image - } - - const newUser = await prisma.user.create({data}) - return newUser - }catch(error){ - console.error(error); - throw new Error(error.message) - } - - } - - loginAndGiveToken = async({email,password}) => { - const user = await prisma.user.findUnique({ - where:{email} - }); - - if (user){ - let accessToken - const isMatch = await bcrypt.compare(password, user.password); - if (isMatch){ - accessToken = await jsonWebToken.generateAccess(user) - }else{ - throw new Error("check Id and Password again") - } - console.log("at user-service, accees: ", accessToken) - return accessToken - }else{ - throw new Error("no user"); - } - } - - /* - input: 현재 로그인한 유저의 - product, user의 좋아요 관계인 - like 모델들의 모임 - output: 유저가 좋아요 한 product들을 list 형식으로 가져옵니다. - */ - likedProduct = async (likeModels) => { - let likedProducList = []; - for (const likedModel of likeModels){ - const productId= likedModel.productId; - const Product = await prisma.product.findFirst({ - id:productId - }) - likedProducList.push(product) - } - - return likedProducList; - } - - formatUser = async(user) => { - let formattedUser = {}; - formattedUser.email = user.email; - formattedUser.nickname = user.nickname; - formattedUser.image = user.image; - formattedUser.createdAt = user.createdAt; - formattedUser.updatedAt = user.updatedAt; - return formattedUser - } - - -} - -export default new userService(); \ No newline at end of file diff --git a/sprint4-TS b/sprint4-TS new file mode 160000 index 000000000..324c02427 --- /dev/null +++ b/sprint4-TS @@ -0,0 +1 @@ +Subproject commit 324c024277287f5a79a0f30de5bfb273caa1c156 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..5d1c6e971 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,46 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./sprint4-TS", + // "outDir": "./sprint4-JS", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "commonjs", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // 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, + "jsx": "react-jsx", + "verbatimModuleSyntax": false, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + + "esModuleInterop": true, + } +}