const express = require("express"); const app = express(); const fileUpload = require("express-fileupload"); const rateLimit = require("express-rate-limit"); const fs = require("fs-extra"); const session = require("express-session"); const eta = require("eta"); const bodyParser = require("body-parser"); const { google } = require("googleapis"); const nodemailer = require("nodemailer"); const cookieParser = require("cookie-parser"); const flash = require("connect-flash"); const Passport = require("passport"); const bcrypt = require("bcrypt"); const axios = require("axios"); const mimeDB = require("mime-db"); const http = require("http"); const server = http.createServer(app); const imageExt = ["png", "gif", "webp", "jpeg", "jpg"]; const videoExt = ["webm", "mkv", "flv", "vob", "ogv", "ogg", "rrc", "gifv", "mng", "mov", "avi", "qt", "wmv", "yuv", "rm", "asf", "amv", "mp4", "m4p", "m4v", "mpg", "mp2", "mpeg", "mpe", "mpv", "m4v", "svi", "3gp", "3g2", "mxf", "roq", "nsv", "flv", "f4v", "f4p", "f4a", "f4b", "mod" ]; const audioExt = ["3gp", "aa", "aac", "aax", "act", "aiff", "alac", "amr", "ape", "au", "awb", "dss", "dvf", "flac", "gsm", "iklax", "ivs", "m4a", "m4b", "m4p", "mmf", "mp3", "mpc", "msv", "nmf", "ogg", "oga", "mogg", "opus", "ra", "rm", "raw", "rf64", "sln", "tta", "voc", "vox", "wav", "wma", "wv", "webm", "8svx", "cd" ]; module.exports = async (api) => { if (!api) await require("./connectDB.js")(); const { utils, utils: { drive } } = global; const { config } = global.GoatBot; const { expireVerifyCode } = config.dashBoard; const { gmailAccount, gRecaptcha } = config.credentials; const getText = global.utils.getText; const { email, clientId, clientSecret, refreshToken } = gmailAccount; const OAuth2 = google.auth.OAuth2; const OAuth2_client = new OAuth2(clientId, clientSecret); OAuth2_client.setCredentials({ refresh_token: refreshToken }); let accessToken; try { accessToken = await OAuth2_client.getAccessToken(); } catch (err) { throw new Error(getText("Goat", "googleApiRefreshTokenExpired")); } const transporter = nodemailer.createTransport({ host: "smtp.gmail.com", service: "Gmail", auth: { type: "OAuth2", user: email, clientId, clientSecret, refreshToken, accessToken } }); const { threadModel, userModel, dashBoardModel, threadsData, usersData, dashBoardData } = global.db; // const verifyCodes = { // fbid: [], // register: [], // forgetPass: [] // }; eta.configure({ useWith: true }); app.set("views", `${__dirname}/views`); app.engine("eta", eta.renderFile); app.set("view engine", "eta"); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cookieParser()); app.use(session({ secret: randomStringApikey(10), resave: false, saveUninitialized: true, cookie: { secure: false, httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days } })); // public folder app.use("/css", express.static(`${__dirname}/css`)); app.use("/js", express.static(`${__dirname}/js`)); app.use("/images", express.static(`${__dirname}/images`)); require("./passport-config.js")(Passport, dashBoardData, bcrypt); app.use(Passport.initialize()); app.use(Passport.session()); app.use(fileUpload()); app.use(flash()); app.use(function (req, res, next) { res.locals.gRecaptcha_siteKey = gRecaptcha.siteKey; res.locals.__dirname = __dirname; res.locals.success = req.flash("success") || []; res.locals.errors = req.flash("errors") || []; res.locals.warnings = req.flash("warnings") || []; res.locals.user = req.user || null; next(); }); const generateEmailVerificationCode = require("./scripts/generate-Email-Verification.js"); // ————————————————— MIDDLEWARE ————————————————— // const createLimiter = (ms, max) => rateLimit({ windowMs: ms, // 5 minutes max, handler: (req, res) => { res.status(429).send({ status: "error", message: getText("app", "tooManyRequests") }); } }); const middleWare = require("./middleware/index.js")(checkAuthConfigDashboardOfThread); // ————————————————————————————————————————————— // async function checkAuthConfigDashboardOfThread(threadData, userID) { if (!isNaN(threadData)) threadData = await threadsData.get(threadData); return threadData.adminIDs?.includes(userID) || threadData.members?.some(m => m.userID == userID && m.permissionConfigDashboard == true) || false; } const isVideoFile = (mimeType) => videoExt.includes(mimeDB[mimeType]?.extensions?.[0]); async function isVerifyRecaptcha(responseCaptcha) { const secret = gRecaptcha.secretKey; const verifyUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secret}&response;=${responseCaptcha}`; const verify = await axios.get(verifyUrl); return verify.data.success; } // ROUTES & MIDDLWARE const { unAuthenticated, isWaitVerifyAccount, isAuthenticated, isAdmin, isVeryfiUserIDFacebook, checkHasAndInThread, middlewareCheckAuthConfigDashboardOfThread } = middleWare; const paramsForRoutes = { unAuthenticated, isWaitVerifyAccount, isAdmin, isAuthenticated, isVeryfiUserIDFacebook, checkHasAndInThread, middlewareCheckAuthConfigDashboardOfThread, isVerifyRecaptcha, validateEmail, randomNumberApikey, transporter, generateEmailVerificationCode, dashBoardData, expireVerifyCode, Passport, isVideoFile, threadsData, api, createLimiter, config, checkAuthConfigDashboardOfThread, imageExt, videoExt, audioExt, convertSize, drive, usersData }; const registerRoute = require("./routes/register.js")(paramsForRoutes); const loginRoute = require("./routes/login.js")(paramsForRoutes); const forgotPasswordRoute = require("./routes/forgotPassword.js")(paramsForRoutes); const changePasswordRoute = require("./routes/changePassword.js")(paramsForRoutes); const dashBoardRoute = require("./routes/dashBoard.js")(paramsForRoutes); const verifyFbidRoute = require("./routes/verifyfbid.js")(paramsForRoutes); const apiRouter = require("./routes/api.js")(paramsForRoutes); app.get(["/", "/home"], (req, res) => { res.render("home"); }); app.get("/stats", async (req, res) => { let fcaVersion; try { fcaVersion = require("fb-chat-api/package.json").version; } catch (e) { fcaVersion = "unknown"; } const totalThread = (await threadsData.getAll()).filter(t => t.threadID.toString().length > 15).length; const totalUser = (await usersData.getAll()).length; const prefix = config.prefix; const uptime = utils.convertTime(process.uptime() * 1000); res.render("stats", { fcaVersion, totalThread, totalUser, prefix, uptime, uptimeSecond: process.uptime() }); }); app.get("/profile", isAuthenticated, async (req, res) => { res.render("profile", { userData: await usersData.get(req.user.facebookUserID) || {} }); }); app.get("/donate", (req, res) => res.render("donate")); app.get("/logout", (req, res, next) => { req.logout(function (err) { if (err) return next(err); res.redirect("/"); }); }); app.post("/changefbstate", isAuthenticated, isVeryfiUserIDFacebook, (req, res) => { if (!global.GoatBot.config.adminBot.includes(req.user.facebookUserID)) return res.send({ status: "error", message: getText("app", "notPermissionChangeFbstate") }); const { fbstate } = req.body; if (!fbstate) return res.send({ status: "error", message: getText("app", "notFoundFbstate") }); fs.writeFileSync(process.cwd() + (process.env.NODE_ENV == "production" || process.env.NODE_ENV == "development" ? "/account.dev.txt" : "/account.txt"), fbstate); res.send({ status: "success", message: getText("app", "changedFbstateSuccess") }); res.on("finish", () => { process.exit(2); }); }); app.get("/uptime", global.responseUptimeCurrent); app.get("/changefbstate", isAuthenticated, isVeryfiUserIDFacebook, isAdmin, (req, res) => { res.render("changeFbstate", { currentFbstate: fs.readFileSync(process.cwd() + (process.env.NODE_ENV == "production" || process.env.NODE_ENV == "development" ? "/account.dev.txt" : "/account.txt"), "utf8") }); }); app.use("/register", registerRoute); app.use("/login", loginRoute); app.use("/forgot-password", forgotPasswordRoute); app.use("/change-password", changePasswordRoute); app.use("/dashboard", dashBoardRoute); app.use("/verifyfbid", verifyFbidRoute); app.use("/api", apiRouter); app.get("*", (req, res) => { res.status(404).render("404"); }); // catch global error app.use((err, req, res, next) => { if (err.message == "Login sessions require session support. Did you forget to use `express-session` middleware?") return res.status(500).send(getText("app", "serverError")); }); const PORT = process.env.PORT || 3001; let dashBoardUrl = `https://${process.env.REPL_OWNER ? `${process.env.REPL_SLUG}.${process.env.REPL_OWNER}.repl.co` : process.env.API_SERVER_EXTERNAL == "https://api.glitch.com" ? `${process.env.PROJECT_DOMAIN}.glitch.me` : `localhost:${PORT}`}`; dashBoardUrl.includes("localhost") && (dashBoardUrl = dashBoardUrl.replace("https", "http")); await server.listen(PORT); utils.log.info("DASHBOARD", `Dashboard is running: ${dashBoardUrl}`); if (config.serverUptime.socket.enable == true) require("../bot/login/socketIO.js")(server); }; function randomStringApikey(max) { let text = ""; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < max; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } function randomNumberApikey(maxLength) { let text = ""; const possible = "0123456789"; for (let i = 0; i < maxLength; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } function validateEmail(email) { const re = /^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); } function convertSize(byte) { return byte > 1024 ? byte > 1024 * 1024 ? (byte / 1024 / 1024).toFixed(2) + " MB" : (byte / 1024).toFixed(2) + " KB" : byte + " Byte"; }