198 lines
8.1 KiB
JavaScript
198 lines
8.1 KiB
JavaScript
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; |