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 a8677e795..000000000 Binary files a/sprint3-sprint4/UploadedFile/admin.png-1754548438586 and /dev/null differ diff --git a/sprint3-sprint4/UploadedFile/car.jpg-1754547331047 b/sprint3-sprint4/UploadedFile/car.jpg-1754547331047 deleted file mode 100644 index 46b19a30b..000000000 Binary files a/sprint3-sprint4/UploadedFile/car.jpg-1754547331047 and /dev/null differ 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, + } +}