From 1bc1c77838aeb566be6b427d67632b57a36e39e2 Mon Sep 17 00:00:00 2001 From: Lee Soobeom Date: Fri, 22 Jul 2022 23:53:47 +0900 Subject: [PATCH] fileinfo db save --- frontend/src/apis/post.api.ts | 14 ++-- frontend/src/post/editpost.tsx | 8 +-- frontend/src/post/intopost.tsx | 1 + frontend/src/post/posting.tsx | 51 ++++++++------ frontend/src/types/index.tsx | 2 +- src/controllers/fileinfo.controller.ts | 45 +++++++++++++ src/controllers/index.ts | 3 +- src/controllers/post.controller.ts | 92 ++++++++++++++------------ src/db/post.db.ts | 26 +++++++- src/db/user.db.ts | 26 ++++---- src/models/fileinfo.model.ts | 8 +-- src/models/index.ts | 2 +- src/models/post.model.ts | 7 +- src/routes/index.ts | 1 - src/routes/post.route.ts | 4 +- 15 files changed, 186 insertions(+), 104 deletions(-) create mode 100644 src/controllers/fileinfo.controller.ts diff --git a/frontend/src/apis/post.api.ts b/frontend/src/apis/post.api.ts index f96f89d..be313f7 100644 --- a/frontend/src/apis/post.api.ts +++ b/frontend/src/apis/post.api.ts @@ -2,8 +2,8 @@ import axios from "axios"; import baseUrl from "./baseUrl"; import { PostType } from "../types"; -export const posting = async (post: PostType) => { - const { data } = await axios.post(`${baseUrl}/posts/`, post); +export const createImgAndPost = async (formdata: FormData) => { + const { data } = await axios.post(`${baseUrl}/posts/`, formdata); return data; }; @@ -12,6 +12,11 @@ export const getData = async () => { return data; }; +export const getImgData = async (name: string) => { + const { data } = await axios.get(`/images/${name}`); + return data; +}; + export const addCounts = async (_id: string, counts: number) => { const { data } = await axios.post(`${baseUrl}/posts/${_id}`, { counts: counts + 1, @@ -33,8 +38,3 @@ export const updating = async (post: PostType) => { const { data } = await axios.put(`${baseUrl}/posts/${post._id}`, post); return data; }; - -export const postImg = async (formdata: FormData) => { - const { data } = await axios.post(`${baseUrl}/posts`, formdata); - return data; -}; diff --git a/frontend/src/post/editpost.tsx b/frontend/src/post/editpost.tsx index 203fbd1..bbff90e 100644 --- a/frontend/src/post/editpost.tsx +++ b/frontend/src/post/editpost.tsx @@ -8,8 +8,8 @@ import { postApi } from "../apis"; import { PostState } from "./intopost"; export function EditPost() { - const [city, setCity] = useState("질문종류"); - const [theme, setTheme] = useState("질문종류"); + const [city, setCity] = useState("city"); + const [theme, setTheme] = useState("theme"); const [title, setTitle] = useState(""); const [text, setText] = useState(""); const navigate = useNavigate(); @@ -65,10 +65,10 @@ export function EditPost() { setError("내용을 입력해 주세요."); return false; } else if (equals(city, "city")) { - setError("테마를 선택해 주세요."); + setError("도시를 선택해 주세요."); return false; } else if (equals(theme, "theme")) { - setError("도시를 선택해 주세요."); + setError("테마를 선택해 주세요."); return false; } else { return true; diff --git a/frontend/src/post/intopost.tsx b/frontend/src/post/intopost.tsx index 03d9943..e48d783 100644 --- a/frontend/src/post/intopost.tsx +++ b/frontend/src/post/intopost.tsx @@ -59,6 +59,7 @@ export function IntoPost() { +
files
{post.text}
diff --git a/frontend/src/post/posting.tsx b/frontend/src/post/posting.tsx index d2f693f..f86de42 100644 --- a/frontend/src/post/posting.tsx +++ b/frontend/src/post/posting.tsx @@ -12,7 +12,7 @@ export default function Posting() { const [theme, setTheme] = useState("theme"); const [title, setTitle] = useState(""); const [text, setText] = useState(""); - const [file, setFile] = useState(); + const [file, setFile] = useState(); const [imgSrc, setImgSrc] = useState(); const navigate = useNavigate(); @@ -32,35 +32,39 @@ export default function Posting() { const [disabled, setDisabled] = useState(false); const [success, setSuccess] = useState(false); - useEffect(() => { - console.log("uploaded imgs", imgSrc); - }, [imgSrc]); - const imgArr = new Array(); const sendImg2Db = async (filelist: FileList) => { const formdata = new FormData(); + formdata.append("title", user.title); + formdata.append("text", user.text); + formdata.append("theme", user.theme); + formdata.append("city", user.city); if (!(filelist === undefined || filelist === null)) { for (var i = 0; i < filelist.length; i++) { - formdata.append(`picture${i}`, filelist?.[i]); + formdata.append("picture", filelist?.[i]); } - console.log("formdata", formdata); - await postApi.postImg(formdata); + // formdata: post, imgs + const res = await postApi.createImgAndPost(formdata); } + // else { + // const res = await postApi.createImgAndPostTable(formdata); + // } }; async function handlePostSubmit(event: FormEvent) { event.preventDefault(); - try { setError(""); console.log("user data", user); - if (postingFormMatch()) { + if (postingFormMatch(user)) { setLoading(true); - // sendImg2Db(); - const res = await postApi.posting(user); - console.log("서버연결됬나요", res); - // console.log("user save"); + if (file) { + sendImg2Db(file); + } + // const res = await postApi.posting(user); + // console.log("서버연결됬나요", res); + navigate("/board", { replace: true }); setSuccess(true); setError(""); @@ -73,19 +77,19 @@ export default function Posting() { } } - function postingFormMatch() { + function postingFormMatch(user: PostType) { if (!isLength(user.title ?? "", { min: 1 })) { setError("제목을 입력해 주세요."); return false; } else if (!isLength(user.text ?? "", { min: 1 })) { setError("내용을 입력해 주세요."); return false; - } else if (equals(city, "질문종류")) { - setError("테마를 선택해 주세요."); - return false; - } else if (equals(theme, "질문종류")) { + } else if (equals(city, "city")) { setError("도시를 선택해 주세요."); return false; + } else if (equals(theme, "theme")) { + setError("테마를 선택해 주세요."); + return false; } else { return true; } @@ -124,10 +128,13 @@ export default function Posting() { }; const handleInputPic = async (event: React.ChangeEvent) => { - event.preventDefault(); - const maxImg = 10; const { files } = event.target; + + if (!(files === null)) { + setFile(files); + } + if (!(files?.length === undefined)) { if (files?.length <= maxImg) { for (var i = 0; i < files.length; i++) { @@ -140,7 +147,7 @@ export default function Posting() { }; } } else { - alert("사진은 최대 10장까지 업로드 가능합니다!"); + alert(`사진은 최대 ${maxImg}장까지 업로드 가능합니다!`); } } }; diff --git a/frontend/src/types/index.tsx b/frontend/src/types/index.tsx index 0047911..a70fe6d 100644 --- a/frontend/src/types/index.tsx +++ b/frontend/src/types/index.tsx @@ -16,7 +16,7 @@ export interface PostType { text: string; theme: string; city: string; - date: string | number; + date: string; counts: number; _id: string; user: string; diff --git a/src/controllers/fileinfo.controller.ts b/src/controllers/fileinfo.controller.ts new file mode 100644 index 0000000..a3d1c43 --- /dev/null +++ b/src/controllers/fileinfo.controller.ts @@ -0,0 +1,45 @@ +import formidable from "formidable"; +import { asyncWrap } from "../helpers/asyncWrap"; +import { TypedRequest } from "../types"; + +export const uploadFile = asyncWrap(async (reqExp, res, next) => { + const req = reqExp as TypedRequest; + const form = formidable({ multiples: false, uploadDir: "uploads" }); + + await new Promise((resolve, reject) => { + form.parse(req, (err, fields, files) => { + if (err) { + reject(err); + return; + } + console.log("fields", fields); + console.log("files", files); + req.body = fields; + req.files = files; + resolve(files); + }); + }); + next(); + return; +}); + +export const uploadFiles = asyncWrap(async (reqExp, res, next) => { + const req = reqExp as TypedRequest; + const form = formidable({ multiples: true, uploadDir: "uploads" }); + + await new Promise((resolve, reject) => { + form.parse(req, (err, fields, files) => { + if (err) { + reject(err); + return; + } + console.log("fields", fields); + console.log("files", files); + req.body = fields; + req.files = files; + resolve(files); + }); + }); + next(); + return; +}); diff --git a/src/controllers/index.ts b/src/controllers/index.ts index cf8e06e..c819db5 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,5 +1,6 @@ export * as authCtrl from "./auth.controller"; +export * as fileInfoCtrl from "./fileinfo.controller"; +export * as mainimgCtrl from "./mainimg.controller"; export * as postCtrl from "./post.controller"; export * as roleCtrl from "./role.controller"; export * as userCtrl from "./user.controller"; -export * as mainimgCtrl from "./mainimg.controller"; \ No newline at end of file diff --git a/src/controllers/post.controller.ts b/src/controllers/post.controller.ts index 41ed408..69af382 100644 --- a/src/controllers/post.controller.ts +++ b/src/controllers/post.controller.ts @@ -1,62 +1,68 @@ import { NextFunction, Request, Response } from "express"; -import isLength from "validator/lib/isLength"; -import equals from "validator/lib/equals"; +import formidable, { Fields, Files } from "formidable"; import { TypedRequestAuth } from "./auth.controller"; import { asyncWrap } from "../helpers"; import { postDb, userDb } from "../db"; import { TypedRequest } from "../types"; -export const postCreate = asyncWrap(async (reqExp, res, next) => { +export const createImgAndPost = asyncWrap(async (reqExp, res, next) => { const req = reqExp as TypedRequestAuth<{ userId: string }>; + // const { date, counts } = req.body as { + // date: ""; + // counts: 0; + // }; - const { title, text, theme, city, date } = req.body as { - title: string; - text: string; - theme: string; - city: string; - date: Date; - counts: number; - }; - - // 1) title 빈 문자열인지 확인 - if (!isLength(title ?? "", { min: 1 })) { - return res.status(422).send("제목을 한 글자 이상 입력해주세요"); - } - - // 2) body 빈 문자열인지 확인 - if (!isLength(text ?? "", { min: 1 })) { - return res.status(422).send("제목을 한 글자 이상 입력해주세요"); - } - - // 3) theme dropdown default-value "테마"일 경우 에러 - if (equals(theme, "질문종류")) { - return res.status(422).send("테마를 입력해 주세요"); - } + const { userId } = req.auth; - // 4) city dropdown default-value "도시"일 경우 에러 - if (equals(city, "질문종류")) { - return res.status(422).send("도시를 선택해 주세요"); - } - - const userId = req.auth.userId; - - const newPost = await postDb.createPost({ - title, - text, - theme, - city, - date: Date.now(), - user: userId, + const form = formidable({ + uploadDir: "uploads", + keepExtensions: true, + multiples: true, }); - console.log("post", newPost); + const fileIdArr = new Array(); + + form.parse(req, (err, fields, files) => { + const title = fields.title?.[0]; + const text = fields.text?.[0]; + const theme = fields.theme?.[0]; + const city = fields.city?.[0]; + + if (Array.isArray(files.picture)) { + for (var i = 0; i < files.picture.length; i++) { + if (!(files.picture?.[i].originalFilename === null)) { + const originalfilename = files.picture?.[i].originalFilename; + const newfilename = files.picture?.[i].newFilename; + const filepath = files.picture?.[i].filepath; + + const filesRes = postDb.createFilesRow( + originalfilename, + newfilename, + filepath + ); + } + } + } else { + console.log("업로드한 사진이 없습니다."); + } + + const postRes = postDb.createPostRow({ + title, + text, + theme, + city, + date: Date.now(), + counts: 0, + user: userId, + }); - return res.json(newPost); + console.log("createPostRow", postRes); + }); }); export const getAllPost = asyncWrap(async (req, res) => { const posts = await postDb.getPosts(); - // console.log(posts); + console.log(posts); return res.json(posts); }); diff --git a/src/db/post.db.ts b/src/db/post.db.ts index 4c89712..1b16277 100644 --- a/src/db/post.db.ts +++ b/src/db/post.db.ts @@ -1,7 +1,8 @@ import { Post, PostType } from "../models"; +import { FileInfo, IFileInfo } from "../models"; -export const createPost = async (post: PostType) => { - const newPosting = await Post.create({ +export const createPostRow = async (post: PostType) => { + const newPostRow = await Post.create({ title: post.title, text: post.text, theme: post.theme, @@ -10,7 +11,26 @@ export const createPost = async (post: PostType) => { date: post.date, counts: 0, }); - return newPosting; + console.log("check", newPostRow); + return newPostRow; +}; + +export const createFilesRow = async ( + originalfilename?: string | null, + newfilename?: string, + picturepath?: string +) => { + const newFileRow = await FileInfo.create({ + originalfilename: originalfilename, + newfilename: newfilename, + picturepath: picturepath, + }); + return newFileRow; +}; + +export const findFileByPostInfo = async (title: string, usreId: string) => { + const posts = await Post.find({ title: title, userId: usreId }); + return posts; }; export const getPosts = async () => { diff --git a/src/db/user.db.ts b/src/db/user.db.ts index f05c955..0cb5a43 100644 --- a/src/db/user.db.ts +++ b/src/db/user.db.ts @@ -1,12 +1,12 @@ import bcrypt from "bcryptjs"; import { ObjectId } from "mongoose"; -import { IUser, Role, Post, User, Avatar } from "../models"; -import fs from "fs"; +import { IUser, Role, Post, User, FileInfo } from "../models"; +import fs from "fs/promises"; export const createUser = async (user: IUser) => { // 비밀번호 암호화 const hash = await bcrypt.hash(user.password, 10); - const newAvatar = await Avatar.create({}); + const newAvatar = await FileInfo.create({}); // 사용자 역할 추가: 기본값은 "user" let userRole = null; if (user.role) { @@ -82,17 +82,17 @@ export const postPicture = async ( if (!(profile?.avatar === undefined)) { if (originalfilename === null) { - await Avatar.findByIdAndUpdate(profile.avatar._id, { + await FileInfo.findByIdAndUpdate(profile.avatar._id, { nickname: nickname, }); } else if (nickname === "") { - await Avatar.findByIdAndUpdate(profile.avatar._id, { + await FileInfo.findByIdAndUpdate(profile.avatar._id, { originalfilename: originalfilename, newfilename: newfilename, picturepath: picturepath, }); } else { - await Avatar.findByIdAndUpdate(profile.avatar._id, { + await FileInfo.findByIdAndUpdate(profile.avatar._id, { originalfilename: originalfilename, newfilename: newfilename, picturepath: picturepath, @@ -102,14 +102,12 @@ export const postPicture = async ( } }; -export const deleteUser = async (userId: ObjectId) => { +export const deleteUser = async (userId: string) => { const user = await User.findById(userId); - if (!(user?.avatar === undefined)) { - const ref = await Avatar.findById(user.avatar._id); - fs.unlink("../travel/uploads/" + ref?.newfilename, (err) => { - console.log(err); - }); - await Avatar.deleteOne({ _id: user.avatar._id }); - await User.deleteOne({ _id: userId }); + if (user && user.avatar) { + const file = await FileInfo.findById(user.avatar._id); + await fs.unlink("../travel/uploads/" + file?.newfilename); + await FileInfo.deleteOne({ _id: user.avatar._id }); + return await user.deleteOne(); } }; diff --git a/src/models/fileinfo.model.ts b/src/models/fileinfo.model.ts index b6a071f..fc05cc8 100644 --- a/src/models/fileinfo.model.ts +++ b/src/models/fileinfo.model.ts @@ -1,17 +1,17 @@ import { model, Schema } from "mongoose"; -export interface IAvatar { +export interface IFileInfo { originalfilename?: string; newfilename?: string; picturepath?: string; nickname?: string; } -const Avatarschema = new Schema({ - originalfilename: { type: String, unique: true }, +const schema = new Schema({ + originalfilename: { type: String }, newfilename: { type: String }, nickname: { type: String }, picturepath: { type: String }, }); -export default model("Avatar", Avatarschema); +export default model("FileInfo", schema); diff --git a/src/models/index.ts b/src/models/index.ts index 821cab3..9154562 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,5 +1,5 @@ export { default as User, IUser } from "./user.model"; export { default as Post, PostType } from "./post.model"; export { default as Role } from "./role.model"; -export { default as Avatar, IAvatar } from "./fileinfo.model"; +export { default as FileInfo, IFileInfo } from "./fileinfo.model"; export { default as Mainimg, MainimgType } from "./mainimg.model"; diff --git a/src/models/post.model.ts b/src/models/post.model.ts index e482605..1570fd0 100644 --- a/src/models/post.model.ts +++ b/src/models/post.model.ts @@ -5,9 +5,10 @@ export interface PostType { text: string; theme: string; city: string; - user: Types.ObjectId | string; date: Date | number; counts?: number; + user: Types.ObjectId | string; + file?: Types.ObjectId | string; } const PostSchema = new Schema({ @@ -37,6 +38,10 @@ const PostSchema = new Schema({ type: Number, default: 0, }, + file: { + type: Schema.Types.ObjectId, + ref: "FileInfo", + }, }); export default model("Post", PostSchema); diff --git a/src/routes/index.ts b/src/routes/index.ts index 7102fb4..8776bc9 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -12,6 +12,5 @@ router.use("/auth", authRouter); router.use("/posts", postRouter); router.use("/profile", profileRouter); router.use("/mainimg", mainimgRouter); -//posting함수 -> mongodb에 posts json형식으로 저장 export default router; diff --git a/src/routes/post.route.ts b/src/routes/post.route.ts index c0108be..71aebe8 100644 --- a/src/routes/post.route.ts +++ b/src/routes/post.route.ts @@ -1,9 +1,9 @@ import express from "express"; -import { postCtrl, authCtrl } from "../controllers"; +import { postCtrl, authCtrl, fileInfoCtrl } from "../controllers"; const router = express.Router(); -router.route("/").post(authCtrl.requireLogin, postCtrl.postCreate); +router.route("/").post(authCtrl.requireLogin, postCtrl.createImgAndPost); router.route("/").get(postCtrl.getAllPost); router -- GitLab