Files
JMuseum/routes/submit_theme.js
2018-02-26 14:09:18 +08:00

198 lines
8.1 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fileSystem = require("fs");
const router = require("express").Router();
const multer = require("multer");
const Jimp = require("jimp");
const dataRender = require("../models/DataRender");
const NewTheme = require("../models/mongooseSchemas/NewTheme");
/**
* 頁面「投稿主題」的路由處理
*/
router.get("/newtheme", (req, res) => {
let user = req.user;
// 如果使用者有登入
if (user) {
// 根據使用者是否已經有投稿過新主題,來決定要給出訊息頁面或「主題投稿」頁面
let source = user.hasPostNewTheme ? "message_form" : "submit_theme";
dataRender.DataRender(source, req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render(source, dataObj);
}
});
}
// 若使用者沒有登入,則重新導向到登入頁面
else {
res.redirect("/login");
}
});
/**
* 透過Multer建立一個「上傳者」物件將圖片暫存在專案目錄下的"temp"資料夾中。
* 最大檔案上傳量限制為1檔案大小限制為128KB。
*/
const Uploader = multer({
dest: "./temp",
limits: { fileSize: 131072, files: 1 }
});
/**
* 檢查由客戶端傳送過來的評分資料。經檢查後無錯誤則繼續至下一個程序;若有錯誤則回送錯誤訊息。
* @param {Express.Request} req Express的Request物件。
* @param {Express.Response} res Express的Response物件。
* @param {Function} next 導向函式。
*/
function NewTheme_CheckTextField(req, res, next) {
// 確認使用者是否有登入。若沒有登入,則回送錯誤訊息
if (!req.user) {
return res.json({isOK: false, field: "SERVER", message: "尚未登入。請登入後再執行投稿主題的動作。"});
}
// 確認使用者是否已有發起過主題。若有發起過主題,則回送錯誤訊息
if (req.user.hasPostFeedback) {
return res.json({isOK: false, field: "SERVER", message: "您已經投稿過主題,請在下一季時再進行投稿動作。"});
}
// 檢查「主題名稱」
req.checkBody("theme")
.notEmpty()
.withMessage("「主題名稱」為必要填寫的欄位。請輸入您想要的主題名稱。")
.isLength({max: 32, min: 1})
.withMessage("請在欄位「主題名稱」中輸入1~32字間的主題名稱。")
.matches(/^[^.<>/\\]+$/)
.withMessage("欄位「主題名稱」中請勿包含「.<>/\\」非法字元。");
// 檢查「敘述」
req.checkBody("narrative")
.notEmpty()
.withMessage("「敘述」為必要填寫的欄位。請輸入對於此主題的相關說明。")
.isLength({max: 100, min: 8})
.withMessage("請在欄位「敘述」中輸入8~100字間的主題敘述。");
// 取得驗證結果
req.getValidationResult().then((result) => {
let errors = result.mapped();
if (result.isEmpty()) {
next();
}
else {
let firstErr = Object.values(errors)[0];
res.json({isOK: false, field: firstErr.param, message: firstErr.msg});
}
});
}
/**
* 檢查由客戶端傳送過來的評分資料。經檢查後無錯誤則繼續至下一個程序;若有錯誤則回送錯誤訊息。
* @param {Express.Request} req Express的Request物件。
* @param {Express.Response} res Express的Response物件。
* @param {Function} next 導向函式。
*/
function NewTheme_CheckImage(req, res, next) {
let file = req.file;
// 若使用者沒有上傳圖片,則回送錯誤訊息
if (!file) {
return res.json({ isOK: false, message: "請選擇小於等於128KB且類型為png或jpeg的正方形圖檔。" });
}
// 若使用者上傳的圖片檔案格式不為png或jpeg則回送錯誤訊息。
else if (file.mimetype != "image/png" && file.mimetype != "image/jpeg") {
fileSystem.unlink(file.path, (err) => { if (err) console.log(err); });
return res.json({ isOK: false, message: "選擇的活動圖示之檔案格式必須為png或jpeg。" });
}
// 讀取圖片,檢視其圖片是否為 1:1 比例
Jimp.read(file.path, (err, img) => {
if (err) return res.json({ isOK: false, message: "伺服器內部錯誤,請稍後再嘗試。" });
// 若為正方形圖示,則繼續下一個程序
if (img.bitmap.height == img.bitmap.width) {
next();
}
// 若不是,則回送錯誤訊息
else {
res.json({ isOK: false, field: "image", message: "選擇的活動圖示其大小比例必須為1:1。" })
}
});
}
/**
* 儲存新的活動資料並將成功訊息回送。
* @param {Express.Request} req Express的Request物件。
* @param {Express.Response} res Express的Response物件。
*/
function NewTheme_SaveDataAndResponse(req, res) {
let body = req.body; // 資料主體
let file = req.file; // 檔案訊息資料
let newFileName = body.theme + (file.mimetype == "image/png" ? ".png" : ".jpg"); // 新檔案的檔案名稱
let outURLPath = "/images/newtheme/" + newFileName; // 對外的檔案路徑表示
let newFilePath = "./public" + outURLPath; // 新檔案的存擋路徑
// 建立基本資料
let data = {
title: body.theme,
narrative: body.narrative,
image: outURLPath,
sponsor: req.user.username
};
// 以基本資料建立新的主題
NewTheme.createNew_NewTheme(data, (err, _id) => {
// 若錯誤為「已有相同的主題名稱」,則回呼錯誤
if (NewTheme.IsError_HaveSameThemeTitle(err))
return res.json({isOK: false, field: "theme", message: "已經有其他使用者發起過相同的主題名稱。請換一個新的主題名稱。"});
// 若為伺服器內部錯誤,則將通用訊息回呼
if (err)
return res.json({isOK: false, field: "SERVER", message: "伺服器內部錯誤,請稍後再嘗試。"});
// 更新檔案名稱,並轉存至目標資料夾中
fileSystem.copyFile(req.file.path, newFilePath, (err) => {
if (err) return res.json({isOK: false, field: "SERVER", message: "伺服器內部錯誤,請稍後再嘗試。"});
// 刪除在暫存區的圖片檔案
fileSystem.unlink(req.file.path, (err) => { if (err) console.log(err); });
req.user.hasPostNewTheme = true; // 將「是否有投稿過新主題」設為true。
req.user.save((err) => { if (err) console.log(err); }); // 並將此使用者資料進行儲存
req.session.newThemeSuccess = true; // 標記newThemeSuccess給轉跳頁面判斷所用
res.json({isOK: true, url: "/newtheme/successful"});
});
});
}
/**
* 當使用者將新的主題資料送至伺服端時的處理。
*/
router.post("/newtheme", Uploader.single("image"), NewTheme_CheckTextField, NewTheme_CheckImage, NewTheme_SaveDataAndResponse);
/**
* 當使用者成功投稿了新主題後的轉跳頁面。
*/
router.get("/newtheme/successful", (req, res) => {
// 確認是否有登入且有被標記newThemeSuccess。則傳送轉跳頁面訊息。
if (req.user && req.session.newThemeSuccess) {
delete req.session.newThemeSuccess; // 將標記給去除掉
dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("message_form", dataObj);
}
});
}
else {
res.redirect("/");
}
});
module.exports = router;