1027 lines
45 KiB
JavaScript
1027 lines
45 KiB
JavaScript
const fileSystem = require("fs");
|
||
const mongoose = require("mongoose");
|
||
const ServerStatus = require("../ServerStatus");
|
||
|
||
const COUT = process.stdout;
|
||
|
||
/** 集合所有的資料庫模板資料。 */
|
||
const DBModels = {
|
||
"User": require("./mongooseSchemas/User"),
|
||
"Painting": require("./mongooseSchemas/Painting"),
|
||
"Season": require("./mongooseSchemas/Season"),
|
||
"Theme": require("./mongooseSchemas/Theme"),
|
||
"Participation": require("./mongooseSchemas/Participation"),
|
||
"ParticipantInfo": require("./mongooseSchemas/ParticipantInfo"),
|
||
"SiteMail": require("./mongooseSchemas/SiteMail"),
|
||
"SiteMessage": require("./mongooseSchemas/SiteMessage"),
|
||
"Rating": require("./mongooseSchemas/Rating"),
|
||
"Comment": require("./mongooseSchemas/Comment"),
|
||
"NewTheme": require("./mongooseSchemas/NewTheme"),
|
||
"PaintingSpotlight": require("./mongooseSchemas/PaintingSpotlight"),
|
||
"ServerMessage": require("./mongooseSchemas/ServerMessage")
|
||
};
|
||
|
||
/** 指令字串切割方式 */
|
||
const cmdSpliter = / +/g;
|
||
|
||
/** 目前正用於處理指令輸入操作的函式。 */
|
||
let CurrentOperationFunc = CommandRoutes;
|
||
|
||
/** 承接頂層的LineReader之指令處理函式。 */
|
||
function MiddleHandler(line) { CurrentOperationFunc(line); }
|
||
|
||
/** 關閉、離開伺服器運行的方法函式。 */
|
||
let ExitServerFunction;
|
||
|
||
/**
|
||
* 初始化「指令路由」的初始化函式。
|
||
* @param {Function} ExitServFunc 停止伺服器運作的函式。此方法由外部傳入。
|
||
*/
|
||
function Init(ExitServFunc) {
|
||
ExitServerFunction = ExitServFunc;
|
||
return MiddleHandler;
|
||
}
|
||
|
||
/**
|
||
* 指令路由函式。
|
||
* @param {string} line 從標準文字輸入所接收到的一行輸入字串。
|
||
*/
|
||
function CommandRoutes(line) {
|
||
let query = line.split(cmdSpliter); // 以空格來切割每一個指令
|
||
switch(query[0]) {
|
||
case ".exit": ExitServerFunction(); break;
|
||
case "showstatus": ShowServerStatus(); break;
|
||
case "act": ActivityFunction(query); break;
|
||
case "servmsg": ServMessageFunction(query); break;
|
||
case "db": DatabaseFunctions(line, query); break;
|
||
case "restore": RestoreServer(); break;
|
||
case "help": HelpInformation(); break;
|
||
default:
|
||
console.log("\n錯誤: 找不到指令或巨集。\n");
|
||
}
|
||
}
|
||
|
||
//#region ==================== Show Status Operation Command =========================
|
||
/**
|
||
* 顯示目前伺服器的狀態。
|
||
*/
|
||
function ShowServerStatus() {
|
||
let status = ServerStatus.status;
|
||
if (status) {
|
||
let info = "目前舉行的活動為第 " + status.currentSeason + " 季\n" +
|
||
"主題投稿活動: " + ( status.submitThemeEvent ? "運行中\n" : "未運行\n" ) +
|
||
"主題投票活動: " + ( status.voteThemeEvent ? "運行中\n" : "未運行\n" ) +
|
||
"在主題投票活動中,每位使用者能夠投選" + status.voteCount + "個候選主題\n" +
|
||
"在主題投票活動完成之後,會選出前" + status.promotionCount + "高票個候選主題作為新一季的主題\n" +
|
||
"自動活動排程系統目前" + ( status.onSchedule ? "正在運行中\n" : "停止使用中\n" ) +
|
||
"伺服狀態自動儲存: " + ( status.autoSaveStatus ? "開啟\n" : "關閉\n" );
|
||
console.log("\n==================== 伺服器狀態 ====================");
|
||
console.log(info);
|
||
}
|
||
else {
|
||
console.log("\nPlease wait for loading server status file.\n");
|
||
}
|
||
}
|
||
//#endregion =========================================================================
|
||
|
||
//#region ==================== Activity Operation Commands ====================
|
||
/**
|
||
* 與藝廊活動有關的相關操作函式。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function ActivityFunction(query) {
|
||
let operation = query[1];
|
||
switch(operation) {
|
||
case "status": Act_Status(); break;
|
||
case "push": Act_Push(); break;
|
||
case "help": Act_Help(); break;
|
||
default:
|
||
console.log("\n錯誤: 找不到指令或巨集。\n");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 顯示目前活動的狀態。
|
||
*/
|
||
function Act_Status() {
|
||
let status = ServerStatus.status;
|
||
let info = "\n目前舉行的活動為第 " + status.currentSeason + " 季\n";
|
||
// 若「投稿主題」正在進行中
|
||
if (status.submitThemeEvent) {
|
||
info += "當前正為下一季的活動做準備,正舉行著「投稿主題」中。\n";
|
||
}
|
||
// 若「票選主題」正在進行中
|
||
else if (status.voteThemeEvent) {
|
||
info += "當前正為下一季的活動做準備,正舉行著「投稿主題」中。\n";
|
||
}
|
||
info += "目前的活動規劃" + (status.onSchedule ? "由排程系統自動進行中。\n" : "為手動操作。\n");
|
||
console.log(info);
|
||
}
|
||
|
||
/**
|
||
* 以手動的方式推進活動狀態。
|
||
* 若活動排程系統為啟用的狀態,則這個操作將會失效。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function Act_Push(query) {
|
||
let status = ServerStatus.status;
|
||
// 若排程系統是啟用的,則印出訊息。
|
||
if (status.onSchedule) {
|
||
console.log("\n目前的活動規劃由排程系統管理中,無法做手動操作的部分。");
|
||
console.log("若想要進行「活動階段推進」的動作,請先將排程系統關閉。\n");
|
||
return;
|
||
}
|
||
|
||
// 若排程系統是關閉的,則先確認是否要執行活動狀態推進的動作。
|
||
function AskPush(line) {
|
||
if (line == "Y" || line == "y") { // 若使用者確認,則將處理文字輸入的主函式改回,並進行推進的動作
|
||
CurrentOperationFunc = CommandRoutes;
|
||
PushActivity();
|
||
}
|
||
else if (line == "N" || line == "n") { // 若使用者確認,則將處理文字輸入的主函式改回,並進行推進的動作
|
||
CurrentOperationFunc = CommandRoutes;
|
||
console.log();
|
||
}
|
||
else {
|
||
console.log("\n請輸入Y或N來決定是否要推進狀態: \n");
|
||
}
|
||
}
|
||
|
||
let curSeason = status.currentSeason; // 取得目前的季數
|
||
COUT.write("\n目前的活動狀態為");
|
||
|
||
// 依不同的狀態印出訊息
|
||
switch(true) {
|
||
case status.submitThemeEvent: // 若目前「投稿主題」活動正在進行
|
||
COUT.write("「投稿主題」。\n");
|
||
console.log("若要推進,則活動狀態會到第" + curSeason + "季與第" + (curSeason + 1) + "季的「主題票選」活動。\n");
|
||
break;
|
||
case status.voteThemeEvent: // 若目前「主題票選」活動正在進行
|
||
COUT.write("「主題票選」。\n");
|
||
console.log("若要推進,則活動狀態會到第" + (curSeason + 1) + "季,也就是更新到全新的一季。\n");
|
||
break;
|
||
default: // 若目前僅進行一般的季活動
|
||
COUT.write("「一般活動」。\n");
|
||
console.log("若要推進,則活動狀態會到第" + curSeason + "季與第" + (curSeason + 1) + "季的「投稿主題」活動。\n");
|
||
}
|
||
console.log("\n請問是否要推進? (Y/N) \n");
|
||
CurrentOperationFunc = AskPush; // 將處理文字輸入的主函式改為AskPush,也就是詢問是否要推進。
|
||
}
|
||
//#region Functions that Act_Push use.
|
||
/**
|
||
* 實作「活動階段推進」的程式動作。 (順序 1)
|
||
*/
|
||
function PushActivity() {
|
||
let status = ServerStatus.status; // 取得伺服器狀態物件
|
||
// 若目前「投稿主題」活動正在進行
|
||
if (status.submitThemeEvent) {
|
||
status.submitThemeEvent = false;
|
||
status.voteThemeEvent = true;
|
||
|
||
// 刷新使用者資料中的 hasVotedNewTheme 欄位,將其轉變為false。
|
||
DBModels.User.Refresh_HasVotedNewTheme((err) => {
|
||
if (err) return console.log("\n設定使用者資料的時候發生了錯誤。請確認是否有與MongoDB連線。\n");
|
||
|
||
// 詢問並設定每一位使用者投票的票數
|
||
AskPromotionTheme(status.submitThemeEvent, status.voteThemeEvent, status.currentSeason);
|
||
});
|
||
}
|
||
// 若目前「主題票選」活動正在進行
|
||
else if (status.voteThemeEvent) {
|
||
status.voteThemeEvent = false;
|
||
status.currentSeason += 1;
|
||
|
||
// 將新的一季活動做推進
|
||
DBModels.Season.PushNewSeason((err, result) => {
|
||
if (err) return console.log(result);
|
||
AskBroadcastServerMessage(status.submitThemeEvent, status.voteThemeEvent, status.currentSeason);
|
||
});
|
||
}
|
||
// 若目前僅一般主題在進行
|
||
else {
|
||
status.submitThemeEvent = true;
|
||
// 刷新使用者資料中的 hasPostNewTheme 欄位,將其轉變為false。
|
||
DBModels.User.Refresh_HasPostNewTheme((err) => {
|
||
if (err) return console.log("\n設定使用者資料的時候發生了錯誤。請確認是否有與MongoDB連線。\n");
|
||
|
||
// 詢問是否進行伺服訊息廣播
|
||
AskBroadcastServerMessage(status.submitThemeEvent, status.voteThemeEvent, status.currentSeason);
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 詢問使用者要選取NewTheme中前幾個主題做為最新一季的主題。
|
||
* @param {boolean} submitTheme 「投稿主題」活動是否為進行中。
|
||
* @param {boolean} voteTheme 「主題票選」活動是否為進行中。
|
||
* @param {number} curSeason 目前最新的季。
|
||
*/
|
||
function AskPromotionTheme(submitTheme, voteTheme, curSeason) {
|
||
let validator = str => /^([1-9]\d*)$/.test(str);
|
||
let ask = "\n在此次主題票選當中,在最後要選取多少候選主題出來作為新一季的主題: ";
|
||
|
||
/** 處理使用者的回應 */
|
||
function HandleResponse(line) {
|
||
// 檢查使用者輸入的數值是否為大於0的正整數
|
||
if (!validator(line)) {
|
||
COUT.write("請輸入大於0的正整數、小於等於總候選主題數的數字: ");
|
||
return;
|
||
}
|
||
// 檢查使用者輸入的數字是否小於等於候選主題的總數
|
||
let count = parseInt(line);
|
||
DBModels.NewTheme.count({}, (err, number) => {
|
||
if (err) {
|
||
console.log("\n檢查候選主題總數時發生了錯誤。請檢查MongoDB是否開啟或稍後重新操作。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
return;
|
||
}
|
||
// 若有小於等於候選主題總數,則進行下一步「詢問是否要對所有使用者廣播訊息」
|
||
if (count <= number) {
|
||
ServerStatus.status.promotionCount = count; // 將數量記在伺服狀態的promotionCount中
|
||
AskBroadcastServerMessage(submitTheme, voteTheme, curSeason);
|
||
}
|
||
else {
|
||
COUT.write("請輸入大於0的正整數、小於等於總候選主題數的數字: ");
|
||
}
|
||
});
|
||
}
|
||
|
||
COUT.write(ask);
|
||
CurrentOperationFunc = HandleResponse;
|
||
}
|
||
|
||
/**
|
||
* 詢問是否要進行伺服器的訊息廣播。 (順序 2)
|
||
* @param {boolean} submitTheme 「投稿主題」活動是否為進行中。
|
||
* @param {boolean} voteTheme 「主題票選」活動是否為進行中。
|
||
* @param {number} curSeason 目前最新的季。
|
||
*/
|
||
function AskBroadcastServerMessage(submitTheme, voteTheme, curSeason) {
|
||
let ask = "是否要對個使用者做新活動的訊息廣播? (Y/N) ";
|
||
let title, content;
|
||
COUT.write(ask);
|
||
|
||
/** 定義處理使用者輸入訊息標題的函式 (順序 3.A.1) */
|
||
function HandleTitleResponse(line) {
|
||
title = line;
|
||
console.log("請輸入訊息內容:");
|
||
CurrentOperationFunc = HandleContentResponse;
|
||
}
|
||
|
||
/** 定義處理使用者輸入訊息內容的函式 (順序 3.A.2) */
|
||
function HandleContentResponse(line) {
|
||
content = line;
|
||
COUT.write("確認廣播此伺服訊息? (Y/N) ");
|
||
CurrentOperationFunc = ConfirmServerMessage;
|
||
}
|
||
|
||
/** 定義處理使用者是否使用預設的訊息內容 (順序 3.B.1) */
|
||
function DefaultResponse(line) {
|
||
if (line == "Y" || line == "y") {
|
||
// 以資料title、content來對所有使用者進行廣播
|
||
BroadcastServerMessage(title, content, (err, result) => {
|
||
if (err) return console.log("\n" + result + "\n");
|
||
|
||
// 依照不同狀態輸出成功訊息
|
||
console.log("\n已成功地將最新的活動訊息廣播給所有使用者!");
|
||
ShowFinishMessage(submitTheme, voteTheme, curSeason);
|
||
|
||
// 最後將主要處理文字輸入的方法設定回主指令路由函式中
|
||
CurrentOperationFunc = CommandRoutes;
|
||
});
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
// 如果使用者拒絕使用預設的伺服器廣播訊息,則輸出最終訊息,並將處理文字輸入的變數函式指回主處理函式
|
||
ShowFinishMessage(submitTheme, voteTheme, curSeason);
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
COUT.write("請問是否要使用預設的廣播訊息內容? (Y/N) ");
|
||
}
|
||
}
|
||
|
||
/** 確認使用者是否要廣播此伺服訊息 (順序 3.A.3) */
|
||
function ConfirmServerMessage(line) {
|
||
if (line == "Y" || line == "y") {
|
||
// 以資料title、content來對所有使用者進行廣播
|
||
BroadcastServerMessage(title, content, (err, result) => {
|
||
if (err) return console.log("\n" + result + "\n");
|
||
|
||
// 依照不同狀態輸出成功訊息
|
||
console.log("\n已成功地將最新的活動訊息廣播給所有使用者!");
|
||
ShowFinishMessage(submitTheme, voteTheme, curSeason);
|
||
|
||
// 最後將主要處理文字輸入的方法設定回主指令路由函式中
|
||
CurrentOperationFunc = CommandRoutes;
|
||
});
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
// 如果選擇不廣播伺服訊息,則再一次詢問「是否要做活動訓席廣播」
|
||
AskBroadcastServerMessage(submitTheme, voteTheme, curSeason);
|
||
}
|
||
else {
|
||
COUT.write("確認廣播此伺服訊息? (Y/N) ");
|
||
}
|
||
}
|
||
|
||
/** 處理回應 (順序 3) */
|
||
function HandleResponse(line) {
|
||
// 若決定要對使用者進行廣播,則先詢問廣播訊息的標題
|
||
if (line == "Y" || line == "y") {
|
||
console.log("請輸入訊息標題:");
|
||
CurrentOperationFunc = HandleTitleResponse;
|
||
}
|
||
// 若沒有決定要自行廣播,則再詢問是否要進行預設的伺服器訊息廣播
|
||
else if (line == "N" || line == "n") {
|
||
[title, content] = GetDefaultActivityServMsg(submitTheme, voteTheme, curSeason);
|
||
console.log("\n預設的廣播格式為:");
|
||
console.log("標題: %s", title);
|
||
console.log("內容: %s\n", content);
|
||
COUT.write("請問是否要使用預設的廣播訊息內容? (Y/N) ");
|
||
CurrentOperationFunc = HandleDefaultResponse;
|
||
}
|
||
else {
|
||
COUT.write(ask);
|
||
}
|
||
}
|
||
|
||
// 變更主要處理文字輸入的函式
|
||
CurrentOperationFunc = HandleResponse;
|
||
}
|
||
|
||
/**
|
||
* 在「活動階段推進」之中,最後完成動作時所顯示的訊息。
|
||
* @param {boolean} submitTheme 「投稿主題」活動是否為進行中。
|
||
* @param {boolean} voteTheme 「主題票選」活動是否為進行中。
|
||
* @param {number} curSeason 目前最新的季。
|
||
*/
|
||
function ShowFinishMessage(submitTheme, voteTheme, curSeason) {
|
||
if (submitTheme)
|
||
console.log("\n第%d季的「投稿主題」活動已經開始!\n", curSeason + 1);
|
||
else if (voteTheme)
|
||
console.log("\n第%d季的「投稿主題」活動已經結束。活動「主題票選」已經開始!\n", curSeason + 1);
|
||
else
|
||
console.log("\n第%d季的繪圖投稿活動與第%d季的「主題票選」活動已經結束。\n最新第%d季的活動已經開始!\n", curSeason - 1, curSeason, curSeason);
|
||
}
|
||
|
||
/**
|
||
* 取得預設的伺服器廣播訊息。
|
||
* @param {boolean} submitTheme 「投稿主題」活動是否為進行中。
|
||
* @param {boolean} voteTheme 「主題票選」活動是否為進行中。
|
||
* @param {number} curSeason 目前最新的季。
|
||
* @return {string[]} 預設的伺服訊息中的 [標題, 內容] 的成對。
|
||
*/
|
||
function GetDefaultActivityServMsg(submitTheme, voteTheme, curSeason) {
|
||
switch(true) {
|
||
case submitTheme:
|
||
return ["第" + (curSeason + 1) + "季的「投稿主題」活動已經開始!",
|
||
"最新的「主題投稿」活動已經開始了!無論是有創意、有熱情、有特殊想法的作家們,趕緊來發起你們心中所想要的主題吧!人人都有機會在版面上看到自己所投稿的主題噢!"];
|
||
case voteTheme:
|
||
return ["第" + (curSeason + 1) + "季的「主題票選」活動已經開始!",
|
||
"最新的「主題票選」活動已經開始了!無論你在先前有投稿主題或沒有投稿主題,趕緊來這個活動看看,有沒有你所想要、中意的主題,有的話就把你手中的票朝那主題投下去吧!"];
|
||
default:
|
||
return ["最新一季繪畫活動,第" + curSeason + "季已經開始了!!",
|
||
"第" + curSeason + "季的繪畫活動已經開始了!來看看在這最新的一季當中,有沒有你所喜歡的、所愛的主題,有的話就開始動起你手中的創意、發揮你的天份,來開始畫畫吧!此外,第" + ( curSeason - 1 ) + "季的繪畫活動結果已經出爐了,可以去瞧一瞧是哪幾位大師登上旁行榜吧!"];
|
||
}
|
||
}
|
||
//#endregion
|
||
|
||
/**
|
||
* 查詢指令"act"的相關功能與說明。
|
||
*/
|
||
function Act_Help() {
|
||
console.log("\nact - 與伺服器活動有相關的操作");
|
||
console.log("act status : 檢視目前伺服器活動的狀態訊息。");
|
||
console.log("act push : 以手動的方式做「活動階段推進」的動作。");
|
||
console.log("act help : 查詢指令act的相關操作方式。\n")
|
||
}
|
||
|
||
/**
|
||
* 新增、廣播伺服訊息。
|
||
* @param {string} title 訊息的標題。
|
||
* @param {string} content 訊息的內容。
|
||
* @param {CallbackFunction} callback 回呼函式。
|
||
*/
|
||
function BroadcastServerMessage(title, content, callback) {
|
||
let ServerMessage = DBModels.ServerMessage;
|
||
let User = DBModels.User;
|
||
// 建立一個新的伺服訊息資料
|
||
ServerMessage.createNewServerMessage({ title, content }, (err, _id) => {
|
||
if (err) return callback(err, "在建立新的伺服器訊息時發生了錯誤,請改用一般的廣播方式來進行廣播。");
|
||
// 透過伺服訊息資料的_id,加入至各使用者的siteMsg中
|
||
User.BroadcastServerMessage(_id, (err, result) => {
|
||
if (err) return callback(err, "在對所有使用者進行廣播動作時發生了錯誤。請更改使用單一或多項指定來傳送伺服訊息。");
|
||
callback(null, true);
|
||
});;
|
||
});
|
||
}
|
||
|
||
//#endregion ======================================================================
|
||
|
||
//#region ==================== Server Message Operation Commands ====================
|
||
/**
|
||
* 撰寫伺服器訊息相關功能的函式。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function ServMessageFunction(query) {
|
||
let operation = query[1];
|
||
switch(operation) {
|
||
case "sendone": WriteServMsg(ServMsg_SendOne, false); break;
|
||
case "sendmany": WriteServMsg(ServMsg_SendOne, false); break;
|
||
case "broadcast": WriteServMsg(ServMsg_Broadcast, true); break;
|
||
case "help": ServMsg_Help(); break;
|
||
default:
|
||
console.log("\n錯誤: 找不到指令或巨集。\n");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 實行指令"servmsg sendone"之指令。此函式必須要透過WriteServMsg函式內部來呼叫。
|
||
* @param {string} title 伺服訊息的標題。
|
||
* @param {string} content 伺服訊息的內容。
|
||
* @param {boolean} isPrivate 是否為私密訊息。
|
||
*/
|
||
function ServMsg_SendOne(title, content, isPrivate) {
|
||
let User = DBModels.User;
|
||
/** 接收輸入的使用者名稱。 */
|
||
function InputUsername(username) {
|
||
// 嘗試取得目標使用者
|
||
User.findOne({ "username": username }, (err, userDocs) => {
|
||
if (err) {
|
||
console.log("\n尋找目標使用者時發生了錯誤。請確認是否有連接上MongoDB。");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else if (!userDocs) {
|
||
console.log("\n找不到目標使用者,請再輸入一次: (使用者名稱) ");
|
||
}
|
||
else {
|
||
userDocs.AddNewSiteMessage(title, content, isPrivate);
|
||
userDocs.save((err) => {
|
||
if (err) {
|
||
console.log("\n使用者資料儲存失敗。請檢查是否與MongoDB連線正常。\n");
|
||
}
|
||
else {
|
||
console.log("\n伺服訊息發送成功!\n");
|
||
}
|
||
CurrentOperationFunc = CommandRoutes;
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
console.log("\n請問要發送給哪位使用者? (使用者名稱) ")
|
||
CurrentOperationFunc = InputUsername;
|
||
}
|
||
|
||
/**
|
||
* 實行指令"servmsg sendmany"之指令。此函式必須要透過WriteServMsg函式內部來呼叫。
|
||
* @param {string} title 伺服訊息的標題。
|
||
* @param {string} content 伺服訊息的內容。
|
||
* @param {boolean} isPrivate 是否為私密訊息。
|
||
*/
|
||
function ServMsg_SendMany(title, content, isPrivate) {
|
||
let User = DBModels.User;
|
||
/** 處理使用者輸入的使用者名稱。 */
|
||
function InputUsername(username) {
|
||
// 若有輸入文字則進行以下動作
|
||
if (username.length > 0) {
|
||
// 以使用者輸入的 username 尋找目標使用者是否存在
|
||
User.findOne({ "username": username }, (err, userDocs) => {
|
||
if (err) {
|
||
console.log("\n尋找目標使用者時發生了錯誤。請確認是否有連接上MongoDB。");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else if (!userDocs) {
|
||
console.log("\n找不到目標使用者,請在輸入一次: (輸入使用者名稱,或直接按下Enter來完成動作)");
|
||
}
|
||
else {
|
||
// 使用者存在,則以title、content與isPrivate新增主頁訊息
|
||
userDocs.AddNewSiteMessage(title, content, isPrivate);
|
||
// 新增完後做儲存動作。
|
||
userDocs.save((err) => {
|
||
if (err) {
|
||
console.log("\n使用者資料儲存失敗。請檢查是否與MongoDB連線正常。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n伺服訊息發送成功,請繼續輸入? (輸入使用者名稱,或直接按下Enter來完成動作)");
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
console.log("\n已完成訊息發送動作。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
}
|
||
|
||
console.log("\n請問要發送給哪位使用者? (輸入使用者名稱,或直接按下Enter來完成動作)");
|
||
CurrentOperationFunc = InputUsername;
|
||
}
|
||
|
||
/**
|
||
* 實行指令"servmsg broadcast"之指令。此函式必須要透過WriteServMsg函式內部來呼叫。
|
||
* @param {string} title 伺服訊息的標題。
|
||
* @param {string} content 伺服訊息的內容。
|
||
*/
|
||
function ServMsg_Broadcast(title, content) {
|
||
let ServerMessage = DBModels.ServerMessage;
|
||
let User = DBModels.User;
|
||
// 以title、content建立伺服訊息(廣播訊息)
|
||
ServerMessage.createNewServerMessage({ title, content }, (err, _id) => {
|
||
if (err) {
|
||
console.log("\n建立伺服訊息資料時發生了錯誤,請檢查是否有連接上MongoDB。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
return;
|
||
}
|
||
// 定義要加入到各個使用者資料上的主頁訊息資料
|
||
let data = {
|
||
isServerMessage: true,
|
||
refId: _id,
|
||
postTime: new Date(),
|
||
isSeen: false,
|
||
isPrivate: false
|
||
};
|
||
// 更新所有使用者資料,將資料加入至siteMsg之中。
|
||
User.updateMany({}, { $push: { siteMsg: data } }, (err, raw) => {
|
||
if (err) {
|
||
console.log("\n將新的伺服訊息更新至所有使用者的資料之中時發生了錯誤,請檢查是否有連接上MongoDB。\n");
|
||
}
|
||
else {
|
||
console.log("\n您的伺服訊息已成功地廣播給所有使用者了!\n");
|
||
}
|
||
CurrentOperationFunc = CommandRoutes;
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 撰寫完訊息之後所要將標題、內容交給目標函式處理的函式。
|
||
* @typedef {Function} WritenMethod
|
||
* @param {string} title 訊息的標題。
|
||
* @param {string} content 訊息的內容。
|
||
* @param {boolean} isPrivate 是否為私密訊息。
|
||
*/
|
||
/**
|
||
* 撰寫伺服器訊息。
|
||
* @param {WritenMethod} sendMethod 當伺服訊息完成之後,所要處理標題、內容的函式。
|
||
* @param {boolean} isBroadcast 是否為廣播訊息。
|
||
*/
|
||
function WriteServMsg(sendMethod, isBroadcast) {
|
||
let title = "", content = "";
|
||
let isPrivate;
|
||
/** 撰寫訊息的標題。 */
|
||
function WriteTitle(line) {
|
||
title = line;
|
||
console.log("\n請在以下轉寫你所要的訊息內容,直到內容中出現「\\0」才會進行下一步驟:");
|
||
CurrentOperationFunc = WriteContent;
|
||
}
|
||
|
||
/** 撰寫訊息內容。 */
|
||
function WriteContent(line) {
|
||
let endIndex = line.search(/\\0/);
|
||
if (endIndex < 0) {
|
||
content += line;
|
||
}
|
||
else {
|
||
content += line.substr(0, endIndex);
|
||
// 若是廣播的話,則跳過觀看權限詢問。
|
||
if (isBroadcast) {
|
||
console.log("\n您的伺服訊息已經撰寫完成,內容如下。");
|
||
console.log("標題: %s", title);
|
||
console.log("內容: %s", content);
|
||
console.log("* 由於此訊息為廣播訊息,因此全部使用者皆有觀看權限。 *");
|
||
console.log("\n以上是否已為你所想要的內容呢? (Y/N) ");
|
||
CurrentOperationFunc = ConfirmIsDone;
|
||
}
|
||
else {
|
||
console.log("\n此訊息是否僅限使用者本身能看到: (Y/N) ");
|
||
CurrentOperationFunc = SetIsPrivate;
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 設定此訊息的觀看權限。 */
|
||
function SetIsPrivate(line) {
|
||
if (line == "Y" || line == "y" || line == "N" || line == "n") {
|
||
isPrivate = (line == "Y" || line == "y");
|
||
console.log("\n您的伺服訊息已經撰寫完成,內容如下。");
|
||
console.log("標題: %s", title);
|
||
console.log("內容: %s", content);
|
||
console.log("僅限擁有者能觀看: %s", isPrivate ? "Yes" : "No");
|
||
console.log("\n以上是否已為你所想要的內容呢? (Y/N) ");
|
||
CurrentOperationFunc = ConfirmIsDone;
|
||
}
|
||
else {
|
||
console.log("\n此訊息是否僅限使用者本身能看到: (Y/N) ");
|
||
}
|
||
}
|
||
|
||
/** 確認訊息是否撰寫完成。 */
|
||
function ConfirmIsDone(line) {
|
||
if (line == "Y" || line == "y") {
|
||
sendMethod(title, content, isPrivate);
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
console.log("\n是否要再撰寫一份新的伺服訊息呢? (Y/N) ");
|
||
CurrentOperationFunc = ConfirmAgain;
|
||
}
|
||
else {
|
||
console.log("\n以上是否已為你所想要的內容呢? (Y/N) ");
|
||
}
|
||
}
|
||
|
||
/** 已經被回拒,但再次確認是否要再撰寫一次。 */
|
||
function ConfirmAgain(line) {
|
||
if (line == "Y" || line == "y") {
|
||
content = "";
|
||
console.log("請寫上您所想要的伺服訊息標題: ");
|
||
CurrentOperationFunc = WriteTitle;
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
console.log();
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n以上是否已為你所想要的內容呢? (Y/N) ");
|
||
}
|
||
}
|
||
|
||
console.log("\n請寫上您所想要的伺服訊息標題: ");
|
||
CurrentOperationFunc = WriteTitle;
|
||
}
|
||
|
||
/**
|
||
* 查詢伺服器訊息指令"servmsg"的相關操作用法。
|
||
*/
|
||
function ServMsg_Help() {
|
||
console.log("\nservmsg - 與伺服器傳送訊息有關的操作");
|
||
console.log("servmsg sendone : 撰寫伺服器訊息,並將訊息傳送給指定的使用者。");
|
||
console.log("servmsg sendmany : 撰寫伺服器訊息,並將訊息傳給選定的多對使用者。");
|
||
console.log("servmsg broadcast : 撰寫伺服器訊息,並將訊息廣播給全部的使用者。");
|
||
console.log("servmsg help : 查詢此指令的相關操作方式。\n");
|
||
}
|
||
|
||
//#endregion ======================================================================
|
||
|
||
//#region ====================== Database Operation Commands ======================
|
||
|
||
/**
|
||
* 有關於資料庫操作的相關指令動作。
|
||
* @param {string} original 原始完整的指令碼。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function DatabaseFunctions(original, query) {
|
||
let operation = query[1];
|
||
switch(operation) {
|
||
case "find": DBOp_Find(original, query); break;
|
||
case "user": DBOp_User(query); break;
|
||
case "help": DBOp_Help(); break;
|
||
default:
|
||
console.log("\n錯誤: 找不到指令或巨集。\n");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 資料庫指令操作中的「搜尋」。
|
||
* @param {string} original 原始完整的指令碼。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function DBOp_Find(original, query) {
|
||
let target = query[2];
|
||
let condition = original.substr(original.indexOf(query[3]));
|
||
|
||
// 若顯示目標為"schema",則秀出所有的模板名稱。
|
||
if (target == "schemas") {
|
||
console.log("\n=============== 資料庫中所有的模板 ===============");
|
||
for (let schemaName in DBModels) {
|
||
console.log(schemaName);
|
||
}
|
||
console.log();
|
||
return;
|
||
}
|
||
|
||
// 若使用者有輸入條件參數,則嘗試轉換成JSON物件;若無,則設定為空JSON物件。
|
||
if (query[3] && condition.length > 0) {
|
||
try {
|
||
condition = JSON.parse(condition);
|
||
}
|
||
catch (err) {
|
||
return console.log("\n條件參數格式錯誤,請寫下正確的JSON格式。\n");
|
||
}
|
||
}
|
||
else {
|
||
condition = {};
|
||
}
|
||
|
||
// 再透過目標(target)找出指定的資料表。
|
||
let model = DBModels[target];
|
||
let index = 0, length;
|
||
// 若目標的資料集合存在,則繼續下一步
|
||
if (model) {
|
||
|
||
// 先計算出目標資料有多少個
|
||
model.count(condition, (err, count) => {
|
||
length = count;
|
||
// 若數量為0,則印出訊息表示找不到並返回
|
||
if (length == 0) {
|
||
console.log("\n指定尋找的資料不存在。\n");
|
||
return;
|
||
}
|
||
|
||
/** 詢問使否連續觀看資料後的處理。 */
|
||
function HandleIsContinue(line) {
|
||
if (line == "Y" || line == "y") {
|
||
SearchAndShows();
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n以下還有%s筆資料,是否再向下顯示10筆? (Y/N)\n", length - index);
|
||
}
|
||
}
|
||
|
||
/** 循環尋找,並且將資料印出。 */
|
||
function SearchAndShows() {
|
||
model.find(condition)
|
||
.skip(index)
|
||
.limit(10)
|
||
.exec((err, docsList) => {
|
||
if (err) {
|
||
console.log("\n搜尋資料庫中的指定資料時發生了錯誤。請檢查是否有與MongoDB連線。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
return;
|
||
}
|
||
console.log();
|
||
console.log(docsList); // 將資料印出
|
||
console.log();
|
||
index += 10; // 推移10個量。
|
||
|
||
// 判斷是否全數顯示完成,若全數顯示完成,則將文字處理介面指回原先的處理函式。
|
||
if (index >= length) {
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n以下還有%s筆資料,請問是否再向下顯示10筆? (Y/N)\n", length - index);
|
||
CurrentOperationFunc = HandleIsContinue;
|
||
}
|
||
}
|
||
);
|
||
}
|
||
SearchAndShows();
|
||
});
|
||
}
|
||
// 若不存在,則印出
|
||
else {
|
||
console.log("\n找不到指定的模板。\n");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 與使用者資料操作相關的功能。
|
||
* @param {string[]} query 解析後的指令片段。
|
||
*/
|
||
function DBOp_User(query) {
|
||
let operation = query[2];
|
||
switch(operation) {
|
||
case "list": DBOp_User_List(); break;
|
||
default:
|
||
console.log("\n錯誤: 找不到指令或巨集。\n");
|
||
|
||
}
|
||
}
|
||
//#region Commands under "db user" operation.
|
||
/**
|
||
* 將所有的使用者名稱以清單的方式列出。
|
||
*/
|
||
function DBOp_User_List() {
|
||
let User = DBModels.User;
|
||
// 先取得所有使用者名稱的數量
|
||
User.count({}, (err, userCount) => {
|
||
if (err) return console.log("\n向資料庫發送請求時發生了錯誤。請檢查是否有與MongoDB連線。\n");
|
||
let index = 0;
|
||
|
||
/** 是否繼續顯示使用者名稱。 */
|
||
function HandleIsContinue(line) {
|
||
if (line == "Y" || line == "y") {
|
||
ShowUsernames();
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n還有剩餘的%s筆資料尚未顯示出來,是否繼續向下顯示80筆? (Y/N)");
|
||
}
|
||
}
|
||
|
||
/** 顯示使用者名稱。 */
|
||
function ShowUsernames() {
|
||
User.find({})
|
||
.select("username")
|
||
.skip(index)
|
||
.limit(80)
|
||
.exec((err, docsList) => {
|
||
if (err) {
|
||
console.log("\n資料庫搜尋資料時發生了錯誤。請檢查是否有與MongoDB連線。\n");
|
||
CurrentOperationFunc = CommandRoutes;
|
||
return;
|
||
}
|
||
// 將使用者資料有序的顯示出來
|
||
let length = docsList.length, spaceChar = ' ', formatSpace;
|
||
for (let i = 0; i < length; i++) {
|
||
formatSpace = 17 - docsList[i].username.length;
|
||
console.log("%s%s%s", docsList[i].username, spaceChar.repeat(formatSpace), i % 4 == 3 ? "\n" : "");
|
||
}
|
||
// 檢查是否還有剩餘的資料沒有顯示出來
|
||
index += 80;
|
||
if (index >= userCount) {
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n還有剩餘的%s筆資料尚未顯示出來,是否繼續向下顯示80筆? (Y/N)");
|
||
CurrentOperationFunc = HandleIsContinue;
|
||
}
|
||
}
|
||
);
|
||
}
|
||
|
||
});
|
||
}
|
||
//#endregion
|
||
|
||
/**
|
||
* 查詢伺服器訊息指令"db"的相關操作用法。
|
||
*/
|
||
function DBOp_Help() {
|
||
console.log("\ndb - 與資料庫操作有關的指令");
|
||
console.log("db find <SchemaName | 'schemas'> [condition] : 尋找指定資料表中的資料,或列出所有的資料表名稱。");
|
||
console.log("db user <operation> : 與使用者資料相關的操作。\n");
|
||
console.log("db help : 查詢此指令的相關操作方式。\n");
|
||
}
|
||
|
||
//#endregion ======================================================================
|
||
|
||
//#region ============================= Restore Command =============================
|
||
/**
|
||
* 將整個JMuseum伺服器恢復的最初始、最原本的狀態。
|
||
*/
|
||
function ServerRestoreCommand() {
|
||
let times = 3;
|
||
/** 再三地確認是否要恢復JMuseum伺服器。 */
|
||
function ConfirmRestore(line) {
|
||
if (line == "Y" || line == "y") {
|
||
time -= 1;
|
||
if (times > 0) {
|
||
console.log("\n是否確定要將JMuseum伺服器恢復到最初始的狀態? (為了確保非為輸入錯誤,因此會詢問%s次) (Y/N)", times);
|
||
}
|
||
else {
|
||
RESTORE_SERVER();
|
||
}
|
||
}
|
||
else if (line == "N" || line == "n") {
|
||
console.log();
|
||
CurrentOperationFunc = CommandRoutes;
|
||
}
|
||
else {
|
||
console.log("\n是否確定要將JMuseum伺服器恢復到最初始的狀態? (為了確保非為輸入錯誤,因此會詢問%s次)", times);
|
||
}
|
||
}
|
||
|
||
console.log("\n是否確定要將JMuseum伺服器恢復到最初始的狀態? (為了確保非為輸入錯誤,因此會詢問%s次)", times);
|
||
CurrentOperationFunc = ConfirmRestore;
|
||
}
|
||
|
||
//#endregion ========================================================================
|
||
|
||
//#region ============================== Help Command ==============================
|
||
/**
|
||
* 打印出所有可用的指令種類。
|
||
*/
|
||
function HelpInformation() {
|
||
console.log("\n所有可用的指令操作種類:");
|
||
console.log("showstatus : 查看目前JMuseum伺服器的狀態。");
|
||
console.log(" act : 繪圖藝廊活動有關的操作,如查看狀態、推進活動等等。");
|
||
console.log(" servmsg : 伺服訊息有關的操作,如對使用者撰寫訊息、發起伺服廣播訊息等等。")
|
||
console.log(" db : 資料庫操作相關的指令群,如搜尋、刪除、加入等等。");
|
||
console.log(" restore : 將JMuseum伺服器回復到最初始的狀態。");
|
||
console.log(" help : 查詢指令操作說明。");
|
||
console.log(" .exit : 關閉JMuseum伺服器。\n");
|
||
}
|
||
//#endregion =======================================================================
|
||
|
||
//#region ============================== SERVER RESTORE ==============================
|
||
|
||
/**
|
||
* 呼叫此函式會還原、回覆整個伺服器狀態與資料庫內的資料。
|
||
*/
|
||
function RESTORE_SERVER() {
|
||
// 讀取還原預設資料檔案
|
||
fileSystem.readFile(global.__dirname + "/db/restore_datas.json", { encoding: "utf8" }, (err, datas) => {
|
||
if (err) return console.log("\n讀取還原之預設資料時發生了錯誤。請檢查在專案目錄之下的\"/db/restore_datas.json\"是否資料格式正確,或至Github上重新下載一個新的還原預設資料。\n");
|
||
let restoreDatas = JSON.parse(datas); // 取得原始資料
|
||
|
||
// 先將資料庫中所有的資料清除
|
||
CLEAR_COLLECTIONS().then(CREATE_RESTORING_DATAS(restoreDatas))
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 將資料庫中所有資料清除。
|
||
* @return {Promise} 讓外頭的函式可以繼續執行下一步。
|
||
*/
|
||
function CLEAR_COLLECTIONS() {
|
||
let emptyCondition = {};
|
||
/** 發生錯誤的時候的處理。 */
|
||
function OnError(error) {
|
||
console.log("\n清除資料庫中所有資料時發生了錯誤。請檢查是否有與MongoDB連線。\n");
|
||
return false;
|
||
}
|
||
|
||
return DBModels.User.remove(emptyCondition).exec()
|
||
.then(result => result ? DBModels.Painting.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.Season.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.Theme.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.Participation.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.Comment.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.ParticipantInfo.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.Rating.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.SiteMessage.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.SiteMail.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.ServerMessage.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.PaintingSpotlight.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? DBModels.NewTheme.remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? mongoose.connection.collection("sessions").remove(emptyCondition).exec() : false, OnError)
|
||
.then(result => result ? Promise.resolve(true) : false , OnError);
|
||
}
|
||
|
||
/**
|
||
* 建立回復伺服器的基本資料。
|
||
* @param {Object} restoreDatas 回復伺服器時所用的資料。
|
||
* @return {Function => Promise} Promise物件。
|
||
*/
|
||
function CREATE_RESTORING_DATAS(restoreDatas) {
|
||
/** 發生錯誤時的處理函式。 */
|
||
function OnError(error) {
|
||
console.log("\n在建立回復伺服器之基本資料時發生了錯誤。請檢查是否有連接上MongoDB。\n");
|
||
return false;
|
||
}
|
||
|
||
return function (result) {
|
||
if (!result) return false;
|
||
return DBModels.User.insertMany(restoreDatas.User)
|
||
.then(result => result ? DBModels.Painting.insertMany(restoreDatas.Painting) : false, OnError)
|
||
.then(result => result ? DBModels.Season.insertMany(restoreDatas.Season) : false, OnError)
|
||
.then(result => result ? DBModels.Theme.insertMany(restoreDatas.Theme) : false, OnError)
|
||
.then(result => result ? DBModels.ParticipantInfo.insertMany(restoreDatas.ParticipantInfo) : false, OnError)
|
||
.then(result => result ? DBModels.Participation.insertMany(restoreDatas.Participation) : false, OnError)
|
||
.then(result => result ? DBModels.PaintingSpotlight.insertMany(restoreDatas.PaintingSpotlight) : false, OnError)
|
||
.then(result => result ? DBModels.ServerMessage.insertMany(restoreDatas.ServerMessage) : false, OnError)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 將有關的回復資料做連結。
|
||
*/
|
||
function CONNECT_RELATIVE_DATAS() {
|
||
let User = DBModels.User, Painting = DBModels.Painting, Season = DBModels.Season;
|
||
let Theme = DBModels.Theme, ParticipantInfo = DBModels.ParticipantInfo, Participation = DBModels.Participation;
|
||
let PaintingSpotlight = DBModels.PaintingSpotlight, ServerMessage = DBModels.ServerMessage;
|
||
|
||
let userDocs, paintingDocs, seasonDocs, themeDocs;
|
||
let participantInfoDocs, participationDocs, serverMessageDocs;
|
||
|
||
/** 當讀取資料庫之資料時發生錯誤的錯誤處理。 */
|
||
function OnLoadError(error) {
|
||
console.log("\n讀取資料庫中的資料時發生錯誤。請檢查是否有連接上MongoDB。\n");
|
||
return false;
|
||
}
|
||
|
||
/** 當資料儲存時所發生的錯誤之錯誤處理。 */
|
||
function OnSaveError(error) {
|
||
console.log("\n將資料回存至資料庫時發生了錯誤。請檢查是否有連接上MongoDB。\n");
|
||
}
|
||
|
||
// 將與使用者有關的繪圖、主頁訊息加入其中
|
||
DBModels.User.find({}).select("username paintings siteMsg").exec()
|
||
.then(docs => docs ? (userDocs = docs, DBModels.Painting.find({}).select("artist activity").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (paintingDocs = docs, DBModels.Season.findOne({}).select("themes").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (seasonDocs = docs, DBModels.Theme.find({}).select("title participants").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (themeDocs = docs, DBModels.ParticipantInfo.find({}).select("paintingName").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (participantInfoDocs = docs, DBModels.Participation.find({}).select("themeName activityRank").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (participationDocs = docs, DBModels.ServerMessage.findOne({}).select("_id").exec()) : false, OnLoadError)
|
||
.then(docs => docs ? (serverMessageDocs = docs, true) : false, OnLoadError)
|
||
.then(result => {
|
||
if (!result) return false;
|
||
// 建立新的主頁訊息資料,並將其主頁資料加入到使用者資料中
|
||
let newSiteMessage = { isServerMessage: true, refId: serverMessageDocs._id, postTime: new Date(), isSeen: true, isPrivate: false };
|
||
userDocs.map((docs) => {
|
||
|
||
});
|
||
});
|
||
}
|
||
|
||
//#endregion =========================================================================
|
||
|
||
module.exports = Init; |