You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
11 KiB

12 months ago
  1. const repl = require("repl");
  2. const boardController = {};
  3. /**
  4. * 게시판 정보를 가져옵니다.
  5. */
  6. boardController.getBoardInfo = async(req, res) => {
  7. const boardKey = req.params?.boardKey ?? ''
  8. // 게시판 키가 올바르게 넘어왔는지 확인합니다.
  9. if(! boardKey) {
  10. return res.status(400).json({error:'존재 하지 않거나 삭제된 게시판입니다.'});
  11. }
  12. // 게시판 모델 불러오기
  13. const boardModel = loadModule('board', 'model')
  14. // 게시판 정보 가져오기
  15. const boardInfo = await boardModel.getBoard(boardKey)
  16. if(!boardInfo || boardInfo === {} )
  17. {
  18. return res.status(400).json({error:'존재 하지 않거나 삭제된 게시판입니다.'})
  19. }
  20. return res.json({result: boardInfo})
  21. }
  22. boardController.getPost = async(req, res) => {
  23. const postId = req.params?.postId
  24. const boardModel = loadModule('board', 'model')
  25. const result = await boardModel.getPostOne(postId)
  26. if(result) {
  27. // 문자열 형태로 되어있는 attach_list는 JSON형태로 변환해준다.
  28. result.attach_list = JSON.parse(result.attach_list);
  29. result.is_notice = result.is_notice === 'Y'
  30. }
  31. return res.json({result})
  32. }
  33. boardController.getPostList = async(req, res) => {
  34. const params = {}
  35. params.key = req.params?.boardKey ?? ''
  36. params.page = req.query?.page ?? 1
  37. params.page_rows = req.query?.page_rows ?? 10
  38. params.searchColumn = req.query?.searchColumn ?? ''
  39. params.searchQuery = req.query?.searchQuery ?? ''
  40. // 게시판 모델 불러오기
  41. const boardModel = loadModule('board', 'model')
  42. // 먼저 공지글 부터 불러온다
  43. params.isNotice = true;
  44. const noticeList = await boardModel.getPost(params);
  45. // 일반 게시글을 불러온다.
  46. params.isNotice = false;
  47. const list = await boardModel.getPost(params);
  48. // 반환할 객체를 반든다.
  49. const result = {
  50. result: [...noticeList.result, ...list.result], // 공지사항 목록과 일반게시글 목록을 합친다.
  51. totalCount: list.totalCount
  52. }
  53. return res.json(result)
  54. }
  55. /**
  56. * 게시글 작성 / 수정 / 답글 처리
  57. */
  58. boardController.writeBoardPost = async(req, res) => {
  59. const boardKey = req.params?.boardKey ?? ""
  60. let postId = req.params?.postId ?? 0
  61. postId = postId * 1;
  62. const postParentId = req.body?.parent_id * 1 ?? 0
  63. // 넘어온 데이타에 따라 수정인지 신규인지 답글인지 미리 정의해둔다
  64. const isEdit = postId > 0
  65. const isReply = postId === 0 && postParentId > 0
  66. // 저장할 데이타를 먼저 정리한다.
  67. const updateData = {};
  68. updateData.title = req.body?.title ?? ''
  69. updateData.content = req.body?.content ?? ''
  70. updateData.category = req.body?.category ?? ''
  71. updateData.author_name = req.body?.author_name ?? ''
  72. updateData.author_pass = req.body?.author_pass ?? ''
  73. updateData.is_notice = req.body?.is_notice === true ? 'Y' : 'N'
  74. updateData.updated_at = new Date()
  75. updateData.updated_user = req.loginUser.id
  76. updateData.updated_ip = req.loginUser.ip
  77. // 첨부파일 목록
  78. const attach_list = req.body?.attach_list ?? []
  79. updateData.attach_list = JSON.stringify(attach_list);
  80. // 게시판 모델 불러오기
  81. const boardModel = loadModule('board','model');
  82. // 답글이거나, 수정일 경우 원본 글을 가져와야 한다.
  83. let refData = null;
  84. if(isEdit) {
  85. refData = await boardModel.getPostOne(postId);
  86. if(! refData) {
  87. return res.status(400).json({error:'수정하려는 원본글이 존재하지 않거나 이미 삭제되었습니다.'})
  88. }
  89. }
  90. else if (isReply) {
  91. refData = await boardModel.getPostOne(postParentId);
  92. if(! refData) {
  93. return res.status(400).json({error:'답글을 작성하려는 원본글이 존재하지 않거나 이미 삭제되었습니다.'})
  94. }
  95. }
  96. // 로그인된 사용자의 경우, 비밀번호와 작성자이름은 비워둔다.
  97. if(req.loginUser.id > 0) {
  98. updateData.author_name = ''
  99. updateData.author_pass = ''
  100. }
  101. // 비로그인 사용자의 경우, 이름과 비밀번호를 작성하였는지 체크한다.
  102. else {
  103. if(updateData.author_name.length === 0) {
  104. return res.status(400).json({error:'닉네임을 입력하셔야 합니다.'});
  105. }
  106. if(updateData.author_pass.length === 0) {
  107. return res.status(400).json({error:'비밀번호를 입력하셔야 합니다.'});
  108. }
  109. // 입력받은 비밀번호를 암호화 한다.
  110. updateData.author_pass = require('sha256')(require('md5')(appConfig.secretKey + updateData.author_pass))
  111. // 수정글인 경우 입력한 비밀번호와 기존 글의 비밀번호가 동일한지 확인한다.
  112. if(isEdit) {
  113. if (updateData.author_pass !== refData?.author_pass) {
  114. return res.status(400).json({error:'수정하려는 글의 비밀번호가 맞지않습니다.'})
  115. }
  116. }
  117. }
  118. // 신규등록일 경우 필수 정보를 추가해준다.
  119. if(! isEdit) {
  120. updateData.board_key = boardKey;
  121. updateData.type = req.body?.type ?? 'POST' // DEFAULT 로 'POST' 입력, 댓글일경우 명시해줘야함
  122. updateData.created_at = updateData.updated_at
  123. updateData.created_user = updateData.updated_user
  124. updateData.created_ip = updateData.updated_ip
  125. if(isReply) {
  126. updateData.parent_id = postParentId
  127. }
  128. }
  129. // 첨부된 파일중 이미지 파일이 있다면 썸네일로 지정함
  130. updateData.thumbnail = ''
  131. // 첨부파일 배열을 돌면서 검사
  132. for(let i=0; i<attach_list.length; i++) {
  133. if(attach_list[i].isImage) {
  134. // 해당 첨부파일이 이미지이면 첨부파일로 값을 넣고, for문 break;
  135. updateData.thumbnail = attach_list[i].file_url;
  136. break;
  137. }
  138. }
  139. updateData.attach_count = attach_list.length;
  140. // 데이타베이스 처리 객체
  141. const db = database()
  142. // 멀티뎁스 게시판을 위한 처리
  143. // 신규작성일때만 처리한다.
  144. if(! isEdit)
  145. {
  146. // 부모게시글이 없다면?
  147. if(! isReply) {
  148. updateData.reply = ''
  149. // num은 게시글의 가장 큰 num 값을 가져와 1을 더한다.
  150. updateData.num = 1
  151. await db('tbl_board_posts').max('num', {as: 'max'})
  152. .then((rows) => {
  153. if(rows && rows[0]) {
  154. updateData.num = (rows[0]?.max ?? 0) + 1
  155. }
  156. })
  157. }
  158. else {
  159. // num은 부모의 num을 그대로 따른다.
  160. updateData.num = refData.num
  161. // reply를 계산하자. reply의 컬럼 길이가 10이므로 최대 10단계까지 답변가능
  162. if(refData.reply.length >= 10) {
  163. return res.status(400).json({error:'더 이상 답변할 수 없습니다. 답변은 10단계 까지만 가능합니다.'})
  164. }
  165. const replyLen = refData.reply.length + 1;
  166. const begin_reply_char = 'A';
  167. const end_reply_char = 'Z';
  168. let replyChar = begin_reply_char;
  169. let query = `SELECT MAX(SUBSTRING(reply, ${replyLen}, 1)) AS reply FROM tbl_board_posts WHERE board_key = ? AND num = ? AND SUBSTRING(reply, ${replyLen}, 1) <> '' `
  170. let bindList = [boardKey, refData.num]
  171. if(replyLen >0) {
  172. query += " AND reply LIKE ? "
  173. bindList.push( refData.reply + '%' );
  174. }
  175. await db.raw(query, bindList)
  176. .then(rows => {
  177. if(rows && rows[0]) {
  178. if(rows[0].reply === end_reply_char ) {
  179. return res.status(500).json({error: '더 이상 답변할 수 없습니다. 답변은 26개까지만 가능합니다.'})
  180. }
  181. else if (rows[0].reply){
  182. replyChar = String.fromCharCode(rows[0].reply.charCodeAt(0) + 1);
  183. }
  184. }
  185. })
  186. }
  187. }
  188. // 실제 DB 입력처리
  189. try {
  190. if(isEdit) {
  191. await db('tbl_board_posts').where('id', postId).update(updateData);
  192. }
  193. else {
  194. await db('tbl_board_posts').insert(updateData).then(id => {
  195. postId = id;
  196. });
  197. }
  198. }
  199. catch {
  200. return res.status(500).json({error:'DB 입력도중 오류가 발생하였습니다.'});
  201. }
  202. return res.status(200).json({result: {id: postId}})
  203. }
  204. /**
  205. * 게시판 조회수 처리하기
  206. */
  207. boardController.increasePostHit = async(req, res) => {
  208. // 패러미터에서 값을 받습니다.
  209. const boardKey = req.params?.boardKey ?? ''
  210. const postId = (req.params?.postId?? 0) * 1
  211. console.log(boardKey, postId);
  212. if(! boardKey || postId < 0) {
  213. // 조회수를 올리는 작업의 경우 실패하여도 사용자에겐 보이지 않도록 silent 하게 처리해야하므로 오류일 경우에도 STATUS 200처리.
  214. return res.json({})
  215. }
  216. try {
  217. const db = database()
  218. await db.raw("UPDATE tbl_board_posts SET `hit` = `hit` + 1 WHERE id = ?", [postId]);
  219. }
  220. catch {}
  221. return res.json({})
  222. }
  223. /**
  224. * 게시글 삭제하기
  225. */
  226. boardController.deletePost = async(req, res) => {
  227. // 패러미터에서 값을 받습니다.
  228. const boardKey = req.params?.boardKey ?? ''
  229. const postId = (req.params?.postId?? 0) * 1
  230. // 먼저 원본 게시글 데이타를 가져옵니다.
  231. const boardModel = loadModule('board', 'model');
  232. const original = boardModel.getPostOne(postId);
  233. // 원본글이 없거나 이미 삭제된 글의 경우 처리
  234. if(! original || original.status === 'N' ) {
  235. return res.status(400).json({error: '존재하지 않거나 이미 삭제된 글입니다.'});
  236. }
  237. // 삭제 권한을 체크합니다.
  238. if(this.loginUser.auth < 10 ) {
  239. // 관리자 권한이 아닌경우
  240. if( original.created_user === 0 && this.loginUser.id === 0 )
  241. {
  242. // 작성자도 비회원, 현재 사용자도 비회원일경우 비밀번호 체크
  243. const password = req.body?.password ?? ''
  244. const encryptedPassword = require('sha256')(require('md5')(appConfig.secretKey + password))
  245. // 비밀번호가 다를경우
  246. if(encryptedPassword !== original.author_pass) {
  247. return res.status(400).json({error :'해당 글을 삭제할 권한이 없습니다.'});
  248. }
  249. }
  250. else if (original.created_user !== this.loginUser.id)
  251. {
  252. // 작성자와 현재 로그인 회원이 다른경우
  253. return res.status(400).json({error :'해당 글을 삭제할 권한이 없습니다.'});
  254. }
  255. }
  256. // STATUS를 N 처리해준다.
  257. try {
  258. const db = database()
  259. await db(boardModel.postTableName)
  260. .where('id', postId)
  261. .update({
  262. status: 'N',
  263. updated_at: new Date(),
  264. updated_user: this.loginUser.id,
  265. updated_ip : this.loginUser.ip``
  266. })
  267. }
  268. catch {
  269. return res.status(500).json({error:'DB 에러'});
  270. }
  271. return res.json({})
  272. }
  273. module.exports = boardController