mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-20 15:26:15 +08:00
Remove class syntax from controllers (#2791)
* Remove class syntax from controllers * Specify base and rename
This commit is contained in:
parent
433b2fc130
commit
beb7e35dcb
82
backend/api/controllers/ape-key.ts
Normal file
82
backend/api/controllers/ape-key.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import _ from "lodash";
|
||||
import { randomBytes } from "crypto";
|
||||
import { hash } from "bcrypt";
|
||||
import ApeKeysDAO from "../../dao/ape-keys";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
import { base64UrlEncode } from "../../utils/misc";
|
||||
|
||||
function cleanApeKey(apeKey: MonkeyTypes.ApeKey): Partial<MonkeyTypes.ApeKey> {
|
||||
return _.omit(apeKey, "hash", "_id", "uid", "useCount");
|
||||
}
|
||||
|
||||
export async function getApeKeys(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const apeKeys = await ApeKeysDAO.getApeKeys(uid);
|
||||
const cleanedKeys = _(apeKeys).keyBy("_id").mapValues(cleanApeKey).value();
|
||||
|
||||
return new MonkeyResponse("ApeKeys retrieved", cleanedKeys);
|
||||
}
|
||||
|
||||
export async function generateApeKey(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name, enabled } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { maxKeysPerUser, apeKeyBytes, apeKeySaltRounds } =
|
||||
req.ctx.configuration.apeKeys;
|
||||
|
||||
const currentNumberOfApeKeys = await ApeKeysDAO.countApeKeysForUser(uid);
|
||||
|
||||
if (currentNumberOfApeKeys >= maxKeysPerUser) {
|
||||
throw new MonkeyError(409, "Maximum number of ApeKeys have been generated");
|
||||
}
|
||||
|
||||
const apiKey = randomBytes(apeKeyBytes).toString("base64url");
|
||||
const saltyHash = await hash(apiKey, apeKeySaltRounds);
|
||||
|
||||
const apeKey: MonkeyTypes.ApeKey = {
|
||||
name,
|
||||
enabled,
|
||||
uid,
|
||||
hash: saltyHash,
|
||||
createdOn: Date.now(),
|
||||
modifiedOn: Date.now(),
|
||||
lastUsedOn: -1,
|
||||
useCount: 0,
|
||||
};
|
||||
|
||||
const apeKeyId = await ApeKeysDAO.addApeKey(apeKey);
|
||||
|
||||
return new MonkeyResponse("ApeKey generated", {
|
||||
apeKey: base64UrlEncode(`${apeKeyId}.${apiKey}`),
|
||||
apeKeyId,
|
||||
apeKeyDetails: cleanApeKey(apeKey),
|
||||
});
|
||||
}
|
||||
|
||||
export async function editApeKey(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { apeKeyId } = req.params;
|
||||
const { name, enabled } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ApeKeysDAO.editApeKey(uid, apeKeyId, name, enabled);
|
||||
|
||||
return new MonkeyResponse("ApeKey updated");
|
||||
}
|
||||
|
||||
export async function deleteApeKey(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { apeKeyId } = req.params;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ApeKeysDAO.deleteApeKey(uid, apeKeyId);
|
||||
|
||||
return new MonkeyResponse("ApeKey deleted");
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import _ from "lodash";
|
||||
import { randomBytes } from "crypto";
|
||||
import { hash } from "bcrypt";
|
||||
import ApeKeysDAO from "../../dao/ape-keys";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
import { base64UrlEncode } from "../../utils/misc";
|
||||
|
||||
function cleanApeKey(apeKey: MonkeyTypes.ApeKey): Partial<MonkeyTypes.ApeKey> {
|
||||
return _.omit(apeKey, "hash", "_id", "uid", "useCount");
|
||||
}
|
||||
|
||||
class ApeKeysController {
|
||||
static async getApeKeys(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const apeKeys = await ApeKeysDAO.getApeKeys(uid);
|
||||
const hashlessKeys = _(apeKeys).keyBy("_id").mapValues(cleanApeKey).value();
|
||||
|
||||
return new MonkeyResponse("ApeKeys retrieved", hashlessKeys);
|
||||
}
|
||||
|
||||
static async generateApeKey(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name, enabled } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { maxKeysPerUser, apeKeyBytes, apeKeySaltRounds } =
|
||||
req.ctx.configuration.apeKeys;
|
||||
|
||||
const currentNumberOfApeKeys = await ApeKeysDAO.countApeKeysForUser(uid);
|
||||
|
||||
if (currentNumberOfApeKeys >= maxKeysPerUser) {
|
||||
throw new MonkeyError(
|
||||
409,
|
||||
"Maximum number of ApeKeys have been generated"
|
||||
);
|
||||
}
|
||||
|
||||
const apiKey = randomBytes(apeKeyBytes).toString("base64url");
|
||||
const saltyHash = await hash(apiKey, apeKeySaltRounds);
|
||||
|
||||
const apeKey: MonkeyTypes.ApeKey = {
|
||||
name,
|
||||
enabled,
|
||||
uid,
|
||||
hash: saltyHash,
|
||||
createdOn: Date.now(),
|
||||
modifiedOn: Date.now(),
|
||||
lastUsedOn: -1,
|
||||
useCount: 0,
|
||||
};
|
||||
|
||||
const apeKeyId = await ApeKeysDAO.addApeKey(apeKey);
|
||||
|
||||
return new MonkeyResponse("ApeKey generated", {
|
||||
apeKey: base64UrlEncode(`${apeKeyId}.${apiKey}`),
|
||||
apeKeyId,
|
||||
apeKeyDetails: cleanApeKey(apeKey),
|
||||
});
|
||||
}
|
||||
|
||||
static async editApeKey(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { apeKeyId } = req.params;
|
||||
const { name, enabled } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ApeKeysDAO.editApeKey(uid, apeKeyId, name, enabled);
|
||||
|
||||
return new MonkeyResponse("ApeKey updated");
|
||||
}
|
||||
|
||||
static async deleteApeKey(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { apeKeyId } = req.params;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ApeKeysDAO.deleteApeKey(uid, apeKeyId);
|
||||
|
||||
return new MonkeyResponse("ApeKey deleted");
|
||||
}
|
||||
}
|
||||
|
||||
export default ApeKeysController;
|
|
@ -1,22 +1,22 @@
|
|||
import ConfigDAO from "../../dao/config";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
|
||||
class ConfigController {
|
||||
static async getConfig(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
export async function getConfig(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await ConfigDAO.getConfig(uid);
|
||||
return new MonkeyResponse("Configuration retrieved", data);
|
||||
}
|
||||
|
||||
static async saveConfig(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ConfigDAO.saveConfig(uid, config);
|
||||
|
||||
return new MonkeyResponse("Config updated");
|
||||
}
|
||||
const data = await ConfigDAO.getConfig(uid);
|
||||
return new MonkeyResponse("Configuration retrieved", data);
|
||||
}
|
||||
|
||||
export default ConfigController;
|
||||
export async function saveConfig(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ConfigDAO.saveConfig(uid, config);
|
||||
|
||||
return new MonkeyResponse("Config updated");
|
||||
}
|
||||
|
|
54
backend/api/controllers/leaderboard.ts
Normal file
54
backend/api/controllers/leaderboard.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import _ from "lodash";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
import LeaderboardsDAO from "../../dao/leaderboards";
|
||||
|
||||
export async function getLeaderboard(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { language, mode, mode2, skip, limit = 50 } = req.query;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const queryLimit = Math.min(parseInt(limit as string, 10), 50);
|
||||
|
||||
const leaderboard = await LeaderboardsDAO.get(
|
||||
mode,
|
||||
mode2,
|
||||
language,
|
||||
parseInt(skip as string, 10),
|
||||
queryLimit
|
||||
);
|
||||
|
||||
if (leaderboard === false) {
|
||||
return new MonkeyResponse(
|
||||
"Leaderboard is currently updating. Please try again in a few seconds.",
|
||||
null,
|
||||
503
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedLeaderboard = _.map(leaderboard as any[], (entry) => {
|
||||
return uid && entry.uid === uid
|
||||
? entry
|
||||
: _.omit(entry, ["discordId", "uid", "difficulty", "language"]);
|
||||
});
|
||||
|
||||
return new MonkeyResponse("Leaderboard retrieved", normalizedLeaderboard);
|
||||
}
|
||||
|
||||
export async function getRankFromLeaderboard(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { language, mode, mode2 } = req.query;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await LeaderboardsDAO.getRank(mode, mode2, language, uid);
|
||||
if (data === false) {
|
||||
return new MonkeyResponse(
|
||||
"Leaderboard is currently updating. Please try again in a few seconds.",
|
||||
null,
|
||||
503
|
||||
);
|
||||
}
|
||||
|
||||
return new MonkeyResponse("Rank retrieved", data);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
import _ from "lodash";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
import LeaderboardsDAO from "../../dao/leaderboards";
|
||||
|
||||
class LeaderboardsController {
|
||||
static async get(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { language, mode, mode2, skip, limit = 50 } = req.query;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const queryLimit = Math.min(parseInt(limit as string, 10), 50);
|
||||
|
||||
const leaderboard = await LeaderboardsDAO.get(
|
||||
mode,
|
||||
mode2,
|
||||
language,
|
||||
parseInt(skip as string, 10),
|
||||
queryLimit
|
||||
);
|
||||
|
||||
if (leaderboard === false) {
|
||||
return new MonkeyResponse(
|
||||
"Leaderboard is currently updating. Please try again in a few seconds.",
|
||||
null,
|
||||
503
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedLeaderboard = _.map(leaderboard as any[], (entry) => {
|
||||
return uid && entry.uid === uid
|
||||
? entry
|
||||
: _.omit(entry, ["discordId", "uid", "difficulty", "language"]);
|
||||
});
|
||||
|
||||
return new MonkeyResponse("Leaderboard retrieved", normalizedLeaderboard);
|
||||
}
|
||||
|
||||
static async getRank(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { language, mode, mode2 } = req.query;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await LeaderboardsDAO.getRank(mode, mode2, language, uid);
|
||||
if (data === false) {
|
||||
return new MonkeyResponse(
|
||||
"Leaderboard is currently updating. Please try again in a few seconds.",
|
||||
null,
|
||||
503
|
||||
);
|
||||
}
|
||||
|
||||
return new MonkeyResponse("Rank retrieved", data);
|
||||
}
|
||||
}
|
||||
|
||||
export default LeaderboardsController;
|
|
@ -1,40 +1,44 @@
|
|||
import PresetDAO from "../../dao/preset";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
|
||||
class PresetController {
|
||||
static async getPresets(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
export async function getPresets(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await PresetDAO.getPresets(uid);
|
||||
return new MonkeyResponse("Preset retrieved", data);
|
||||
}
|
||||
|
||||
static async addPreset(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { name, config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await PresetDAO.addPreset(uid, name, config);
|
||||
|
||||
return new MonkeyResponse("Preset created", data);
|
||||
}
|
||||
|
||||
static async editPreset(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { _id, name, config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.editPreset(uid, _id, name, config);
|
||||
|
||||
return new MonkeyResponse("Preset updated");
|
||||
}
|
||||
|
||||
static async removePreset(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { presetId } = req.params;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.removePreset(uid, presetId);
|
||||
|
||||
return new MonkeyResponse("Preset deleted");
|
||||
}
|
||||
const data = await PresetDAO.getPresets(uid);
|
||||
return new MonkeyResponse("Preset retrieved", data);
|
||||
}
|
||||
|
||||
export default PresetController;
|
||||
export async function addPreset(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name, config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const data = await PresetDAO.addPreset(uid, name, config);
|
||||
|
||||
return new MonkeyResponse("Preset created", data);
|
||||
}
|
||||
|
||||
export async function editPreset(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { _id, name, config } = req.body;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.editPreset(uid, _id, name, config);
|
||||
|
||||
return new MonkeyResponse("Preset updated");
|
||||
}
|
||||
|
||||
export async function removePreset(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { presetId } = req.params;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.removePreset(uid, presetId);
|
||||
|
||||
return new MonkeyResponse("Preset deleted");
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import PsaDAO from "../../dao/psa";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
|
||||
class PsaController {
|
||||
static async get(_req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const data = await PsaDAO.get();
|
||||
return new MonkeyResponse("PSAs retrieved", data);
|
||||
}
|
||||
export async function getPsas(
|
||||
_req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const data = await PsaDAO.get();
|
||||
return new MonkeyResponse("PSAs retrieved", data);
|
||||
}
|
||||
|
||||
export default PsaController;
|
||||
|
|
138
backend/api/controllers/quote.ts
Normal file
138
backend/api/controllers/quote.ts
Normal file
|
@ -0,0 +1,138 @@
|
|||
import _ from "lodash";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import UserDAO from "../../dao/user";
|
||||
import ReportDAO from "../../dao/report";
|
||||
import NewQuotesDao from "../../dao/new-quotes";
|
||||
import QuoteRatingsDAO from "../../dao/quote-ratings";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import { verify } from "../../utils/captcha";
|
||||
import Logger from "../../utils/logger";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
|
||||
async function verifyCaptcha(captcha: string): Promise<void> {
|
||||
if (!(await verify(captcha))) {
|
||||
throw new MonkeyError(422, "Captcha check failed");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getQuotes(
|
||||
_req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const data = await NewQuotesDao.get();
|
||||
return new MonkeyResponse("Quote submissions retrieved", data);
|
||||
}
|
||||
|
||||
export async function addQuote(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { text, source, language, captcha } = req.body;
|
||||
|
||||
await verifyCaptcha(captcha);
|
||||
|
||||
await NewQuotesDao.add(text, source, language, uid);
|
||||
return new MonkeyResponse("Quote submission added");
|
||||
}
|
||||
|
||||
export async function approveQuote(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, editText, editSource } = req.body;
|
||||
|
||||
const data = await NewQuotesDao.approve(quoteId, editText, editSource);
|
||||
Logger.logToDb("system_quote_approved", data, uid);
|
||||
|
||||
return new MonkeyResponse(data.message, data.quote);
|
||||
}
|
||||
|
||||
export async function refuseQuote(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { quoteId } = req.body;
|
||||
|
||||
await NewQuotesDao.refuse(quoteId);
|
||||
return new MonkeyResponse("Quote refused");
|
||||
}
|
||||
|
||||
export async function getRating(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { quoteId, language } = req.query;
|
||||
|
||||
const data = await QuoteRatingsDAO.get(
|
||||
parseInt(quoteId as string, 10),
|
||||
language as string
|
||||
);
|
||||
|
||||
return new MonkeyResponse("Rating retrieved", data);
|
||||
}
|
||||
|
||||
export async function submitRating(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, rating, language } = req.body;
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
if (!user) {
|
||||
throw new MonkeyError(401, "User not found.");
|
||||
}
|
||||
|
||||
const normalizedQuoteId = parseInt(quoteId as string, 10);
|
||||
const normalizedRating = Math.round(parseInt(rating as string, 10));
|
||||
|
||||
const userQuoteRatings = user.quoteRatings ?? {};
|
||||
const currentRating = userQuoteRatings[language]?.[normalizedQuoteId] ?? 0;
|
||||
|
||||
const newRating = normalizedRating - currentRating;
|
||||
const shouldUpdateRating = currentRating !== 0;
|
||||
|
||||
await QuoteRatingsDAO.submit(
|
||||
quoteId,
|
||||
language,
|
||||
newRating,
|
||||
shouldUpdateRating
|
||||
);
|
||||
|
||||
_.setWith(
|
||||
userQuoteRatings,
|
||||
`[${language}][${normalizedQuoteId}]`,
|
||||
normalizedRating,
|
||||
Object
|
||||
);
|
||||
|
||||
await UserDAO.updateQuoteRatings(uid, userQuoteRatings);
|
||||
|
||||
const responseMessage = `Rating ${
|
||||
shouldUpdateRating ? "updated" : "submitted"
|
||||
}`;
|
||||
return new MonkeyResponse(responseMessage);
|
||||
}
|
||||
|
||||
export async function reportQuote(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const {
|
||||
quoteReport: { maxReports, contentReportLimit },
|
||||
} = req.ctx.configuration;
|
||||
|
||||
const { quoteId, quoteLanguage, reason, comment, captcha } = req.body;
|
||||
|
||||
await verifyCaptcha(captcha);
|
||||
|
||||
const newReport: MonkeyTypes.Report = {
|
||||
id: uuidv4(),
|
||||
type: "quote",
|
||||
timestamp: new Date().getTime(),
|
||||
uid,
|
||||
contentId: `${quoteLanguage}-${quoteId}`,
|
||||
reason,
|
||||
comment,
|
||||
};
|
||||
|
||||
await ReportDAO.createReport(newReport, maxReports, contentReportLimit);
|
||||
|
||||
return new MonkeyResponse("Quote reported");
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
import _ from "lodash";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import UserDAO from "../../dao/user";
|
||||
import ReportDAO from "../../dao/report";
|
||||
import NewQuotesDao from "../../dao/new-quotes";
|
||||
import QuoteRatingsDAO from "../../dao/quote-ratings";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import { verify } from "../../utils/captcha";
|
||||
import Logger from "../../utils/logger";
|
||||
import { MonkeyResponse } from "../../utils/monkey-response";
|
||||
|
||||
async function verifyCaptcha(captcha: string): Promise<void> {
|
||||
if (!(await verify(captcha))) {
|
||||
throw new MonkeyError(422, "Captcha check failed");
|
||||
}
|
||||
}
|
||||
|
||||
class QuotesController {
|
||||
static async getQuotes(_req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const data = await NewQuotesDao.get();
|
||||
return new MonkeyResponse("Quote submissions retrieved", data);
|
||||
}
|
||||
|
||||
static async addQuote(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { text, source, language, captcha } = req.body;
|
||||
|
||||
await verifyCaptcha(captcha);
|
||||
|
||||
await NewQuotesDao.add(text, source, language, uid);
|
||||
return new MonkeyResponse("Quote submission added");
|
||||
}
|
||||
|
||||
static async approveQuote(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, editText, editSource } = req.body;
|
||||
|
||||
const data = await NewQuotesDao.approve(quoteId, editText, editSource);
|
||||
Logger.logToDb("system_quote_approved", data, uid);
|
||||
|
||||
return new MonkeyResponse(data.message, data.quote);
|
||||
}
|
||||
|
||||
static async refuseQuote(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { quoteId } = req.body;
|
||||
|
||||
await NewQuotesDao.refuse(quoteId);
|
||||
return new MonkeyResponse("Quote refused");
|
||||
}
|
||||
|
||||
static async getRating(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { quoteId, language } = req.query;
|
||||
|
||||
const data = await QuoteRatingsDAO.get(
|
||||
parseInt(quoteId as string),
|
||||
language as string
|
||||
);
|
||||
|
||||
return new MonkeyResponse("Rating retrieved", data);
|
||||
}
|
||||
|
||||
static async submitRating(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, rating, language } = req.body;
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
if (!user) {
|
||||
throw new MonkeyError(401, "User not found.");
|
||||
}
|
||||
|
||||
const normalizedQuoteId = parseInt(quoteId as string);
|
||||
const normalizedRating = Math.round(parseInt(rating as string));
|
||||
|
||||
const userQuoteRatings = user.quoteRatings ?? {};
|
||||
const currentRating = userQuoteRatings[language]?.[normalizedQuoteId] ?? 0;
|
||||
|
||||
const newRating = normalizedRating - currentRating;
|
||||
const shouldUpdateRating = currentRating !== 0;
|
||||
|
||||
await QuoteRatingsDAO.submit(
|
||||
quoteId,
|
||||
language,
|
||||
newRating,
|
||||
shouldUpdateRating
|
||||
);
|
||||
|
||||
_.setWith(
|
||||
userQuoteRatings,
|
||||
`[${language}][${normalizedQuoteId}]`,
|
||||
normalizedRating,
|
||||
Object
|
||||
);
|
||||
|
||||
await UserDAO.updateQuoteRatings(uid, userQuoteRatings);
|
||||
|
||||
const responseMessage = `Rating ${
|
||||
shouldUpdateRating ? "updated" : "submitted"
|
||||
}`;
|
||||
return new MonkeyResponse(responseMessage);
|
||||
}
|
||||
|
||||
static async reportQuote(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const {
|
||||
quoteReport: { maxReports, contentReportLimit },
|
||||
} = req.ctx.configuration;
|
||||
|
||||
const { quoteId, quoteLanguage, reason, comment, captcha } = req.body;
|
||||
|
||||
await verifyCaptcha(captcha);
|
||||
|
||||
const newReport: MonkeyTypes.Report = {
|
||||
id: uuidv4(),
|
||||
type: "quote",
|
||||
timestamp: new Date().getTime(),
|
||||
uid,
|
||||
contentId: `${quoteLanguage}-${quoteId}`,
|
||||
reason,
|
||||
comment,
|
||||
};
|
||||
|
||||
await ReportDAO.createReport(newReport, maxReports, contentReportLimit);
|
||||
|
||||
return new MonkeyResponse("Quote reported");
|
||||
}
|
||||
}
|
||||
|
||||
export default QuotesController;
|
|
@ -34,74 +34,181 @@ try {
|
|||
}
|
||||
}
|
||||
|
||||
class ResultController {
|
||||
static async getResults(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const results = await ResultDAO.getResults(uid);
|
||||
return new MonkeyResponse("Result retrieved", results);
|
||||
export async function getResults(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const results = await ResultDAO.getResults(uid);
|
||||
return new MonkeyResponse("Result retrieved", results);
|
||||
}
|
||||
|
||||
export async function deleteAll(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ResultDAO.deleteAll(uid);
|
||||
Logger.logToDb("user_results_deleted", "", uid);
|
||||
return new MonkeyResponse("All results deleted");
|
||||
}
|
||||
|
||||
export async function updateTags(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagIds, resultId } = req.body;
|
||||
|
||||
await ResultDAO.updateTags(uid, resultId, tagIds);
|
||||
return new MonkeyResponse("Result tags updated");
|
||||
}
|
||||
|
||||
export async function addResult(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const useRedisForBotTasks = req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const result = Object.assign({}, req.body.result);
|
||||
result.uid = uid;
|
||||
if (result.wpm === result.raw && result.acc !== 100) {
|
||||
const status = MonkeyStatusCodes.RESULT_DATA_INVALID;
|
||||
throw new MonkeyError(status.code, "Bad input"); // todo move this
|
||||
}
|
||||
if (isTestTooShort(result)) {
|
||||
const status = MonkeyStatusCodes.TEST_TOO_SHORT;
|
||||
throw new MonkeyError(status.code, status.message);
|
||||
}
|
||||
|
||||
static async deleteAll(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await ResultDAO.deleteAll(uid);
|
||||
Logger.logToDb("user_results_deleted", "", uid);
|
||||
return new MonkeyResponse("All results deleted");
|
||||
const resulthash = result.hash;
|
||||
delete result.hash;
|
||||
delete result.stringified;
|
||||
if (
|
||||
req.ctx.configuration.resultObjectHashCheck.enabled &&
|
||||
resulthash.length === 40
|
||||
) {
|
||||
//if its not 64 that means client is still using old hashing package
|
||||
const serverhash = objectHash(result);
|
||||
if (serverhash !== resulthash) {
|
||||
Logger.logToDb(
|
||||
"incorrect_result_hash",
|
||||
{
|
||||
serverhash,
|
||||
resulthash,
|
||||
result,
|
||||
},
|
||||
uid
|
||||
);
|
||||
const status = MonkeyStatusCodes.RESULT_HASH_INVALID;
|
||||
throw new MonkeyError(status.code, "Incorrect result hash");
|
||||
}
|
||||
}
|
||||
|
||||
static async updateTags(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagIds, resultId } = req.body;
|
||||
|
||||
await ResultDAO.updateTags(uid, resultId, tagIds);
|
||||
return new MonkeyResponse("Result tags updated");
|
||||
}
|
||||
|
||||
static async addResult(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const useRedisForBotTasks =
|
||||
req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const result = Object.assign({}, req.body.result);
|
||||
result.uid = uid;
|
||||
if (result.wpm === result.raw && result.acc !== 100) {
|
||||
if (anticheatImplemented()) {
|
||||
if (!validateResult(result)) {
|
||||
const status = MonkeyStatusCodes.RESULT_DATA_INVALID;
|
||||
throw new MonkeyError(status.code, "Bad input"); // todo move this
|
||||
throw new MonkeyError(status.code, "Result data doesn't make sense");
|
||||
}
|
||||
if (isTestTooShort(result)) {
|
||||
const status = MonkeyStatusCodes.TEST_TOO_SHORT;
|
||||
throw new MonkeyError(status.code, status.message);
|
||||
} else {
|
||||
if (process.env.MODE !== "dev") {
|
||||
throw new Error("No anticheat module found");
|
||||
}
|
||||
Logger.warning(
|
||||
"No anticheat module found. Continuing in dev mode, results will not be validated."
|
||||
);
|
||||
}
|
||||
|
||||
const resulthash = result.hash;
|
||||
delete result.hash;
|
||||
delete result.stringified;
|
||||
if (
|
||||
req.ctx.configuration.resultObjectHashCheck.enabled &&
|
||||
resulthash.length === 40
|
||||
) {
|
||||
//if its not 64 that means client is still using old hashing package
|
||||
const serverhash = objectHash(result);
|
||||
if (serverhash !== resulthash) {
|
||||
Logger.logToDb(
|
||||
"incorrect_result_hash",
|
||||
{
|
||||
serverhash,
|
||||
resulthash,
|
||||
result,
|
||||
},
|
||||
uid
|
||||
);
|
||||
const status = MonkeyStatusCodes.RESULT_HASH_INVALID;
|
||||
throw new MonkeyError(status.code, "Incorrect result hash");
|
||||
}
|
||||
}
|
||||
//dont use - result timestamp is unreliable, can be changed by system time and stuff
|
||||
// if (result.timestamp > Math.round(Date.now() / 1000) * 1000 + 10) {
|
||||
// log(
|
||||
// "time_traveler",
|
||||
// {
|
||||
// resultTimestamp: result.timestamp,
|
||||
// serverTimestamp: Math.round(Date.now() / 1000) * 1000 + 10,
|
||||
// },
|
||||
// uid
|
||||
// );
|
||||
// return res.status(400).json({ message: "Time traveler detected" });
|
||||
|
||||
// this probably wont work if we replace the timestamp with the server time later
|
||||
// let timestampres = await ResultDAO.getResultByTimestamp(
|
||||
// uid,
|
||||
// result.timestamp
|
||||
// );
|
||||
// if (timestampres) {
|
||||
// return res.status(400).json({ message: "Duplicate result" });
|
||||
// }
|
||||
|
||||
//convert result test duration to miliseconds
|
||||
const testDurationMilis = result.testDuration * 1000;
|
||||
//get latest result ordered by timestamp
|
||||
let lastResultTimestamp;
|
||||
try {
|
||||
lastResultTimestamp = (await ResultDAO.getLastResult(uid)).timestamp;
|
||||
} catch (e) {
|
||||
lastResultTimestamp = null;
|
||||
}
|
||||
|
||||
result.timestamp = Math.floor(Date.now() / 1000) * 1000;
|
||||
|
||||
//check if now is earlier than last result plus duration (-1 second as a buffer)
|
||||
const earliestPossible = lastResultTimestamp + testDurationMilis;
|
||||
const nowNoMilis = Math.floor(Date.now() / 1000) * 1000;
|
||||
if (lastResultTimestamp && nowNoMilis < earliestPossible - 1000) {
|
||||
Logger.logToDb(
|
||||
"invalid_result_spacing",
|
||||
{
|
||||
lastTimestamp: lastResultTimestamp,
|
||||
earliestPossible,
|
||||
now: nowNoMilis,
|
||||
testDuration: testDurationMilis,
|
||||
difference: nowNoMilis - earliestPossible,
|
||||
},
|
||||
uid
|
||||
);
|
||||
const status = MonkeyStatusCodes.RESULT_SPACING_INVALID;
|
||||
throw new MonkeyError(status.code, "Invalid result spacing");
|
||||
}
|
||||
|
||||
try {
|
||||
result.keySpacingStats = {
|
||||
average:
|
||||
result.keySpacing.reduce((previous, current) => (current += previous)) /
|
||||
result.keySpacing.length,
|
||||
sd: stdDev(result.keySpacing),
|
||||
};
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
result.keyDurationStats = {
|
||||
average:
|
||||
result.keyDuration.reduce(
|
||||
(previous, current) => (current += previous)
|
||||
) / result.keyDuration.length,
|
||||
sd: stdDev(result.keyDuration),
|
||||
};
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
|
||||
//check keyspacing and duration here for bots
|
||||
if (
|
||||
result.mode === "time" &&
|
||||
result.wpm > 130 &&
|
||||
result.testDuration < 122 &&
|
||||
(user.verified === false || user.verified === undefined)
|
||||
) {
|
||||
if (!result.keySpacingStats || !result.keyDurationStats) {
|
||||
const status = MonkeyStatusCodes.MISSING_KEY_DATA;
|
||||
throw new MonkeyError(status.code, "Missing key data");
|
||||
}
|
||||
if (anticheatImplemented()) {
|
||||
if (!validateResult(result)) {
|
||||
const status = MonkeyStatusCodes.RESULT_DATA_INVALID;
|
||||
throw new MonkeyError(status.code, "Result data doesn't make sense");
|
||||
if (!validateKeys(result, uid)) {
|
||||
const status = MonkeyStatusCodes.BOT_DETECTED;
|
||||
throw new MonkeyError(status.code, "Possible bot detected");
|
||||
}
|
||||
} else {
|
||||
if (process.env.MODE !== "dev") {
|
||||
|
@ -111,202 +218,95 @@ class ResultController {
|
|||
"No anticheat module found. Continuing in dev mode, results will not be validated."
|
||||
);
|
||||
}
|
||||
|
||||
//dont use - result timestamp is unreliable, can be changed by system time and stuff
|
||||
// if (result.timestamp > Math.round(Date.now() / 1000) * 1000 + 10) {
|
||||
// log(
|
||||
// "time_traveler",
|
||||
// {
|
||||
// resultTimestamp: result.timestamp,
|
||||
// serverTimestamp: Math.round(Date.now() / 1000) * 1000 + 10,
|
||||
// },
|
||||
// uid
|
||||
// );
|
||||
// return res.status(400).json({ message: "Time traveler detected" });
|
||||
|
||||
// this probably wont work if we replace the timestamp with the server time later
|
||||
// let timestampres = await ResultDAO.getResultByTimestamp(
|
||||
// uid,
|
||||
// result.timestamp
|
||||
// );
|
||||
// if (timestampres) {
|
||||
// return res.status(400).json({ message: "Duplicate result" });
|
||||
// }
|
||||
|
||||
//convert result test duration to miliseconds
|
||||
const testDurationMilis = result.testDuration * 1000;
|
||||
//get latest result ordered by timestamp
|
||||
let lastResultTimestamp;
|
||||
try {
|
||||
lastResultTimestamp = (await ResultDAO.getLastResult(uid)).timestamp;
|
||||
} catch (e) {
|
||||
lastResultTimestamp = null;
|
||||
}
|
||||
|
||||
result.timestamp = Math.floor(Date.now() / 1000) * 1000;
|
||||
|
||||
//check if now is earlier than last result plus duration (-1 second as a buffer)
|
||||
const earliestPossible = lastResultTimestamp + testDurationMilis;
|
||||
const nowNoMilis = Math.floor(Date.now() / 1000) * 1000;
|
||||
if (lastResultTimestamp && nowNoMilis < earliestPossible - 1000) {
|
||||
Logger.logToDb(
|
||||
"invalid_result_spacing",
|
||||
{
|
||||
lastTimestamp: lastResultTimestamp,
|
||||
earliestPossible,
|
||||
now: nowNoMilis,
|
||||
testDuration: testDurationMilis,
|
||||
difference: nowNoMilis - earliestPossible,
|
||||
},
|
||||
uid
|
||||
);
|
||||
const status = MonkeyStatusCodes.RESULT_SPACING_INVALID;
|
||||
throw new MonkeyError(status.code, "Invalid result spacing");
|
||||
}
|
||||
|
||||
try {
|
||||
result.keySpacingStats = {
|
||||
average:
|
||||
result.keySpacing.reduce(
|
||||
(previous, current) => (current += previous)
|
||||
) / result.keySpacing.length,
|
||||
sd: stdDev(result.keySpacing),
|
||||
};
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
result.keyDurationStats = {
|
||||
average:
|
||||
result.keyDuration.reduce(
|
||||
(previous, current) => (current += previous)
|
||||
) / result.keyDuration.length,
|
||||
sd: stdDev(result.keyDuration),
|
||||
};
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
|
||||
//check keyspacing and duration here for bots
|
||||
if (
|
||||
result.mode === "time" &&
|
||||
result.wpm > 130 &&
|
||||
result.testDuration < 122 &&
|
||||
(user.verified === false || user.verified === undefined)
|
||||
) {
|
||||
if (!result.keySpacingStats || !result.keyDurationStats) {
|
||||
const status = MonkeyStatusCodes.MISSING_KEY_DATA;
|
||||
throw new MonkeyError(status.code, "Missing key data");
|
||||
}
|
||||
if (anticheatImplemented()) {
|
||||
if (!validateKeys(result, uid)) {
|
||||
const status = MonkeyStatusCodes.BOT_DETECTED;
|
||||
throw new MonkeyError(status.code, "Possible bot detected");
|
||||
}
|
||||
} else {
|
||||
if (process.env.MODE !== "dev") {
|
||||
throw new Error("No anticheat module found");
|
||||
}
|
||||
Logger.warning(
|
||||
"No anticheat module found. Continuing in dev mode, results will not be validated."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delete result.keySpacing;
|
||||
delete result.keyDuration;
|
||||
delete result.smoothConsistency;
|
||||
delete result.wpmConsistency;
|
||||
|
||||
try {
|
||||
result.keyDurationStats.average = roundTo2(
|
||||
result.keyDurationStats.average
|
||||
);
|
||||
result.keyDurationStats.sd = roundTo2(result.keyDurationStats.sd);
|
||||
result.keySpacingStats.average = roundTo2(result.keySpacingStats.average);
|
||||
result.keySpacingStats.sd = roundTo2(result.keySpacingStats.sd);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
let isPb = false;
|
||||
let tagPbs: any[] = [];
|
||||
|
||||
if (!result.bailedOut) {
|
||||
[isPb, tagPbs] = await Promise.all([
|
||||
UserDAO.checkIfPb(uid, user, result),
|
||||
UserDAO.checkIfTagPb(uid, user, result),
|
||||
]);
|
||||
}
|
||||
|
||||
if (isPb) {
|
||||
result.isPb = true;
|
||||
}
|
||||
|
||||
if (result.mode === "time" && String(result.mode2) === "60") {
|
||||
UserDAO.incrementBananas(uid, result.wpm);
|
||||
if (isPb && user.discordId) {
|
||||
if (useRedisForBotTasks) {
|
||||
George.updateDiscordRole(user.discordId, result.wpm);
|
||||
}
|
||||
BotDAO.updateDiscordRole(user.discordId, result.wpm);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.challenge && user.discordId) {
|
||||
if (useRedisForBotTasks) {
|
||||
George.awardChallenge(user.discordId, result.challenge);
|
||||
}
|
||||
BotDAO.awardChallenge(user.discordId, result.challenge);
|
||||
} else {
|
||||
delete result.challenge;
|
||||
}
|
||||
|
||||
let tt = 0;
|
||||
let afk = result.afkDuration;
|
||||
if (afk == undefined) {
|
||||
afk = 0;
|
||||
}
|
||||
tt = result.testDuration + result.incompleteTestSeconds - afk;
|
||||
UserDAO.updateTypingStats(uid, result.restartCount, tt);
|
||||
PublicStatsDAO.updateStats(result.restartCount, tt);
|
||||
|
||||
if (result.bailedOut === false) delete result.bailedOut;
|
||||
if (result.blindMode === false) delete result.blindMode;
|
||||
if (result.lazyMode === false) delete result.lazyMode;
|
||||
if (result.difficulty === "normal") delete result.difficulty;
|
||||
if (result.funbox === "none") delete result.funbox;
|
||||
if (result.language === "english") delete result.language;
|
||||
if (result.numbers === false) delete result.numbers;
|
||||
if (result.punctuation === false) delete result.punctuation;
|
||||
|
||||
if (result.mode !== "custom") delete result.customText;
|
||||
|
||||
const addedResult = await ResultDAO.addResult(uid, result);
|
||||
|
||||
if (isPb) {
|
||||
Logger.logToDb(
|
||||
"user_new_pb",
|
||||
`${result.mode + " " + result.mode2} ${result.wpm} ${result.acc}% ${
|
||||
result.rawWpm
|
||||
} ${result.consistency}% (${addedResult.insertedId})`,
|
||||
uid
|
||||
);
|
||||
}
|
||||
|
||||
const data = {
|
||||
isPb,
|
||||
name: result.name,
|
||||
tagPbs,
|
||||
insertedId: addedResult.insertedId,
|
||||
};
|
||||
|
||||
incrementResult(result);
|
||||
|
||||
return new MonkeyResponse("Result saved", data);
|
||||
}
|
||||
}
|
||||
|
||||
export default ResultController;
|
||||
delete result.keySpacing;
|
||||
delete result.keyDuration;
|
||||
delete result.smoothConsistency;
|
||||
delete result.wpmConsistency;
|
||||
|
||||
try {
|
||||
result.keyDurationStats.average = roundTo2(result.keyDurationStats.average);
|
||||
result.keyDurationStats.sd = roundTo2(result.keyDurationStats.sd);
|
||||
result.keySpacingStats.average = roundTo2(result.keySpacingStats.average);
|
||||
result.keySpacingStats.sd = roundTo2(result.keySpacingStats.sd);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
let isPb = false;
|
||||
let tagPbs: any[] = [];
|
||||
|
||||
if (!result.bailedOut) {
|
||||
[isPb, tagPbs] = await Promise.all([
|
||||
UserDAO.checkIfPb(uid, user, result),
|
||||
UserDAO.checkIfTagPb(uid, user, result),
|
||||
]);
|
||||
}
|
||||
|
||||
if (isPb) {
|
||||
result.isPb = true;
|
||||
}
|
||||
|
||||
if (result.mode === "time" && String(result.mode2) === "60") {
|
||||
UserDAO.incrementBananas(uid, result.wpm);
|
||||
if (isPb && user.discordId) {
|
||||
if (useRedisForBotTasks) {
|
||||
George.updateDiscordRole(user.discordId, result.wpm);
|
||||
}
|
||||
BotDAO.updateDiscordRole(user.discordId, result.wpm);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.challenge && user.discordId) {
|
||||
if (useRedisForBotTasks) {
|
||||
George.awardChallenge(user.discordId, result.challenge);
|
||||
}
|
||||
BotDAO.awardChallenge(user.discordId, result.challenge);
|
||||
} else {
|
||||
delete result.challenge;
|
||||
}
|
||||
|
||||
let tt = 0;
|
||||
let afk = result.afkDuration;
|
||||
if (afk == undefined) {
|
||||
afk = 0;
|
||||
}
|
||||
tt = result.testDuration + result.incompleteTestSeconds - afk;
|
||||
UserDAO.updateTypingStats(uid, result.restartCount, tt);
|
||||
PublicStatsDAO.updateStats(result.restartCount, tt);
|
||||
|
||||
if (result.bailedOut === false) delete result.bailedOut;
|
||||
if (result.blindMode === false) delete result.blindMode;
|
||||
if (result.lazyMode === false) delete result.lazyMode;
|
||||
if (result.difficulty === "normal") delete result.difficulty;
|
||||
if (result.funbox === "none") delete result.funbox;
|
||||
if (result.language === "english") delete result.language;
|
||||
if (result.numbers === false) delete result.numbers;
|
||||
if (result.punctuation === false) delete result.punctuation;
|
||||
|
||||
if (result.mode !== "custom") delete result.customText;
|
||||
|
||||
const addedResult = await ResultDAO.addResult(uid, result);
|
||||
|
||||
if (isPb) {
|
||||
Logger.logToDb(
|
||||
"user_new_pb",
|
||||
`${result.mode + " " + result.mode2} ${result.wpm} ${result.acc}% ${
|
||||
result.rawWpm
|
||||
} ${result.consistency}% (${addedResult.insertedId})`,
|
||||
uid
|
||||
);
|
||||
}
|
||||
|
||||
const data = {
|
||||
isPb,
|
||||
name: result.name,
|
||||
tagPbs,
|
||||
insertedId: addedResult.insertedId,
|
||||
};
|
||||
|
||||
incrementResult(result);
|
||||
|
||||
return new MonkeyResponse("Result saved", data);
|
||||
}
|
||||
|
|
|
@ -7,268 +7,286 @@ import { linkAccount } from "../../utils/discord";
|
|||
import { buildAgentLog } from "../../utils/misc";
|
||||
import George from "../../tasks/george";
|
||||
|
||||
class UserController {
|
||||
static async createNewUser(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name } = req.body;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
export async function createNewUser(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name } = req.body;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
|
||||
await UsersDAO.addUser(name, email, uid);
|
||||
Logger.logToDb("user_created", `${name} ${email}`, uid);
|
||||
await UsersDAO.addUser(name, email, uid);
|
||||
Logger.logToDb("user_created", `${name} ${email}`, uid);
|
||||
|
||||
return new MonkeyResponse("User created");
|
||||
}
|
||||
|
||||
static async deleteUser(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.deleteUser(uid);
|
||||
Logger.logToDb("user_deleted", `${userInfo.email} ${userInfo.name}`, uid);
|
||||
|
||||
return new MonkeyResponse("User deleted");
|
||||
}
|
||||
|
||||
static async updateName(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { name } = req.body;
|
||||
|
||||
const oldUser = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.updateName(uid, name);
|
||||
Logger.logToDb(
|
||||
"user_name_updated",
|
||||
`changed name from ${oldUser.name} to ${name}`,
|
||||
uid
|
||||
);
|
||||
|
||||
return new MonkeyResponse("User's name updated");
|
||||
}
|
||||
|
||||
static async clearPb(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await UsersDAO.clearPb(uid);
|
||||
Logger.logToDb("user_cleared_pbs", "", uid);
|
||||
|
||||
return new MonkeyResponse("User's PB cleared");
|
||||
}
|
||||
|
||||
static async checkName(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { name } = req.params;
|
||||
|
||||
const available = await UsersDAO.isNameAvailable(name);
|
||||
if (!available) {
|
||||
throw new MonkeyError(409, "Username unavailable");
|
||||
}
|
||||
|
||||
return new MonkeyResponse("Username available");
|
||||
}
|
||||
|
||||
static async updateEmail(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { newEmail } = req.body;
|
||||
|
||||
try {
|
||||
await UsersDAO.updateEmail(uid, newEmail);
|
||||
} catch (e) {
|
||||
throw new MonkeyError(404, e.message, "update email", uid);
|
||||
}
|
||||
|
||||
Logger.logToDb("user_email_updated", `changed email to ${newEmail}`, uid);
|
||||
|
||||
return new MonkeyResponse("Email updated");
|
||||
}
|
||||
|
||||
static async getUser(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UsersDAO.getUser(uid);
|
||||
} catch (e) {
|
||||
if (email && uid) {
|
||||
userInfo = await UsersDAO.addUser(undefined, email, uid);
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
404,
|
||||
"User not found. Could not recreate user document.",
|
||||
"Tried to recreate user document but either email or uid is nullish",
|
||||
uid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const agentLog = buildAgentLog(req);
|
||||
Logger.logToDb("user_data_requested", agentLog, uid);
|
||||
|
||||
return new MonkeyResponse("User data retrieved", userInfo);
|
||||
}
|
||||
|
||||
static async linkDiscord(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const {
|
||||
data: { tokenType, accessToken },
|
||||
} = req.body;
|
||||
|
||||
const useRedisForBotTasks =
|
||||
req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
if (userInfo.banned) {
|
||||
throw new MonkeyError(403, "Banned accounts cannot link with Discord");
|
||||
}
|
||||
|
||||
const { id: discordId } = await linkAccount(tokenType, accessToken);
|
||||
|
||||
if (!discordId) {
|
||||
throw new MonkeyError(
|
||||
500,
|
||||
"Could not get Discord account info",
|
||||
"discord id is undefined"
|
||||
);
|
||||
}
|
||||
|
||||
const discordIdAvailable = await UsersDAO.isDiscordIdAvailable(discordId);
|
||||
if (!discordIdAvailable) {
|
||||
throw new MonkeyError(
|
||||
409,
|
||||
"This Discord account is already linked to a different account"
|
||||
);
|
||||
}
|
||||
|
||||
await UsersDAO.linkDiscord(uid, discordId);
|
||||
|
||||
if (useRedisForBotTasks) {
|
||||
George.linkDiscord(discordId, uid);
|
||||
}
|
||||
await BotDAO.linkDiscord(uid, discordId);
|
||||
Logger.logToDb("user_discord_link", `linked to ${discordId}`, uid);
|
||||
|
||||
return new MonkeyResponse("Discord account linked", discordId);
|
||||
}
|
||||
|
||||
static async unlinkDiscord(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const useRedisForBotTasks =
|
||||
req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
if (!userInfo.discordId) {
|
||||
throw new MonkeyError(404, "User does not have a linked Discord account");
|
||||
}
|
||||
|
||||
if (useRedisForBotTasks) {
|
||||
George.unlinkDiscord(userInfo.discordId, uid);
|
||||
}
|
||||
await BotDAO.unlinkDiscord(uid, userInfo.discordId);
|
||||
|
||||
await UsersDAO.unlinkDiscord(uid);
|
||||
Logger.logToDb("user_discord_unlinked", userInfo.discordId, uid);
|
||||
|
||||
return new MonkeyResponse("Discord account unlinked");
|
||||
}
|
||||
|
||||
static async addTag(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagName } = req.body;
|
||||
|
||||
const tag = await UsersDAO.addTag(uid, tagName);
|
||||
return new MonkeyResponse("Tag updated", tag);
|
||||
}
|
||||
|
||||
static async clearTagPb(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTagPb(uid, tagId);
|
||||
return new MonkeyResponse("Tag PB cleared");
|
||||
}
|
||||
|
||||
static async editTag(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId, newName } = req.body;
|
||||
|
||||
await UsersDAO.editTag(uid, tagId, newName);
|
||||
return new MonkeyResponse("Tag updated");
|
||||
}
|
||||
|
||||
static async removeTag(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTag(uid, tagId);
|
||||
return new MonkeyResponse("Tag deleted");
|
||||
}
|
||||
|
||||
static async getTags(req: MonkeyTypes.Request): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const tags = await UsersDAO.getTags(uid);
|
||||
return new MonkeyResponse("Tags retrieved", tags ?? []);
|
||||
}
|
||||
|
||||
static async updateLbMemory(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2, language, rank } = req.body;
|
||||
|
||||
await UsersDAO.updateLbMemory(uid, mode, mode2, language, rank);
|
||||
return new MonkeyResponse("Leaderboard memory updated");
|
||||
}
|
||||
|
||||
static async getCustomThemes(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const customThemes = await UsersDAO.getThemes(uid);
|
||||
return new MonkeyResponse("Custom themes retrieved", customThemes);
|
||||
}
|
||||
|
||||
static async addCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { name, colors } = req.body;
|
||||
|
||||
const addedTheme = await UsersDAO.addTheme(uid, { name, colors });
|
||||
return new MonkeyResponse("Custom theme added", {
|
||||
theme: addedTheme,
|
||||
});
|
||||
}
|
||||
|
||||
static async removeCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId } = req.body;
|
||||
await UsersDAO.removeTheme(uid, themeId);
|
||||
return new MonkeyResponse("Custom theme removed");
|
||||
}
|
||||
|
||||
static async editCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId, theme } = req.body;
|
||||
|
||||
await UsersDAO.editTheme(uid, themeId, theme);
|
||||
return new MonkeyResponse("Custom theme updated");
|
||||
}
|
||||
|
||||
static async getPersonalBests(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2 } = req.query;
|
||||
|
||||
const data = (await UsersDAO.getPersonalBests(uid, mode, mode2)) ?? null;
|
||||
return new MonkeyResponse("Personal bests retrieved", data);
|
||||
}
|
||||
return new MonkeyResponse("User created");
|
||||
}
|
||||
|
||||
export default UserController;
|
||||
export async function deleteUser(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.deleteUser(uid);
|
||||
Logger.logToDb("user_deleted", `${userInfo.email} ${userInfo.name}`, uid);
|
||||
|
||||
return new MonkeyResponse("User deleted");
|
||||
}
|
||||
|
||||
export async function updateName(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { name } = req.body;
|
||||
|
||||
const oldUser = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.updateName(uid, name);
|
||||
Logger.logToDb(
|
||||
"user_name_updated",
|
||||
`changed name from ${oldUser.name} to ${name}`,
|
||||
uid
|
||||
);
|
||||
|
||||
return new MonkeyResponse("User's name updated");
|
||||
}
|
||||
|
||||
export async function clearPb(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await UsersDAO.clearPb(uid);
|
||||
Logger.logToDb("user_cleared_pbs", "", uid);
|
||||
|
||||
return new MonkeyResponse("User's PB cleared");
|
||||
}
|
||||
|
||||
export async function checkName(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { name } = req.params;
|
||||
|
||||
const available = await UsersDAO.isNameAvailable(name);
|
||||
if (!available) {
|
||||
throw new MonkeyError(409, "Username unavailable");
|
||||
}
|
||||
|
||||
return new MonkeyResponse("Username available");
|
||||
}
|
||||
|
||||
export async function updateEmail(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { newEmail } = req.body;
|
||||
|
||||
try {
|
||||
await UsersDAO.updateEmail(uid, newEmail);
|
||||
} catch (e) {
|
||||
throw new MonkeyError(404, e.message, "update email", uid);
|
||||
}
|
||||
|
||||
Logger.logToDb("user_email_updated", `changed email to ${newEmail}`, uid);
|
||||
|
||||
return new MonkeyResponse("Email updated");
|
||||
}
|
||||
|
||||
export async function getUser(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UsersDAO.getUser(uid);
|
||||
} catch (e) {
|
||||
if (email && uid) {
|
||||
userInfo = await UsersDAO.addUser(undefined, email, uid);
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
404,
|
||||
"User not found. Could not recreate user document.",
|
||||
"Tried to recreate user document but either email or uid is nullish",
|
||||
uid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const agentLog = buildAgentLog(req);
|
||||
Logger.logToDb("user_data_requested", agentLog, uid);
|
||||
|
||||
return new MonkeyResponse("User data retrieved", userInfo);
|
||||
}
|
||||
|
||||
export async function linkDiscord(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const {
|
||||
data: { tokenType, accessToken },
|
||||
} = req.body;
|
||||
|
||||
const useRedisForBotTasks = req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
if (userInfo.banned) {
|
||||
throw new MonkeyError(403, "Banned accounts cannot link with Discord");
|
||||
}
|
||||
|
||||
const { id: discordId } = await linkAccount(tokenType, accessToken);
|
||||
|
||||
if (!discordId) {
|
||||
throw new MonkeyError(
|
||||
500,
|
||||
"Could not get Discord account info",
|
||||
"discord id is undefined"
|
||||
);
|
||||
}
|
||||
|
||||
const discordIdAvailable = await UsersDAO.isDiscordIdAvailable(discordId);
|
||||
if (!discordIdAvailable) {
|
||||
throw new MonkeyError(
|
||||
409,
|
||||
"This Discord account is already linked to a different account"
|
||||
);
|
||||
}
|
||||
|
||||
await UsersDAO.linkDiscord(uid, discordId);
|
||||
|
||||
if (useRedisForBotTasks) {
|
||||
George.linkDiscord(discordId, uid);
|
||||
}
|
||||
await BotDAO.linkDiscord(uid, discordId);
|
||||
Logger.logToDb("user_discord_link", `linked to ${discordId}`, uid);
|
||||
|
||||
return new MonkeyResponse("Discord account linked", discordId);
|
||||
}
|
||||
|
||||
export async function unlinkDiscord(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const useRedisForBotTasks = req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
if (!userInfo.discordId) {
|
||||
throw new MonkeyError(404, "User does not have a linked Discord account");
|
||||
}
|
||||
|
||||
if (useRedisForBotTasks) {
|
||||
George.unlinkDiscord(userInfo.discordId, uid);
|
||||
}
|
||||
await BotDAO.unlinkDiscord(uid, userInfo.discordId);
|
||||
|
||||
await UsersDAO.unlinkDiscord(uid);
|
||||
Logger.logToDb("user_discord_unlinked", userInfo.discordId, uid);
|
||||
|
||||
return new MonkeyResponse("Discord account unlinked");
|
||||
}
|
||||
|
||||
export async function addTag(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagName } = req.body;
|
||||
|
||||
const tag = await UsersDAO.addTag(uid, tagName);
|
||||
return new MonkeyResponse("Tag updated", tag);
|
||||
}
|
||||
|
||||
export async function clearTagPb(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTagPb(uid, tagId);
|
||||
return new MonkeyResponse("Tag PB cleared");
|
||||
}
|
||||
|
||||
export async function editTag(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId, newName } = req.body;
|
||||
|
||||
await UsersDAO.editTag(uid, tagId, newName);
|
||||
return new MonkeyResponse("Tag updated");
|
||||
}
|
||||
|
||||
export async function removeTag(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTag(uid, tagId);
|
||||
return new MonkeyResponse("Tag deleted");
|
||||
}
|
||||
|
||||
export async function getTags(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const tags = await UsersDAO.getTags(uid);
|
||||
return new MonkeyResponse("Tags retrieved", tags ?? []);
|
||||
}
|
||||
|
||||
export async function updateLbMemory(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2, language, rank } = req.body;
|
||||
|
||||
await UsersDAO.updateLbMemory(uid, mode, mode2, language, rank);
|
||||
return new MonkeyResponse("Leaderboard memory updated");
|
||||
}
|
||||
|
||||
export async function getCustomThemes(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const customThemes = await UsersDAO.getThemes(uid);
|
||||
return new MonkeyResponse("Custom themes retrieved", customThemes);
|
||||
}
|
||||
|
||||
export async function addCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { name, colors } = req.body;
|
||||
|
||||
const addedTheme = await UsersDAO.addTheme(uid, { name, colors });
|
||||
return new MonkeyResponse("Custom theme added", {
|
||||
theme: addedTheme,
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId } = req.body;
|
||||
await UsersDAO.removeTheme(uid, themeId);
|
||||
return new MonkeyResponse("Custom theme removed");
|
||||
}
|
||||
|
||||
export async function editCustomTheme(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId, theme } = req.body;
|
||||
|
||||
await UsersDAO.editTheme(uid, themeId, theme);
|
||||
return new MonkeyResponse("Custom theme updated");
|
||||
}
|
||||
|
||||
export async function getPersonalBests(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2 } = req.query;
|
||||
|
||||
const data = (await UsersDAO.getPersonalBests(uid, mode, mode2)) ?? null;
|
||||
return new MonkeyResponse("Personal bests retrieved", data);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
validateRequest,
|
||||
} from "../../middlewares/api-utils";
|
||||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import ApeKeysController from "../controllers/ape-keys";
|
||||
import * as ApeKeyController from "../controllers/ape-key";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
|
||||
const apeKeyNameSchema = joi
|
||||
|
@ -43,7 +43,7 @@ router.get(
|
|||
RateLimit.apeKeysGet,
|
||||
authenticateRequest(),
|
||||
checkIfUserCanManageApeKeys,
|
||||
asyncHandler(ApeKeysController.getApeKeys)
|
||||
asyncHandler(ApeKeyController.getApeKeys)
|
||||
);
|
||||
|
||||
router.post(
|
||||
|
@ -57,7 +57,7 @@ router.post(
|
|||
enabled: joi.boolean().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(ApeKeysController.generateApeKey)
|
||||
asyncHandler(ApeKeyController.generateApeKey)
|
||||
);
|
||||
|
||||
router.patch(
|
||||
|
@ -74,7 +74,7 @@ router.patch(
|
|||
enabled: joi.boolean(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(ApeKeysController.editApeKey)
|
||||
asyncHandler(ApeKeyController.editApeKey)
|
||||
);
|
||||
|
||||
router.delete(
|
||||
|
@ -87,7 +87,7 @@ router.delete(
|
|||
apeKeyId: joi.string().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(ApeKeysController.deleteApeKey)
|
||||
asyncHandler(ApeKeyController.deleteApeKey)
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Router } from "express";
|
|||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import { asyncHandler, validateRequest } from "../../middlewares/api-utils";
|
||||
import configSchema from "../schemas/config-schema";
|
||||
import ConfigController from "../controllers/config";
|
||||
import * as ConfigController from "../controllers/config";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
|
||||
const router = Router();
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Router } from "express";
|
|||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
import apeRateLimit from "../../middlewares/ape-rate-limit";
|
||||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import LeaderboardsController from "../controllers/leaderboards";
|
||||
import * as LeaderboardController from "../controllers/leaderboard";
|
||||
import { asyncHandler, validateRequest } from "../../middlewares/api-utils";
|
||||
|
||||
const router = Router();
|
||||
|
@ -21,7 +21,7 @@ router.get(
|
|||
limit: joi.number().min(0).max(50),
|
||||
},
|
||||
}),
|
||||
asyncHandler(LeaderboardsController.get)
|
||||
asyncHandler(LeaderboardController.getLeaderboard)
|
||||
);
|
||||
|
||||
router.get(
|
||||
|
@ -36,7 +36,7 @@ router.get(
|
|||
mode2: joi.string().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(LeaderboardsController.getRank)
|
||||
asyncHandler(LeaderboardController.getRankFromLeaderboard)
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import joi from "joi";
|
||||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import PresetController from "../controllers/preset";
|
||||
import * as PresetController from "../controllers/preset";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
import configSchema from "../schemas/config-schema";
|
||||
import { asyncHandler, validateRequest } from "../../middlewares/api-utils";
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import PsaController from "../controllers/psa";
|
||||
import * as PsaController from "../controllers/psa";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
import { asyncHandler } from "../../middlewares/api-utils";
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", RateLimit.psaGet, asyncHandler(PsaController.get));
|
||||
router.get("/", RateLimit.psaGet, asyncHandler(PsaController.getPsas));
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import joi from "joi";
|
||||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import { Router } from "express";
|
||||
import QuotesController from "../controllers/quotes";
|
||||
import * as QuoteController from "../controllers/quote";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
import {
|
||||
asyncHandler,
|
||||
|
@ -23,7 +23,7 @@ quotesRouter.get(
|
|||
RateLimit.newQuotesGet,
|
||||
authenticateRequest(),
|
||||
checkIfUserIsQuoteMod,
|
||||
asyncHandler(QuotesController.getQuotes)
|
||||
asyncHandler(QuoteController.getQuotes)
|
||||
);
|
||||
|
||||
quotesRouter.post(
|
||||
|
@ -46,7 +46,7 @@ quotesRouter.post(
|
|||
},
|
||||
validationErrorMessage: "Please fill all the fields",
|
||||
}),
|
||||
asyncHandler(QuotesController.addQuote)
|
||||
asyncHandler(QuoteController.addQuote)
|
||||
);
|
||||
|
||||
quotesRouter.post(
|
||||
|
@ -62,7 +62,7 @@ quotesRouter.post(
|
|||
validationErrorMessage: "Please fill all the fields",
|
||||
}),
|
||||
checkIfUserIsQuoteMod,
|
||||
asyncHandler(QuotesController.approveQuote)
|
||||
asyncHandler(QuoteController.approveQuote)
|
||||
);
|
||||
|
||||
quotesRouter.post(
|
||||
|
@ -75,7 +75,7 @@ quotesRouter.post(
|
|||
},
|
||||
}),
|
||||
checkIfUserIsQuoteMod,
|
||||
asyncHandler(QuotesController.refuseQuote)
|
||||
asyncHandler(QuoteController.refuseQuote)
|
||||
);
|
||||
|
||||
quotesRouter.get(
|
||||
|
@ -88,7 +88,7 @@ quotesRouter.get(
|
|||
language: joi.string().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(QuotesController.getRating)
|
||||
asyncHandler(QuoteController.getRating)
|
||||
);
|
||||
|
||||
quotesRouter.post(
|
||||
|
@ -102,7 +102,7 @@ quotesRouter.post(
|
|||
language: joi.string().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandler(QuotesController.submitRating)
|
||||
asyncHandler(QuoteController.submitRating)
|
||||
);
|
||||
|
||||
quotesRouter.post(
|
||||
|
@ -137,7 +137,7 @@ quotesRouter.post(
|
|||
return !user.cannotReport;
|
||||
},
|
||||
}),
|
||||
asyncHandler(QuotesController.reportQuote)
|
||||
asyncHandler(QuoteController.reportQuote)
|
||||
);
|
||||
|
||||
export default quotesRouter;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ResultController from "../controllers/result";
|
||||
import * as ResultController from "../controllers/result";
|
||||
import resultSchema from "../schemas/result-schema";
|
||||
import {
|
||||
asyncHandler,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import joi from "joi";
|
||||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
import { Router } from "express";
|
||||
import UserController from "../controllers/user";
|
||||
import * as UserController from "../controllers/user";
|
||||
import { asyncHandler, validateRequest } from "../../middlewares/api-utils";
|
||||
import * as RateLimit from "../../middlewares/rate-limit";
|
||||
import apeRateLimit from "../../middlewares/ape-rate-limit";
|
||||
|
|
Loading…
Reference in a new issue