Upload All

This commit is contained in:
Alan
2018-02-26 14:09:18 +08:00
parent 42d3a3fc46
commit 46257f08b0
1024 changed files with 204324 additions and 0 deletions

495
routes/main.js Normal file
View File

@ -0,0 +1,495 @@
const User = require("../models/mongooseSchemas/User");
const router = require("express").Router();
const pug = require("pug");
const passport = require("passport");
const LocalStrategy = require('passport-local').Strategy;
const multer = require("multer");
const dataRender = require("../models/DataRender");
// 序列化: 在第一次驗證之後session皆不會保留驗證訊息因此取得user.id
passport.serializeUser(function(user, done) {
done(null, user._id);
});
// 反序列化: 用user.id來取得資料庫中的指定資料
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
/**
* 定義在「登入」頁面,使用者進行登入、傳送帳號密碼時所做的驗證策略。
*/
passport.use("login" ,new LocalStrategy({
usernameField: "username",
passwordField: "password"
}, function (username, password, done) {
User.AccountComparison(username, password, (err, user) => {
if (err) return done(err);
if (!user)
return done(null, false, {message: "使用者名稱或密碼錯誤。"});
else
return done(null, user);
});
}));
/**
* 頁面「首頁」的路由處理。
*/
router.use(require("./index"));
/**
* 頁面「傑作藝廊」的路由處理。
*/
router.use(require("./gallery"));
/**
* 頁面「畫作主題」的路由處理。
*/
router.use(require("./theme"));
/**
* 頁面「繪圖創作」的路由處理。
*/
router.use(require("./drawing"));
/**
* 頁面「意見回饋」的路由處理。
*/
router.use(require("./feedback"));
/**
* 註冊、登入與登出的相關頁面與處理。
*/
router.use(require("./gate"));
/**
* 頁面「展示藝廊」的路由處理。
*/
router.use(require("./showcase"));
/**
* 頁面「投稿主題」的路由處理。.
*/
router.use(require("./submit_theme"));
/**
* 頁面「」
*/
router.use(require("./vote_theme"));
/* =================================== */
/**
* 頁面「個人主頁」的路由處理。
*/
router.get("/home/:username", (req, res) => {
// 若使用者有登入,則允許觀看其他會員的個人頁面
if (req.session.passport && req.session.passport.user) {
dataRender.DataRender("personal_page", req.url, req.session, (err, dataObj) => {
if (!err) {
res.render("personal_page", dataObj);
}
else if (User.IsUserNotExist(err)) {
res.redirect("/homenotexist/" + req.params["username"]);
}
else {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
});
}
// 若沒有則轉跳到登入頁面
else {
res.redirect("/login");
}
});
/**
* 在「個人頁面」要尋找指定使用者之下,發生找不到該使用者時所跳轉的訊息頁面。
*/
router.get("/homenotexist/:username", (req, res) => {
dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("message_form", dataObj);
}
});
});
/**
* 頁面「編輯個人資料」的路由處理。
*/
router.get("/edit_personal_info", (req, res) => {
// 若使用者有登入,則將編輯頁面傳送至客戶端
if (req.session.passport && req.session.passport.user) {
// 取得相對應的插值物件
dataRender.DataRender("edit_personal_info", req.url, req.session, (err, dataObj) => {
// 若發生錯誤則傳送訊息。
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
// 若無則將頁面傳送至客戶端
else {
res.render("edit_personal_info", dataObj);
}
});
}
// 若沒有則轉跳到首頁
else {
res.redirect("/");
}
});
/**
* 檔案上傳協助物件SavePersonalInfo_Upload。
* 上傳的圖片會暫存在"./temp"中。
*/
const SPI_Upload = multer({
dest: "./temp",
limits: { fileSize: 131072, files: 1 }
});
/**
* 在頁面「編輯個人資料」之下,按下「儲存更變」後所傳來的資料,由此路由來處理。
* 圖像檔案資訊存放於「req.files」中文字類訊息存放於「req.body」中。
*/
router.post("/save_personal_info", SPI_Upload.single("photo"), (req, res) => {
// 若沒有登入,則跳轉至首頁
if (!req.session.passport || !req.session.passport.user) return res.redirect("/");
res.setHeader("Content-Type", "application/json");
let theFile = req.file;
// 若有登入,則在做檢查動作。
// 若使用者有傳送檔案 且 檔案類別不為"image/jpeg"與"image/png"的話,則回送錯誤訊息。
if (theFile && theFile.mimetype != "image/jpeg" && theFile.mimetype != "image/png") {
res.end({"isOK": false, "field": "photo", "message": "類型錯誤: 上傳的檔案類型請選擇jpg或png圖檔。"});
return;
}
// 檢查「姓」
req.checkBody("lastName")
.notEmpty()
.withMessage("「姓」為必要輸入的欄位,請輸入您的大名。")
.matches(/^[a-zA-Z\u2E80-\u2FDF\u3190-\u319F\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]{1,16}$/)
.withMessage("「姓」欄位輸入錯誤請輸入小於17字的英文或中文字。");
// 檢查「名」
req.checkBody("firstName")
.notEmpty()
.withMessage("「名」為必要輸入的欄位,請輸入您的大名。")
.matches(/^([a-zA-Z\u2E80-\u2FDF\u3190-\u319F\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]{1,16})([\ ]*)([a-zA-Z]{0,16})$/)
.withMessage("「名」欄位輸入錯誤請輸入小於33字的英文或中文字。");
// 檢查「暱稱」
req.checkBody("nickname")
.len({max: 16})
.withMessage("暱稱欄位的字數至多16字元。")
.matches(/^([^<>\&"']{0,16})$/)
.withMessage("在暱稱欄位中請勿輸入非法字元: <>&\"\'");
// 檢查「短言」
req.checkBody("motto")
.len({max: 32})
.withMessage("短言欄位的字數至多32字元。")
.matches(/^([^<>\&"']{0,32})$/)
.withMessage("在短言欄位中請勿輸入非法字元: <>&\"\'");
// 取得檢查結果
req.getValidationResult().then((result) => {
let errors = result.mapped(); // 將結果做成字典物件
let responseMsg = {isOK: result.isEmpty()}; // 宣告、初步定義回應物件
// 若檢查通過,則將資料更新至資料庫(交給資料庫Schema處理)
if (responseMsg.isOK) {
// 呼叫UpdatePersonalInfo傳入使用者的_id、文字資料、檔案訊息與回呼函式
User.UpdatePersonalInfo(req.session.passport.user, req.body, theFile, (err, isOK) => {
// 若有錯誤,則將錯誤設定至回應物件上,並在控制台上印出錯誤
if (err) {
responseMsg.isOK = false;
responseMsg.field = "SERVER";
responseMsg.message = (User.IsUserNotExist(err) ? "使用者資料對應錯誤。請重新登入再執行此操作。" : "伺服器內部錯誤,請稍後再嘗試。");
console.log(err);
}
// 若無錯誤,則標上 personalInfoUpdated 與轉跳頁面
else {
req.session.personalInfoUpdated = true;
responseMsg.redirect = "/personalinfo_updated";
}
// 若無錯誤,則直接回呼 (不改變 responseMsg.isOK 的 true 值)
res.send(responseMsg);
});
}
else {
let firstErr = Object.values(errors)[0]; // 取得第一個錯誤訊息物件
responseMsg.field = firstErr.param; // 將錯誤的欄位名稱新增到回應物件的field屬性
responseMsg.message = firstErr.msg; // 將錯誤訊息新增到回應物件的message屬性
res.send(responseMsg);
}
});
});
/**
* 在「編輯個人資料」頁面下,使用者上傳資料並成功後的跳轉訊息頁面。
*/
router.get("/personalinfo_updated", (req, res) => {
// 若有標記 personalInfoUpdated
if (req.session.personalInfoUpdated) {
// 刪除標記,以免重複
delete req.session.personalInfoUpdated;
// 取得插值資料(傳入URL給內部判斷)
dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
// 若有錯誤,則發送錯誤訊息
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("message_form", dataObj);
}
});
}
// 若無標記,則直接跳轉到首頁
else {
res.redirect("/");
}
});
/**
* 頁面「撰寫站內訊息」的路由處理
*/
router.get(["/write_message", "/write_message/:username"], (req, res) => {
if (req.session.passport && req.session.passport.user) {
dataRender.DataRender("write_message", req.url, req.session, (err, dataObj) => {
if (err) {
console.log(err);
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("write_message", dataObj);
}
});
}
else {
res.redirect("/");
}
});
/**
* 取得由「撰寫站內訊息」頁面所傳來的訊息
*/
router.post("/write_message", (req, res) => {
res.setHeader("Content-Type", "application/json");
// 檢查使用者是否有登入。 若有登入,則近一步地做檢查動作
if (req.session.passport && req.session.passport.user) {
// 檢查「收件者」
req.checkBody("recipient")
.notEmpty()
.withMessage("請選擇接收此站內信的目標使用者名稱。");
// 檢查「標題」
req.checkBody("subject")
.notEmpty()
.withMessage("請在「主旨」欄位上輸入1~32字間的主旨。")
.matches(/^([^<>\&"'$]{1,32})$/)
.withMessage("請勿在「主旨」中輸入有包含「<>&\"'$」有關的字元。");
// 檢查「內文」
req.checkBody("content")
.notEmpty()
.withMessage("請在「內文」中輸入您想對目標使用者傳達的訊息。")
.len({min: 8, max: 500})
.withMessage("請在「內文」中輸入8~500數量的文字訊息。");
// 取得驗證結果
req.getValidationResult().then((result) => {
let errors = result.mapped();
let responseMsg = { isOK: result.isEmpty() };
// 若驗證不通過,則傳送錯誤訊息
if (!responseMsg.isOK) {
let firstErr = Object.values(errors)[0];
responseMsg.field = firstErr.param;
responseMsg.message = firstErr.msg;
res.send(responseMsg);
return;
}
// 若驗證皆通過,則寄發信件: 將寄件者的信件寄給收件者。
User.SendSiteMail(req.session.passport.user, req.body, (err, isOK) => {
if (!err) {
responseMsg.url = "/send_sitemail_successfully";
}
else if (User.IsUserNotExist(err)) {
responseMsg.isOK = false;
responseMsg.field = "SERVER";
responseMsg.message = "找不到指定的目標的收件者。";
}
else {
responseMsg.isOK = false;
responseMsg.field = "SERVER";
responseMsg.message = err.message;
}
res.send(responseMsg);
});
});
}
// 若沒有登入,則回應錯誤。
else {
res.send({isOK: false, message: "您尚未登入,請登入後再嘗試撰寫、傳送站內訊息。"});
}
});
/**
* 在頁面「撰寫站內訊息」下,成功處理站內信後的跳轉頁面。
*/
router.get("/send_sitemail_successfully", (req, res) => {
dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("message_form", dataObj);
}
});
});
/**
* 頁面「更變密碼」的路由處理。
*/
router.get("/newpw", (req, res) => {
// 確認使用者是否有登入
if (req.session.passport && req.session.passport.user) {
dataRender.DataRender("change_password", req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
let errMsg = req.flash("error");
if (errMsg.length > 0) {
dataObj.datas.errorMessage = errMsg[0];
}
res.render("change_password", dataObj);
}
});
}
// 若沒有登入,則轉跳到首頁。
else {
res.redirect("/");
}
})
/**
* 在頁面「更變密碼」下,傳送新、舊密碼來進行更新密碼的處理。
*/
router.post("/newpw", (req, res) => {
// 若使用者為登入狀態,則進一步檢查內容
if (req.session.passport && req.session.passport.user) {
let body = req.body;
req.checkBody("oldpw")
.notEmpty()
.withMessage("「舊密碼」為必要的欄位,請填寫您目前正使用的密碼。");
req.checkBody("newpw")
.notEmpty()
.withMessage("「新密碼」為必要的欄位,請填寫您想要的新密碼。")
.not().equals(body.oldpw)
.withMessage("「新密碼」請勿與「舊密碼」完全一致。請更換新密碼。")
.matches(/(^[0-9a-zA-Z_?!@#+-]{5,16}$)/)
.withMessage("「新密碼」欄位僅能填寫5~16的數字、英文字母或「_?!@#+-」字元。");
req.checkBody("newpw_confirm")
.notEmpty()
.withMessage("「確認新密碼」為必要的欄位,請填寫與「新密碼」欄位中一致的密碼。")
.equals(body.newpw)
.withMessage("「確認新密碼」欄位必須與「新密碼」欄位中的密碼一致。");
// 取得驗證結果
req.getValidationResult().then((result) => {
// 如果結果為空,也就是沒有任何錯誤訊息的話
if (result.isEmpty()) {
// 嘗試更變使用者的密碼
User.ChangePassword(req.session.passport.user, body.oldpw, body.newpw, (err, result) => {
// 若有錯誤,則對錯誤做處理
if (err) {
if (User.IsUserNotExist(err)){
// * 可改成轉跳到登入頁面 *
req.flash("error", "使用者帳號已登出,請先重新登入。");
res.redirect("/newpw");
}
// 若為其他錯誤,則告知使用者
else {
console.log(err);
req.flash("error", "伺服器內部錯誤,請稍後再嘗試。");
res.redirect("/newpw");
}
}
// 若無錯誤,則依照結果發送訊息
else {
if (result) {
req.session.changePW_successfuly = true;
res.redirect("/newpw_success");
}
else {
req.flash("error", "「舊密碼」輸入錯誤。請輸入正確的、當前使用的密碼。");
res.redirect("/newpw");
}
}
});
}
// 若不為空,表示有誤
else {
let firstErr = Object.values(result.mapped())[0]; // 取得第一個錯誤
req.flash("error", firstErr.msg); // 將錯誤設定至flash中
res.redirect("/newpw"); // 轉跳到「更新密碼」頁面
}
});
}
// 若沒有登入,則轉跳到首頁。 (暫時)
else {
res.redirect("/");
}
});
/**
* 在頁面「更變密碼」之下,成功更改密碼之後的轉跳訊息頁面。
*/
router.get("/newpw_success", (req, res) => {
// 確認是否「剛剛更新完密碼」
if (req.session.changePW_successfuly) {
delete req.session.changePW_successfuly; // 刪除標記,以免重複
dataRender.DataRender("message_form", req.url, req.session, (err, dataObj) => {
if (err) {
res.setHeader("Content-Type", "text/plain");
res.status(500);
res.end("Server side error 500 : " + err);
}
else {
res.render("message_form", dataObj);
}
});
}
});
module.exports = router;