Skip to content
Snippets Groups Projects
Commit 219afa33 authored by Jaeyong Lee's avatar Jaeyong Lee
Browse files
parents bcbedbaa c20349e0
No related branches found
No related tags found
No related merge requests found
Showing with 393 additions and 105 deletions
......@@ -7,3 +7,23 @@
#### 프론트: cd frontend; npm run start
#### 백앤드: cd backend; npm run start
프론트 백 모두 문서 저장되면 다시 로딩 되니까 두개 한번에 켜놓고 수정하시면 되시겠습니다.
## Auth Specification
|Method|Path|Message Format(JSON)|설명|
|---|---|---|---|
|POST|/auth/login|{<br>"email": string,<br>"name": string,<br>"sub": string,<br>"picture": string,<br>}|로그인 요청|
|GET|/auth/logout||로그아웃 요청|
|GET|/auth/session||세션 연결 확인 요청|
## Article Specification
|Method|Path|Message Format(JSON)|설명|
|---|---|---|---|
|POST|/article|{<br>"title": string,<br>"content": string,<br>"img": string[],<br>"keyword": string,<br>"latitiude":number,<br>"longitude": number<br>}|게시글 작성 요청|
|DELETE|/article/[id]||특정 게시글 삭제 요청|
|GET|/article||전체 게시글 요청|
|GET|/article/[id]||특정 게시글 요청|
|GET|/article/search/[keyword]||키워드로 게시글 검색 요청|
|GET|/article/sort/[crit]||게시글 정렬 요청|
|POST|/article/[id]/comment|{<br>"content": string<br>}|댓글 작성 요청|
|DELETE|/article/[articleId]/comment/[commentId]||특정 댓글 삭제 요청|
|PUT|/article/[id]/like||게시글 좋아요 요청|
import mongoose from 'mongoose';
import UserSchema from './user.js';
import UserSchema from '../user/user.js';
const CommentSchema = new mongoose.Schema({
content: {
......
......@@ -3,87 +3,96 @@ import multer from 'multer';
import path from 'path'
import moment from 'moment'
import userService from './data/userService.js';
import articleService from './data/articleService.js';
import userService from '../user/userService.js';
import articleService from './articleService.js';
const __dirname = path.resolve();
export const router = express.Router();
const upload = multer({
storage: multer.diskStorage({ // 저장한공간 정보 : 하드디스크에 저장
destination(req, file, done) { // 저장 위치
done(null, 'files'); // uploads라는 폴더 안에 저장
},
filename(req, file, done) { // 파일명을 어떤 이름으로 올릴지
const ext = path.extname(file.originalname); // 파일의 확장자
done(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확장자 이름으로 저장
}
}),
limits: { fileSize: 5 * 1024 * 1024 } // 5메가로 용량 제한
});
storage: multer.diskStorage({ // 저장한공간 정보 : 하드디스크에 저장
destination(req, file, done) { // 저장 위치
done(null, 'public/uploads/'); // uploads라는 폴더 안에 저장
},
filename(req, file, done) { // 파일명을 어떤 이름으로 올릴지
const ext = path.extname(file.originalname); // 파일의 확장자
done(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확장자 이름으로 저장
}
}),
limits: { fileSize: 5 * 1024 * 1024 } // 5메가로 용량 제한
});
router.post("/upload", upload.array("img"), async function(req, res, next) {
if(!req.session.sessionid){
// 세션이 없엉
}
router.post("/", upload.array("img"), async function (req, res, next) {
if (!req.session.sessionid) {
console.log("No session error");
return;
}
console.log("POST /article");
const inputTitle = req.body.title
const inputContent = req.body.content
const inputImage = req.files.map(el => el.path);
const useremail = req.session.sessionid.email
const author = await userService.findUserByEmail(useremail);
const useremail = req.session.sessionid.email
const author = await userService.findUserByEmail(useremail);
const inputkeyword = req.body.keyword
const inputlat = req.body.latitude
const inputlng = req.body.longitude
await articleService.createArticle({
title: inputTitle,
content: inputContent,
imageUrls: inputImage,
author: author,
title: inputTitle,
content: inputContent,
imageUrls: inputImage,
author: author,
keyword: inputkeyword,
latitude: inputlat,
longitude: inputlng,
comments: [],
likes: [],
});
console.log('saved.')
res.send();
res.send();
comments: [],
likes: [],
});
console.log('saved.')
res.send();
});
router.get("/article", async (req, res) => {
router.get("/", async (req, res) => {
console.log(path.join(process.cwd(), '/public'))
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
if (!req.session.sessionid) {
console.log("No session");
}
const articles = await articleService.findAllArticle();
articles.forEach((article) => {
article.imageUrls.forEach(
(urls) => {
try {
//res.sendFile(path.join(__dirname, urls));
}
catch (err) {
console.log(err)
}
});
});
res.send(JSON.stringify(articles));
});
router.get("/article/:id", async (req, res) => {
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
router.get("/:id", async (req, res) => {
if (!req.session.sessionid) {
console.log("No session");
}
const articles = await articleService.findArticleById(req.params.id);
res.send(JSON.stringify(articles));
}).delete("/:id", async (req, res) => {
if (!req.session.sessionid) {
console.log("No session - del");
}
const articles = await articleService.deleteArticle(req.params.id);
res.send(JSON.stringify(articles));
});
router.post("/comment/:id", async (req, res) => {
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
router.post("/:id/comment", async (req, res) => {
if (!req.session.sessionid) {
console.log("No session");
}
const user = await userService.findUserByEmail(req.session.sessionid.email);
const data = {
......@@ -94,42 +103,44 @@ router.post("/comment/:id", async (req, res) => {
res.send(JSON.stringify(articles));
})
router.delete("/comment/:articleid/:commentid", async (req, res) => {
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
router.delete("/:articleid/comment/:commentid", async (req, res) => {
if (!req.session.sessionid) {
console.log("No session");
}
const articles = await articleService.deleteComment(req.params.articleid, req.params.commentid);
res.send(JSON.stringify(articles));
});
router.post("/comment/:id", async (req, res) => {
router.put("/:id/like", async (req, res) => {
if (!req.session.sessionid) {
console.log("No session");
}
const user = await userService.findUserByEmail(req.session.sessionid.email);
const articles = await articleService.setLike(req.params.id, user._id)
res.send(JSON.stringify(articles));
});
router.get("/search/:keyword", async (req, res) => {
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
}
const user = await userService.findUserByEmail(req.session.sessionid.email);
const data = {
content: req.body.content,
author: user._id
}
const articles = await articleService.createComment(req.params.id, data);
console.log(req.params.keyword)
const articles = await articleService.findArticlesByKeyword(req.params.keyword);
res.send(JSON.stringify(articles));
})
});
router.put("/comment/like/:id", async (req, res) => {
router.get("/sort/:crit", async (req, res) => {
if(req.session.sessionid){
console.log("세션 O")
}
else {
console.log("세션 X")
}
const user = await userService.findUserByEmail(req.session.sessionid.email);
const articles = await articleService.setLike(req.params.id, user._id)
console.log(req.params.crit)
const articles = await articleService.findAllAndSortArticle(req.params.crit);
res.send(JSON.stringify(articles));
});
......
import Article from '../models/article.js';
import Article from './article.js';
const articleService = {
async createArticle(articleData) {
......@@ -23,6 +23,63 @@ const articleService = {
}
},
async findAllAndSortArticle(criteria) {
if (criteria==="time"){
try {
const articles = await Article
.find({})
.populate('author')
.populate('likes')
.sort({"createdAt": -1});
return articles;
} catch (err) {
throw err;
}
}
else if (criteria==="like") {
try {
const agg =
await Article.aggregate(
[
{ "$project":
{
"title": 1,
"content": 1,
"imageUrls": 1,
"author": 1,
"keyword": 1,
"latitude": 1,
"longitude": 1,
"comments": 1,
"likes": 1,
"createdAt": 1,
"length": { "$size": "$likes" }
}
},
{ "$sort": { "length": -1 } },
]
)
let articles = null;
const result = await Article.populate(agg, {path: "author"}).then(
(res)=>{return Article.populate(res, {path: "likes"})}
).then(
(res)=>{articles = res;}
);
return articles;
} catch (err) {
throw err;
}
}
else {
console.log("invalid criteria.");
return;
}
},
async findArticleById(articleId) {
try {
const article = await Article
......@@ -47,6 +104,18 @@ const articleService = {
throw err;
}
},
async findArticlesByKeyword(keyword) {
try {
const articles = await Article
.find({ keyword: keyword })
.populate('author')
.populate('likes');
return articles;
} catch (err) {
throw err;
}
},
async deleteArticle(articleId) {
try {
......
import express from 'express';
import moment from 'moment';
import userService from './data/userService.js';
import userService from '../user/userService.js';
export const router = express.Router();
......@@ -39,20 +39,21 @@ router.post('/login', async (req, res) => {
});
router.get("/logout", (req, res) => {
res.clearCookie('name');
if (req.session.sessionid) {
req.session.destroy((err) => {
if (err) {
console.log(err)
return;
}
});
res.clearCookie('name');
res.send(req.body.name);
} else {
res.send(req.body.name);
}
});
router.get("/check", (req, res) => {
router.get("/session", (req, res) => {
if(req.session.sessionid){
res.send(true);
}
......
......@@ -3,81 +3,6 @@ import mongoose from 'mongoose';
dotenv.config();
// const GoogleProviderSchema = new mongoose.Schema({
// id: {
// type: String,
// required: true,
// //unique: true,
// },
// profileUrl: {
// type: String,
// },
// });
// const UserSchema = new mongoose.Schema({
// nickname: {
// type: String,
// },
// email: {
// type: String,
// //unique: true,
// },
// google: {
// type: GoogleProviderSchema,
// }
// });
// const UserModel = mongoose.model("User", UserSchema);
// const CommentSchema = new mongoose.Schema({
// content: {
// type: String,
// required: true,
// },
// author: {
// type: UserSchema,
// required: true,
// },
// createdAt: {
// type: Date,
// default: Date.now,
// },
// });
// CommentSchema.index( { commentId: 1 } , { sparse: true } )
// const ArticleSchema = new mongoose.Schema({
// title: {
// type: String,
// required: true,
// },
// content: {
// type: String,
// required: true,
// },
// imageUrls: {
// type: [String],
// },
// author: {
// type: UserSchema,
// required: true,
// },
// comments: {
// type: [CommentSchema],
// unique: false,
// },
// likes: {
// type: [UserSchema],
// },
// createdAt: {
// type: Date,
// default: Date.now,
// },
// });
// ArticleSchema.index({articleId:1}, { sparse: true })
// const ArticleModel = mongoose.model("Article", ArticleSchema);
const connectDB = async () => {
const url = process.env.MONGODB_URI;
try {
......
......@@ -6,8 +6,8 @@ import cookieParser from 'cookie-parser';
import session from 'express-session';
import auth from './auth.js';
import post from './post.js';
import auth from './auth/authController.js';
import article from './article/articleController.js';
import connectDB from './db.js';
......@@ -38,7 +38,7 @@ app.use(
); //cors 설정을 한다..
// app.use(express.static(path.join(process.cwd(), '../public')));
app.use(express.static('files'))
app.use(express.static('public'));
app.use(cookieParser());
app.use(express.json());
......@@ -51,4 +51,4 @@ app.listen(PORT, () => {
app.use('/auth', auth);
app.use('/post', post);
\ No newline at end of file
app.use('/article', article);
\ No newline at end of file
File moved
import User from '../models/user.js';
import User from './user.js';
const userService = {
async createUser(userData) {
......
......@@ -88,7 +88,7 @@ async function requestLogout() {
async function requestCheckSession() {
const response = await axios({
url: 'http://localhost:8080/auth/check', // 통신할 웹문서
url: 'http://localhost:8080/auth/session', // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
......
......@@ -20,8 +20,8 @@ function Article({ data }) {
// http://localhost:8080/uploads/21701487062905.png
return (
<img
src={`http://localhost:8080/${el.replaceAll("\\", "/").substring(5)}`}
alt={`http://localhost:8080/${el.replaceAll("\\", "/").substring(5)}`}
src={`http://localhost:8080/${el.replaceAll("\\", "/").substring(7)}`}
alt={`http://localhost:8080/${el.replaceAll("\\", "/").substring(7)}`}
style={{ width: "100px", height: "100px" }} />
)
});
......
......@@ -24,7 +24,7 @@ function Comments({data}) {
function DeleteComment(e) {
const data = {id: _id}
axios
.delete(`http://localhost:8080/post/comment/${params.id}/${_id}`, data,
.delete(`http://localhost:8080/article/${params.id}/comment/${_id}`, data,
{
headers: {"Content-Type": 'application/json'}
})
......
......@@ -38,13 +38,29 @@ function Main() {
})
}, []);
const handleSortChange = (event) => {
console.log(event.target.value)
requestSortArticle(event.target.value)
.then((response) => {
console.log(response)
setArticleList(response.data)
})
};
return(
<div className="App">
<h1>식도락에 오신 것을 환영합니다. </h1>
<div className="introduction">
<p>식당 리뷰를 손쉽게 모아볼 있는 서비스</p>
</div>
<Button>검색</Button>
<div style={{display: 'flex'}}>
<Button>검색</Button>
<select id="addInputPrior" onChange={handleSortChange}>
<option value="time">최신순</option>
<option value="like">인기순</option>
</select>
</div>
{listItem}
</div>)
;
......@@ -52,10 +68,18 @@ function Main() {
async function requestLoadArticle() {
const response = await axios({
url: 'http://localhost:8080/post/article', // 통신할 웹문서
url: 'http://localhost:8080/article', // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
}
async function requestSortArticle(crit) {
const response = await axios({
url: `http://localhost:8080/article/sort/${crit}`, // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
}
export default Main;
......@@ -4,6 +4,7 @@ import Article from '../components/Article.js';
import { UserContext, ArticleContext } from '../Context.js';
import MapLocator from '../components/MapForLoaction.js';
import Comment from '../components/Comment.js';
import cookie from 'react-cookies';
import axios from 'axios';
axios.defaults.withCredentials = true;
......@@ -12,7 +13,7 @@ function PostRead() {
let params = useParams();
const userContext = useContext(UserContext);
const articleContext = useContext(ArticleContext);
const userinfo = cookie.load('name')
const [article, setArticle] = useState(null)
const [inputComment, setInputComment] = useState("")
const [commentList, setCommentList] = useState("")
......@@ -44,7 +45,7 @@ function PostRead() {
}, []);
function SetLike(){
axios.put(`http://localhost:8080/post/comment/like/${params.id}`)
axios.put(`http://localhost:8080/article/${params.id}/like`)
.then(res => {
alert("The comment is successfully uploaded");
return requestLoadArticleById(params.id)
......@@ -69,7 +70,7 @@ function PostRead() {
return
}
const data = {content: inputComment}
axios.post(`http://localhost:8080/post/comment/${params.id}`, data,
axios.post(`http://localhost:8080/article/${params.id}/comment`, data,
{
headers: {"Content-Type": 'application/json'}
})
......@@ -85,6 +86,26 @@ function PostRead() {
});
};
function DelButton({isThatYou, target}){
function deleteArticle(){
console.log(target._id)
requestDeleteArticleById(target._id).then(res => {
alert("The article is successfully deleted");
MoveTo('/')
})
.catch(err => {
console.error(err);
});
}
if (isThatYou) {
return(<button onClick={deleteArticle}>지우기</button>)
}
else {
return null
}
}
if (article) {
return(
<>
......@@ -92,7 +113,11 @@ function PostRead() {
<MapLocator loc={{lat: article.latitude, lng: article.longitude}} keyword={article.keyword}></MapLocator>
<div>
<Article data={article}></Article>
<button onClick={SetLike}>조와요</button>
<div style={{display: 'flex'}}>
<button onClick={SetLike}>조와요</button>
<DelButton isThatYou={userinfo.id === article.author.user_id} target={article}></DelButton>
</div>
</div>
</div>
<form onSubmit={onSubmit}>
......@@ -124,10 +149,18 @@ function PostRead() {
async function requestLoadArticleById(id) {
const response = await axios({
url: `http://localhost:8080/post/article/${id}`, // 통신할 웹문서
url: `http://localhost:8080/article/${id}`, // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
}
async function requestDeleteArticleById(id) {
const response = await axios({
url: `http://localhost:8080/article/${id}`, // 통신할 웹문서
method: 'delete', // 통신할 방식
});
return response;
}
export default PostRead;
\ No newline at end of file
......@@ -113,7 +113,7 @@ function PostWrite(){
formData.append("latitude", location.center.lat);
formData.append("longitude", location.center.lng);
axios
.post("http://localhost:8080/post/upload", formData,
.post("http://localhost:8080/article", formData,
{
headers: {"Content-Type": "multipart/form-data"}
})
......
......@@ -3,10 +3,13 @@ import React, { useState, useEffect, useContext } from 'react';
import {Routes, Route, Link, useNavigate, Navigate } from 'react-router-dom';
import SearchMap from '../components/SearchMapByKeyword.js'
import { UserContext } from '../Context.js';
import Article from '../components/Article.js';
import axios from 'axios';
axios.defaults.withCredentials = true;
const {kakao} = window;
function Search(props) {
const [articleList, setArticleList] = useState([])
const userContext = useContext(UserContext);
const navigate = useNavigate();
function MoveTo(link){
......@@ -24,21 +27,87 @@ function Search(props) {
if (!response.data) {
alert("세션이 만료되었습니다. 다시 로그인 바랍니다.")
MoveTo('/login')
}
}
else {
// requestLoadArticle()
// .then((response) => {
// console.log(response)
// setArticleList(response.data)
// })
}
})
.catch((response)=>{
console.log("error!:LogOut")
console.log(response)
})
});
}, []);
let listItem = [];
listItem = articleList.map((article)=>{
return(
<Article
key={article._id}
data={article}
></Article>
)
})
const onSubmit = e => {
e.preventDefault();
if (!location.keyword){
alert("검색대상을지정해주세요");
return
}
requestLoadArticleByKeyword(location.keyword)
.then((response) => {
console.log(response)
setArticleList(response.data)
})
};
return (
<div style={{display: 'flex'}}>
<div className="search">
<h1>검색페이지입니다.</h1>
<p>무엇을 드시고 싶나요? 어디 계시죠?</p>
<SearchMap loc={location} setLoc={setLocation}></SearchMap>
</div>
<div className="searchresult">
<p>선택한 키워드로 검색합니다.</p>
<form onSubmit={onSubmit} style={{display: 'flex'}}>
<input readonly value={location.keyword} type="text"></input>
<button type="submit">검색!</button>
<button type="button" onClick={()=>{
setLocation({
keyword: "",
center: { lat: null, lng: null }
})}}>선택 해제</button>
</form>
<h1>검색결과 {listItem.length} 확인</h1>
{listItem}
</div>
</div>
return (
<div className="search">
<h1>검색페이지입니다.</h1>
<SearchMap loc={location} setLoc={setLocation}></SearchMap>
</div>
);
}
async function requestLoadArticle() {
const response = await axios({
url: 'http://localhost:8080/article', // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
}
async function requestLoadArticleByKeyword(keyword) {
const response = await axios({
url: `http://localhost:8080/article/search/${keyword}`, // 통신할 웹문서
method: 'get', // 통신할 방식
});
return response;
}
export default Search;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment