mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-11-22 19:27:55 +11:00
312 lines
7.3 KiB
JavaScript
312 lines
7.3 KiB
JavaScript
const express = require("express")
|
|
const router = express.Router()
|
|
const Errors = require("../lib/errors.js")
|
|
const auth = require("../lib/authorize.js")
|
|
const { User, Theme, Message, Feedback } = require("../models")
|
|
const { Op } = require("sequelize")
|
|
const dayjs = require("dayjs")
|
|
const fs = require("fs")
|
|
const os = require("os")
|
|
const argon2 = require("argon2")
|
|
|
|
router.all("*", auth, async (req, res, next) => {
|
|
try {
|
|
if (!req.user.admin) {
|
|
throw Errors.unauthorized
|
|
} else {
|
|
next()
|
|
}
|
|
} catch (err) {
|
|
next(err)
|
|
}
|
|
})
|
|
|
|
router.all("*", auth, async (req, res, next) => {
|
|
try {
|
|
if (!req.user.emailVerified && req.app.locals.config.emailVerification) {
|
|
throw Errors.emailVerificationRequired
|
|
} else {
|
|
next()
|
|
}
|
|
} catch (e) {
|
|
next(e)
|
|
}
|
|
})
|
|
|
|
router.get("/feedback", auth, async (req, res, next) => {
|
|
try {
|
|
const feedback = await Feedback.findAndCountAll({
|
|
order: [["createdAt", "DESC"]],
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: "user"
|
|
}
|
|
]
|
|
})
|
|
res.json(feedback)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
router.get("/", auth, async (req, res, next) => {
|
|
try {
|
|
res.json({
|
|
users: await User.count(),
|
|
themes: await Theme.count(),
|
|
messages: await Message.count(),
|
|
usersToday: await User.count({
|
|
where: {
|
|
lastSeenAt: {
|
|
[Op.gte]: dayjs().startOf("day").toDate()
|
|
}
|
|
}
|
|
}),
|
|
usersThisWeek: await User.count({
|
|
where: {
|
|
lastSeenAt: {
|
|
[Op.gte]: dayjs().startOf("week").toDate()
|
|
}
|
|
}
|
|
})
|
|
})
|
|
} catch (e) {
|
|
next(e)
|
|
}
|
|
})
|
|
|
|
router.put("/user/:id", auth, async (req, res, next) => {
|
|
try {
|
|
const user = await User.findOne({
|
|
where: {
|
|
id: req.params.id
|
|
}
|
|
})
|
|
if (!user) {
|
|
throw Errors.communicationsUserNotFound
|
|
} else {
|
|
await user.update({
|
|
banned: req.body.banned
|
|
})
|
|
res.json(user)
|
|
}
|
|
} catch (e) {
|
|
next(e)
|
|
}
|
|
})
|
|
|
|
router.post("/user", auth, async (req, res, next) => {
|
|
try {
|
|
const user = await User.create({
|
|
username: req.body.username,
|
|
password: await argon2.hash(req.body.password),
|
|
email: req.body.email,
|
|
emailVerified: req.body.emailVerified,
|
|
admin: false,
|
|
banned: false,
|
|
lastSeenAt: new Date()
|
|
})
|
|
res.json(user)
|
|
} catch (e) {
|
|
next(e)
|
|
}
|
|
})
|
|
|
|
router.get("/metrics", auth, async (req, res, next) => {
|
|
try {
|
|
const sevenDaysAgo = new Date(new Date().setDate(new Date().getDate() - 7))
|
|
const createdAt = {
|
|
[Op.gte]: sevenDaysAgo
|
|
}
|
|
|
|
const registrationStats = await User.findAll({
|
|
where: {
|
|
createdAt: createdAt
|
|
},
|
|
attributes: {
|
|
exclude: ["totp", "password"]
|
|
}
|
|
})
|
|
const messages = await Message.findAll({
|
|
where: {
|
|
createdAt: createdAt
|
|
},
|
|
attributes: {
|
|
exclude: ["totp", "password"]
|
|
}
|
|
})
|
|
|
|
const registrationGraphInterim = registrationStats.reduce(function (
|
|
result,
|
|
user
|
|
) {
|
|
let day = dayjs(user.createdAt).format("YYYY-MM-DD")
|
|
if (!result[day]) {
|
|
result[day] = 0
|
|
}
|
|
result[day]++
|
|
return result
|
|
},
|
|
{})
|
|
|
|
const messagesGraphInterim = messages.reduce(function (result, message) {
|
|
let day = dayjs(message.createdAt).format("YYYY-MM-DD")
|
|
if (!result[day]) {
|
|
result[day] = 0
|
|
}
|
|
result[day]++
|
|
return result
|
|
}, {})
|
|
|
|
const usersGraph = {
|
|
labels: Object.keys(registrationGraphInterim),
|
|
datasets: [
|
|
{
|
|
data: Object.values(registrationGraphInterim),
|
|
label: "Users",
|
|
borderColor: "#3e95cd",
|
|
pointBackgroundColor: "#FFFFFF",
|
|
backgroundColor: "transparent"
|
|
}
|
|
]
|
|
}
|
|
|
|
const messagesGraph = {
|
|
labels: Object.keys(messagesGraphInterim),
|
|
datasets: [
|
|
{
|
|
data: Object.values(messagesGraphInterim),
|
|
label: "Messages",
|
|
borderColor: "#3e95cd",
|
|
pointBackgroundColor: "#FFFFFF",
|
|
backgroundColor: "transparent"
|
|
}
|
|
]
|
|
}
|
|
|
|
res.json({
|
|
users: usersGraph,
|
|
activeUsers: messagesGraph
|
|
})
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
router.get("/users", auth, async (req, res, next) => {
|
|
try {
|
|
const users = await User.findAndCountAll({
|
|
include: [
|
|
{
|
|
model: Theme,
|
|
as: "themeObject"
|
|
}
|
|
],
|
|
attributes: {
|
|
exclude: ["totp", "password"]
|
|
}
|
|
})
|
|
res.json(users)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
router.get("/themes", auth, async (req, res, next) => {
|
|
try {
|
|
const themes = await Theme.findAndCountAll({
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: "user",
|
|
attributes: {
|
|
exclude: ["totp", "password"]
|
|
}
|
|
},
|
|
{
|
|
model: User,
|
|
as: "users"
|
|
}
|
|
]
|
|
})
|
|
res.json(themes)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
router.put("/themes/apply", auth, async (req, res, next) => {
|
|
try {
|
|
await User.update(
|
|
{
|
|
themeId: req.body.themeId
|
|
},
|
|
{
|
|
where: {
|
|
id: req.user.id
|
|
}
|
|
}
|
|
)
|
|
res.sendStatus(204)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
router.put("/state", auth, async (req, res, next) => {
|
|
function setEnvValue(key, value) {
|
|
// read file from hdd & split if from a linebreak to a array
|
|
const ENV_VARS = fs.readFileSync(".env", "utf8").split(os.EOL)
|
|
|
|
// find the env we want based on the key
|
|
const target = ENV_VARS.indexOf(
|
|
ENV_VARS.find((line) => {
|
|
// (?<!#\s*) Negative lookbehind to avoid matching comments (lines that starts with #).
|
|
// There is a double slash in the RegExp constructor to escape it.
|
|
// (?==) Positive lookahead to check if there is an equal sign right after the key.
|
|
// This is to prevent matching keys prefixed with the key of the env var to update.
|
|
const keyValRegex = new RegExp(`(?<!#\\s*)${key}(?==)`)
|
|
|
|
return line.match(keyValRegex)
|
|
})
|
|
)
|
|
|
|
// if key-value pair exists in the .env file,
|
|
if (target !== -1) {
|
|
// replace the key/value with the new value
|
|
ENV_VARS.splice(target, 1, `${key}=${value}`)
|
|
} else {
|
|
// if it doesn't exist, add it instead
|
|
ENV_VARS.push(`${key}=${value}`)
|
|
}
|
|
|
|
// write everything back to the file system
|
|
fs.writeFileSync(".env", ENV_VARS.join(os.EOL))
|
|
}
|
|
try {
|
|
const io = req.app.get("io")
|
|
req.app.locals.config.allowRegistrations = req.body.allowRegistrations
|
|
req.app.locals.config.rules = req.body.rules
|
|
if (req.body.broadcastType === "permanent") {
|
|
req.app.locals.config.notification = req.body.notification
|
|
req.app.locals.config.notificationType = req.body.notificationType
|
|
}
|
|
io.emit("siteState", {
|
|
notification: req.body.notification,
|
|
notificationType: req.body.notificationType,
|
|
latestVersion: require("../../frontend/package.json").version,
|
|
allowRegistrations: req.body.allowRegistrations,
|
|
rules: req.body.rules
|
|
})
|
|
fs.writeFileSync(
|
|
"./config/config.json",
|
|
JSON.stringify(req.app.locals.config, null, 2)
|
|
)
|
|
res.sendStatus(204)
|
|
} catch (err) {
|
|
return next(err)
|
|
}
|
|
})
|
|
|
|
module.exports = router
|