aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMiodec <[email protected]>2024-07-18 16:36:46 +0200
committerMiodec <[email protected]>2024-07-18 16:36:46 +0200
commit6d1e070b827d7598b132c9cdfd2155ac43930524 (patch)
tree1c022c2539048e04fce3cbdb74fb373f6d3ae184
parenta80fed24bca401cbd3fdc4204fca75477962bf66 (diff)
downloadmonkeytype-6d1e070b827d7598b132c9cdfd2155ac43930524.tar.gz
monkeytype-6d1e070b827d7598b132c9cdfd2155ac43930524.zip
fully fix backend
-rw-r--r--backend/src/api/controllers/dev.ts12
-rw-r--r--backend/src/api/controllers/result.ts16
-rw-r--r--backend/src/api/controllers/user.ts28
-rw-r--r--backend/src/constants/base-configuration.ts777
-rw-r--r--backend/src/dal/blocklist.ts6
-rw-r--r--backend/src/dal/config.ts15
-rw-r--r--backend/src/dal/leaderboards.ts15
-rw-r--r--backend/src/dal/preset.ts10
-rw-r--r--backend/src/dal/psa.ts7
-rw-r--r--backend/src/dal/public.ts7
-rw-r--r--backend/src/dal/result.ts7
-rw-r--r--backend/src/dal/user.ts61
-rw-r--r--backend/src/init/configuration.ts17
-rw-r--r--backend/src/jobs/update-leaderboards.ts7
-rw-r--r--backend/src/middlewares/auth.ts5
-rw-r--r--backend/src/middlewares/configuration.ts3
-rw-r--r--backend/src/queues/george-queue.ts3
-rw-r--r--backend/src/queues/later-queue.ts5
-rw-r--r--backend/src/services/weekly-xp-leaderboard.ts9
-rw-r--r--backend/src/utils/daily-leaderboards.ts19
-rw-r--r--backend/src/utils/monkey-mail.ts7
-rw-r--r--backend/src/utils/pb.ts55
-rw-r--r--backend/src/utils/prometheus.ts6
-rw-r--r--backend/src/utils/result.ts8
-rw-r--r--backend/src/utils/validation.ts3
-rw-r--r--backend/src/workers/later-worker.ts5
26 files changed, 567 insertions, 546 deletions
diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts
index 1b9b263bb..58e7a88e8 100644
--- a/backend/src/api/controllers/dev.ts
+++ b/backend/src/api/controllers/dev.ts
@@ -10,6 +10,8 @@ import { ObjectId } from "mongodb";
import * as LeaderboardDal from "../../dal/leaderboards.js";
import MonkeyError from "../../utils/error.js";
import isNumber from "lodash/isNumber.js";
+import { Mode } from "@monkeytype/shared-types/config";
+import { PersonalBest, PersonalBests } from "@monkeytype/shared-types/user";
type GenerateDataOptions = {
firstTestTimestamp: Date;
@@ -113,7 +115,7 @@ function createResult(
user: MonkeyTypes.DBUser,
timestamp: Date //evil, we modify this value
): MonkeyTypes.DBResult {
- const mode: SharedTypes.Config.Mode = randomValue(["time", "words"]);
+ const mode: Mode = randomValue(["time", "words"]);
const mode2: number =
mode === "time"
? randomValue([15, 30, 60, 120])
@@ -129,7 +131,7 @@ function createResult(
charStats: [131, 0, 0, 0],
acc: random(80, 100),
language: "english",
- mode: mode as SharedTypes.Config.Mode,
+ mode: mode as Mode,
mode2: mode2 as unknown as never,
timestamp: timestamp.valueOf(),
testDuration: testDuration,
@@ -191,7 +193,7 @@ async function updateUser(uid: string): Promise<void> {
},
};
- const personalBests: SharedTypes.PersonalBests = {
+ const personalBests: PersonalBests = {
time: {},
custom: {},
words: {},
@@ -228,7 +230,7 @@ async function updateUser(uid: string): Promise<void> {
wpm: best.wpm,
numbers: best.numbers,
timestamp: best.timestamp,
- } as SharedTypes.PersonalBest;
+ } as PersonalBest;
personalBests[mode.mode][mode.mode2].push(entry);
@@ -251,7 +253,7 @@ async function updateUser(uid: string): Promise<void> {
timeTyping: timeTyping,
completedTests: completedTests,
startedTests: Math.round(completedTests * 1.25),
- personalBests: personalBests as SharedTypes.PersonalBests,
+ personalBests: personalBests as PersonalBests,
lbPersonalBests: lbPersonalBests,
},
}
diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts
index be6977156..c1be7290a 100644
--- a/backend/src/api/controllers/result.ts
+++ b/backend/src/api/controllers/result.ts
@@ -39,6 +39,11 @@ import * as WeeklyXpLeaderboard from "../../services/weekly-xp-leaderboard.js";
import { UAParser } from "ua-parser-js";
import { canFunboxGetPb } from "../../utils/pb.js";
import { buildDbResult } from "../../utils/result.js";
+import {
+ CompletedEvent,
+ Configuration,
+ PostResultResponse,
+} from "@monkeytype/shared-types";
try {
if (!anticheatImplemented()) throw new Error("undefined");
@@ -181,10 +186,7 @@ export async function addResult(
);
}
- const completedEvent = Object.assign(
- {},
- req.body.result
- ) as SharedTypes.CompletedEvent;
+ const completedEvent = Object.assign({}, req.body.result) as CompletedEvent;
if (!user.lbOptOut && completedEvent.acc < 75) {
throw new MonkeyError(
400,
@@ -603,7 +605,7 @@ export async function addResult(
);
}
- const data: Omit<SharedTypes.PostResultResponse, "insertedId"> & {
+ const data: Omit<PostResultResponse, "insertedId"> & {
insertedId: ObjectId;
} = {
isPb,
@@ -635,8 +637,8 @@ type XpResult = {
};
async function calculateXp(
- result: SharedTypes.CompletedEvent,
- xpConfiguration: SharedTypes.Configuration["users"]["xp"],
+ result: CompletedEvent,
+ xpConfiguration: Configuration["users"]["xp"],
uid: string,
currentTotalXp: number,
streak: number
diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts
index 485033fe3..c9af70a58 100644
--- a/backend/src/api/controllers/user.ts
+++ b/backend/src/api/controllers/user.ts
@@ -29,6 +29,15 @@ import * as AuthUtil from "../../utils/auth.js";
import * as Dates from "date-fns";
import { UTCDateMini } from "@date-fns/utc";
import * as BlocklistDal from "../../dal/blocklist.js";
+import { Mode, Mode2 } from "@monkeytype/shared-types/config";
+import {
+ AllTimeLbs,
+ CountByYearAndDay,
+ RankAndCount,
+ TestActivity,
+ UserProfile,
+ UserProfileDetails,
+} from "@monkeytype/shared-types";
async function verifyCaptcha(captcha: string): Promise<void> {
if (!(await verify(captcha))) {
@@ -661,8 +670,7 @@ export async function updateLbMemory(
): Promise<MonkeyResponse> {
const { uid } = req.ctx.decodedToken;
const { mode, language, rank } = req.body;
- const mode2 = req.body
- .mode2 as SharedTypes.Config.Mode2<SharedTypes.Config.Mode>;
+ const mode2 = req.body.mode2 as Mode2<Mode>;
await UserDAL.updateLbMemory(uid, mode, mode2, language, rank);
return new MonkeyResponse("Leaderboard memory updated");
@@ -837,7 +845,7 @@ export async function getProfile(
details: profileDetails,
allTimeLbs,
uid: user.uid,
- } as SharedTypes.UserProfile;
+ } as UserProfile;
return new MonkeyResponse("Profile retrieved", profileData);
}
@@ -865,13 +873,13 @@ export async function updateProfile(
}
});
- const profileDetailsUpdates: Partial<SharedTypes.UserProfileDetails> = {
+ const profileDetailsUpdates: Partial<UserProfileDetails> = {
bio: sanitizeString(bio),
keyboard: sanitizeString(keyboard),
socialProfiles: _.mapValues(
socialProfiles,
sanitizeString
- ) as SharedTypes.UserProfileDetails["socialProfiles"],
+ ) as UserProfileDetails["socialProfiles"],
};
await UserDAL.updateProfile(uid, profileDetailsUpdates, user.inventory);
@@ -986,7 +994,7 @@ export async function revokeAllTokens(
return new MonkeyResponse("All tokens revoked");
}
-async function getAllTimeLbs(uid: string): Promise<SharedTypes.AllTimeLbs> {
+async function getAllTimeLbs(uid: string): Promise<AllTimeLbs> {
const allTime15English = await LeaderboardsDAL.getRank(
"time",
"15",
@@ -1007,7 +1015,7 @@ async function getAllTimeLbs(uid: string): Promise<SharedTypes.AllTimeLbs> {
: ({
rank: allTime15English.rank,
count: allTime15English.count,
- } as SharedTypes.RankAndCount);
+ } as RankAndCount);
const english60 =
allTime60English === false
@@ -1015,7 +1023,7 @@ async function getAllTimeLbs(uid: string): Promise<SharedTypes.AllTimeLbs> {
: ({
rank: allTime60English.rank,
count: allTime60English.count,
- } as SharedTypes.RankAndCount);
+ } as RankAndCount);
return {
time: {
@@ -1030,8 +1038,8 @@ async function getAllTimeLbs(uid: string): Promise<SharedTypes.AllTimeLbs> {
}
export function generateCurrentTestActivity(
- testActivity: SharedTypes.CountByYearAndDay | undefined
-): SharedTypes.TestActivity | undefined {
+ testActivity: CountByYearAndDay | undefined
+): TestActivity | undefined {
const thisYear = Dates.startOfYear(new UTCDateMini());
const lastYear = Dates.startOfYear(Dates.subYears(thisYear, 1));
diff --git a/backend/src/constants/base-configuration.ts b/backend/src/constants/base-configuration.ts
index 5ae74dfb9..8f137ee91 100644
--- a/backend/src/constants/base-configuration.ts
+++ b/backend/src/constants/base-configuration.ts
@@ -1,9 +1,11 @@
+import { Configuration } from "@monkeytype/shared-types";
+
/**
* This is the base schema for the configuration of the API backend.
* To add a new configuration. Simply add it to this object.
* When changing this template, please follow the principle of "Secure by default" (https://en.wikipedia.org/wiki/Secure_by_default).
*/
-export const BASE_CONFIGURATION: SharedTypes.Configuration = {
+export const BASE_CONFIGURATION: Configuration = {
maintenance: false,
dev: {
responseSlowdownMs: 0,
@@ -145,449 +147,447 @@ type Schema<T> = {
: never;
};
-export const CONFIGURATION_FORM_SCHEMA: ObjectSchema<SharedTypes.Configuration> =
- {
- type: "object",
- label: "Server Configuration",
- fields: {
- maintenance: {
- type: "boolean",
- label: "In Maintenance",
- },
- dev: {
- type: "object",
- label: "Development",
- fields: {
- responseSlowdownMs: {
- type: "number",
- label: "Response Slowdown (miliseconds)",
- min: 0,
- },
+export const CONFIGURATION_FORM_SCHEMA: ObjectSchema<Configuration> = {
+ type: "object",
+ label: "Server Configuration",
+ fields: {
+ maintenance: {
+ type: "boolean",
+ label: "In Maintenance",
+ },
+ dev: {
+ type: "object",
+ label: "Development",
+ fields: {
+ responseSlowdownMs: {
+ type: "number",
+ label: "Response Slowdown (miliseconds)",
+ min: 0,
},
},
- results: {
- type: "object",
- label: "Results",
- fields: {
- savingEnabled: {
- type: "boolean",
- label: "Saving Results",
- },
- objectHashCheckEnabled: {
- type: "boolean",
- label: "Object Hash Check",
- },
- filterPresets: {
- type: "object",
- label: "Filter Presets",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- maxPresetsPerUser: {
- type: "number",
- label: "Max Presets Per User",
- min: 0,
- },
+ },
+ results: {
+ type: "object",
+ label: "Results",
+ fields: {
+ savingEnabled: {
+ type: "boolean",
+ label: "Saving Results",
+ },
+ objectHashCheckEnabled: {
+ type: "boolean",
+ label: "Object Hash Check",
+ },
+ filterPresets: {
+ type: "object",
+ label: "Filter Presets",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
},
- },
- limits: {
- type: "object",
- label: "maximum results",
- fields: {
- regularUser: {
- type: "number",
- label: "for regular users",
- min: 0,
- },
- premiumUser: {
- type: "number",
- label: "for premium users",
- min: 0,
- },
+ maxPresetsPerUser: {
+ type: "number",
+ label: "Max Presets Per User",
+ min: 0,
},
},
- maxBatchSize: {
- type: "number",
- label: "results endpoint max batch size",
- min: 1,
+ },
+ limits: {
+ type: "object",
+ label: "maximum results",
+ fields: {
+ regularUser: {
+ type: "number",
+ label: "for regular users",
+ min: 0,
+ },
+ premiumUser: {
+ type: "number",
+ label: "for premium users",
+ min: 0,
+ },
},
},
+ maxBatchSize: {
+ type: "number",
+ label: "results endpoint max batch size",
+ min: 1,
+ },
},
- quotes: {
- type: "object",
- label: "Quotes",
- fields: {
- reporting: {
- type: "object",
- label: "Reporting",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- maxReports: {
- type: "number",
- label: "Max Reports",
- },
- contentReportLimit: {
- type: "number",
- label: "Content Report Limit",
- },
+ },
+ quotes: {
+ type: "object",
+ label: "Quotes",
+ fields: {
+ reporting: {
+ type: "object",
+ label: "Reporting",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ maxReports: {
+ type: "number",
+ label: "Max Reports",
+ },
+ contentReportLimit: {
+ type: "number",
+ label: "Content Report Limit",
},
},
- submissionsEnabled: {
- type: "boolean",
- label: "Submissions Enabled",
- },
- maxFavorites: {
- type: "number",
- label: "Max Favorites",
- },
+ },
+ submissionsEnabled: {
+ type: "boolean",
+ label: "Submissions Enabled",
+ },
+ maxFavorites: {
+ type: "number",
+ label: "Max Favorites",
},
},
- admin: {
- type: "object",
- label: "Admin",
- fields: {
- endpointsEnabled: {
- type: "boolean",
- label: "Endpoints Enabled",
- },
+ },
+ admin: {
+ type: "object",
+ label: "Admin",
+ fields: {
+ endpointsEnabled: {
+ type: "boolean",
+ label: "Endpoints Enabled",
},
},
- apeKeys: {
- type: "object",
- label: "Ape Keys",
- fields: {
- endpointsEnabled: {
- type: "boolean",
- label: "Endpoints Enabled",
- },
- acceptKeys: {
- type: "boolean",
- label: "Accept Keys",
- },
- maxKeysPerUser: {
- type: "number",
- label: "Max Keys Per User",
- min: 0,
- },
- apeKeyBytes: {
- type: "number",
- label: "Ape Key Bytes",
- min: 24,
+ },
+ apeKeys: {
+ type: "object",
+ label: "Ape Keys",
+ fields: {
+ endpointsEnabled: {
+ type: "boolean",
+ label: "Endpoints Enabled",
+ },
+ acceptKeys: {
+ type: "boolean",
+ label: "Accept Keys",
+ },
+ maxKeysPerUser: {
+ type: "number",
+ label: "Max Keys Per User",
+ min: 0,
+ },
+ apeKeyBytes: {
+ type: "number",
+ label: "Ape Key Bytes",
+ min: 24,
+ },
+ apeKeySaltRounds: {
+ type: "number",
+ label: "Ape Key Salt Rounds",
+ min: 5,
+ },
+ },
+ },
+ users: {
+ type: "object",
+ label: "Users",
+ fields: {
+ premium: {
+ type: "object",
+ label: "Premium",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
},
- apeKeySaltRounds: {
- type: "number",
- label: "Ape Key Salt Rounds",
- min: 5,
+ },
+ signUp: {
+ type: "boolean",
+ label: "Sign Up Enabled",
+ },
+ lastHashesCheck: {
+ type: "object",
+ label: "Last Hashes Check",
+ fields: {
+ enabled: { type: "boolean", label: "Enabled" },
+ maxHashes: { type: "number", label: "Hashes to store" },
},
},
- },
- users: {
- type: "object",
- label: "Users",
- fields: {
- premium: {
- type: "object",
- label: "Premium",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
+ xp: {
+ type: "object",
+ label: "XP",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ gainMultiplier: {
+ type: "number",
+ label: "Gain Multiplier",
+ },
+ funboxBonus: {
+ type: "number",
+ label: "Funbox Bonus",
+ },
+ maxDailyBonus: {
+ type: "number",
+ label: "Max Daily Bonus",
+ },
+ minDailyBonus: {
+ type: "number",
+ label: "Min Daily Bonus",
+ },
+ streak: {
+ type: "object",
+ label: "Streak",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ maxStreakDays: {
+ type: "number",
+ label: "Max Streak Days",
+ },
+ maxStreakMultiplier: {
+ type: "number",
+ label: "Max Streak Multiplier",
+ },
},
},
},
- signUp: {
- type: "boolean",
- label: "Sign Up Enabled",
+ },
+ discordIntegration: {
+ type: "object",
+ label: "Discord Integration",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
},
- lastHashesCheck: {
- type: "object",
- label: "Last Hashes Check",
- fields: {
- enabled: { type: "boolean", label: "Enabled" },
- maxHashes: { type: "number", label: "Hashes to store" },
+ },
+ autoBan: {
+ type: "object",
+ label: "Auto Ban",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ maxCount: {
+ type: "number",
+ label: "Max Count",
+ min: 0,
+ },
+ maxHours: {
+ type: "number",
+ label: "Max Hours",
+ min: 0,
},
},
- xp: {
- type: "object",
- label: "XP",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- gainMultiplier: {
- type: "number",
- label: "Gain Multiplier",
- },
- funboxBonus: {
- type: "number",
- label: "Funbox Bonus",
- },
- maxDailyBonus: {
- type: "number",
- label: "Max Daily Bonus",
- },
- minDailyBonus: {
- type: "number",
- label: "Min Daily Bonus",
- },
- streak: {
- type: "object",
- label: "Streak",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- maxStreakDays: {
- type: "number",
- label: "Max Streak Days",
- },
- maxStreakMultiplier: {
- type: "number",
- label: "Max Streak Multiplier",
- },
- },
- },
+ },
+ inbox: {
+ type: "object",
+ label: "Inbox",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ maxMail: {
+ type: "number",
+ label: "Max Messages",
+ min: 0,
},
},
- discordIntegration: {
- type: "object",
- label: "Discord Integration",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
+ },
+ profiles: {
+ type: "object",
+ label: "User Profiles",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
},
},
- autoBan: {
- type: "object",
- label: "Auto Ban",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- maxCount: {
- type: "number",
- label: "Max Count",
- min: 0,
- },
- maxHours: {
+ },
+ },
+ },
+ rateLimiting: {
+ type: "object",
+ label: "Rate Limiting",
+ fields: {
+ badAuthentication: {
+ type: "object",
+ label: "Bad Authentication Rate Limiter",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ penalty: {
+ type: "number",
+ label: "Penalty",
+ min: 0,
+ },
+ flaggedStatusCodes: {
+ type: "array",
+ label: "Flagged Status Codes",
+ items: {
+ label: "Status Code",
type: "number",
- label: "Max Hours",
min: 0,
},
},
},
- inbox: {
+ },
+ },
+ },
+ dailyLeaderboards: {
+ type: "object",
+ label: "Daily Leaderboards",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
+ },
+ maxResults: {
+ type: "number",
+ label: "Max Results",
+ min: 0,
+ },
+ leaderboardExpirationTimeInDays: {
+ type: "number",
+ label: "Leaderboard Expiration Time In Days",
+ min: 0,
+ },
+ validModeRules: {
+ type: "array",
+ label: "Valid Mode Rules",
+ items: {
type: "object",
- label: "Inbox",
+ label: "Rule",
fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
+ language: {
+ type: "string",
+ label: "Language",
},
- maxMail: {
- type: "number",
- label: "Max Messages",
- min: 0,
+ mode: {
+ type: "string",
+ label: "Mode",
+ },
+ mode2: {
+ type: "string",
+ label: "Secondary Mode",
},
},
},
- profiles: {
+ },
+ scheduleRewardsModeRules: {
+ type: "array",
+ label: "Schedule Rewards Mode Rules",
+ items: {
type: "object",
- label: "User Profiles",
+ label: "Rule",
fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
+ language: {
+ type: "string",
+ label: "Language",
+ },
+ mode: {
+ type: "string",
+ label: "Mode",
+ },
+ mode2: {
+ type: "string",
+ label: "Secondary Mode",
},
},
},
},
- },
- rateLimiting: {
- type: "object",
- label: "Rate Limiting",
- fields: {
- badAuthentication: {
+ topResultsToAnnounce: {
+ type: "number",
+ label: "Top Results To Announce",
+ min: 1,
+ hint: "This should atleast be 1. Setting to zero is very bad.",
+ },
+ xpRewardBrackets: {
+ type: "array",
+ label: "XP Reward Brackets",
+ items: {
type: "object",
- label: "Bad Authentication Rate Limiter",
+ label: "Bracket",
fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
+ minRank: {
+ type: "number",
+ label: "Min Rank",
+ min: 1,
+ },
+ maxRank: {
+ type: "number",
+ label: "Max Rank",
+ min: 1,
},
- penalty: {
+ minReward: {
type: "number",
- label: "Penalty",
+ label: "Min Reward",
min: 0,
},
- flaggedStatusCodes: {
- type: "array",
- label: "Flagged Status Codes",
- items: {
- label: "Status Code",
- type: "number",
- min: 0,
- },
+ maxReward: {
+ type: "number",
+ label: "Max Reward",
+ min: 0,
},
},
},
},
},
- dailyLeaderboards: {
- type: "object",
- label: "Daily Leaderboards",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- maxResults: {
- type: "number",
- label: "Max Results",
- min: 0,
- },
- leaderboardExpirationTimeInDays: {
- type: "number",
- label: "Leaderboard Expiration Time In Days",
- min: 0,
- },
- validModeRules: {
- type: "array",
- label: "Valid Mode Rules",
- items: {
- type: "object",
- label: "Rule",
- fields: {
- language: {
- type: "string",
- label: "Language",
- },
- mode: {
- type: "string",
- label: "Mode",
- },
- mode2: {
- type: "string",
- label: "Secondary Mode",
- },
- },
+ },
+ leaderboards: {
+ type: "object",
+ label: "Leaderboards",
+ fields: {
+ weeklyXp: {
+ type: "object",
+ label: "Weekly XP",
+ fields: {
+ enabled: {
+ type: "boolean",
+ label: "Enabled",
},
- },
- scheduleRewardsModeRules: {
- type: "array",
- label: "Schedule Rewards Mode Rules",
- items: {
- type: "object",
- label: "Rule",
- fields: {
- language: {
- type: "string",
- label: "Language",
- },
- mode: {
- type: "string",
- label: "Mode",
- },
- mode2: {
- type: "string",
- label: "Secondary Mode",
- },
- },
+ expirationTimeInDays: {
+ type: "number",
+ label: "Expiration time in days",
+ min: 0,
+ hint: "This should atleast be 15, to allow for past week queries.",
},
- },
- topResultsToAnnounce: {
- type: "number",
- label: "Top Results To Announce",
- min: 1,
- hint: "This should atleast be 1. Setting to zero is very bad.",
- },
- xpRewardBrackets: {
- type: "array",
- label: "XP Reward Brackets",
- items: {
- type: "object",
- label: "Bracket",
- fields: {
- minRank: {
- type: "number",
- label: "Min Rank",
- min: 1,
- },
- maxRank: {
- type: "number",
- label: "Max Rank",
- min: 1,
- },
- minReward: {
- type: "number",
- label: "Min Reward",
- min: 0,
- },
- maxReward: {
- type: "number",
- label: "Max Reward",
- min: 0,
- },
- },
- },
- },
- },
- },
- leaderboards: {
- type: "object",
- label: "Leaderboards",
- fields: {
- weeklyXp: {
- type: "object",
- label: "Weekly XP",
- fields: {
- enabled: {
- type: "boolean",
- label: "Enabled",
- },
- expirationTimeInDays: {
- type: "number",
- label: "Expiration time in days",
- min: 0,
- hint: "This should atleast be 15, to allow for past week queries.",
- },
- xpRewardBrackets: {
- type: "array",
- label: "XP Reward Brackets",
- items: {
- type: "object",
- label: "Bracket",
- fields: {
- minRank: {
- type: "number",
- label: "Min Rank",
- min: 1,
- },
- maxRank: {
- type: "number",
- label: "Max Rank",
- min: 1,
- },
- minReward: {
- type: "number",
- label: "Min Reward",
- min: 0,
- },
- maxReward: {
- type: "number",
- label: "Max Reward",
- min: 0,
- },
+ xpRewardBrackets: {
+ type: "array",
+ label: "XP Reward Brackets",
+ items: {
+ type: "object",
+ label: "Bracket",
+ fields: {
+ minRank: {
+ type: "number",
+ label: "Min Rank",
+ min: 1,
+ },
+ maxRank: {
+ type: "number",
+ label: "Max Rank",
+ min: 1,
+ },
+ minReward: {
+ type: "number",
+ label: "Min Reward",
+ min: 0,
+ },
+ maxReward: {
+ type: "number",
+ label: "Max Reward",
+ min: 0,
},
},
},
@@ -596,4 +596,5 @@ export const CONFIGURATION_FORM_SCHEMA: ObjectSchema<SharedTypes.Configuration>
},
},
},
- };
+ },
+};
diff --git a/backend/src/dal/blocklist.ts b/backend/src/dal/blocklist.ts
index 3ebc9355e..234380b62 100644
--- a/backend/src/dal/blocklist.ts
+++ b/backend/src/dal/blocklist.ts
@@ -1,11 +1,9 @@
import { Collection } from "mongodb";
import * as db from "../init/db.js";
import { createHash } from "crypto";
+import { User } from "@monkeytype/shared-types";
-type BlocklistEntryProperties = Pick<
- SharedTypes.User,
- "name" | "email" | "discordId"
->;
+type BlocklistEntryProperties = Pick<User, "name" | "email" | "discordId">;
// Export for use in tests
export const getCollection = (): Collection<MonkeyTypes.DBBlocklistEntry> =>
db.collection("blocklist");
diff --git a/backend/src/dal/config.ts b/backend/src/dal/config.ts
index 488dea3a7..a0116586e 100644
--- a/backend/src/dal/config.ts
+++ b/backend/src/dal/config.ts
@@ -1,6 +1,7 @@
import { type UpdateResult } from "mongodb";
import * as db from "../init/db.js";
import _ from "lodash";
+import { Config } from "@monkeytype/shared-types/config";
const configLegacyProperties = [
"swapEscAndTab",
@@ -24,7 +25,7 @@ const configLegacyProperties = [
export async function saveConfig(
uid: string,
- config: SharedTypes.Config
+ config: Config
): Promise<UpdateResult> {
const configChanges = _.mapKeys(config, (_value, key) => `config.${key}`);
@@ -33,7 +34,7 @@ export async function saveConfig(
) as Record<string, "">;
return await db
- .collection<SharedTypes.Config>("configs")
+ .collection<Config>("configs")
.updateOne(
{ uid },
{ $set: configChanges, $unset: unset },
@@ -41,15 +42,11 @@ export async function saveConfig(
);
}
-export async function getConfig(
- uid: string
-): Promise<SharedTypes.Config | null> {
- const config = await db
- .collection<SharedTypes.Config>("configs")
- .findOne({ uid });
+export async function getConfig(uid: string): Promise<Config | null> {
+ const config = await db.collection<Config>("configs").findOne({ uid });
return config;
}
export async function deleteConfig(uid: string): Promise<void> {
- await db.collection<SharedTypes.Config>("configs").deleteOne({ uid });
+ await db.collection<Config>("configs").deleteOne({ uid });
}
diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts
index 22edb4e22..5b8472b21 100644
--- a/backend/src/dal/leaderboards.ts
+++ b/backend/src/dal/leaderboards.ts
@@ -4,6 +4,7 @@ import { performance } from "perf_hooks";
import { setLeaderboard } from "../utils/prometheus.js";
import { isDevEnvironment } from "../utils/misc.js";
import { getCachedConfiguration } from "../init/configuration.js";
+import { LeaderboardEntry } from "@monkeytype/shared-types";
export async function get(
mode: string,
@@ -11,16 +12,14 @@ export async function get(
language: string,
skip: number,
limit = 50
-): Promise<SharedTypes.LeaderboardEntry[] | false> {
+): Promise<LeaderboardEntry[] | false> {
//if (leaderboardUpdating[`${language}_${mode}_${mode2}`]) return false;
if (limit > 50 || limit <= 0) limit = 50;
if (skip < 0) skip = 0;
try {
const preset = await db
- .collection<SharedTypes.LeaderboardEntry>(
- `leaderboards.${language}.${mode}.${mode2}`
- )
+ .collection<LeaderboardEntry>(`leaderboards.${language}.${mode}.${mode2}`)
.find()
.sort({ rank: 1 })
.skip(skip)
@@ -46,7 +45,7 @@ export async function get(
type GetRankResponse = {
count: number;
rank: number | null;
- entry: SharedTypes.LeaderboardEntry | null;
+ entry: LeaderboardEntry | null;
};
export async function getRank(
@@ -57,9 +56,7 @@ export async function getRank(
): Promise<GetRankResponse | false> {
try {
const entry = await db
- .collection<SharedTypes.LeaderboardEntry>(
- `leaderboards.${language}.${mode}.${mode2}`
- )
+ .collection<LeaderboardEntry>(`leaderboards.${language}.${mode}.${mode2}`)
.findOne({ uid });
const count = await db
.collection(`leaderboards.${language}.${mode}.${mode2}`)
@@ -91,7 +88,7 @@ export async function update(
const lbCollectionName = `leaderboards.${language}.${mode}.${mode2}`;
const lb = db
.collection<MonkeyTypes.DBUser>("users")
- .aggregate<SharedTypes.LeaderboardEntry>(
+ .aggregate<LeaderboardEntry>(
[
{
$match: {
diff --git a/backend/src/dal/preset.ts b/backend/src/dal/preset.ts
index 5febf8f24..6a79e7c57 100644
--- a/backend/src/dal/preset.ts
+++ b/backend/src/dal/preset.ts
@@ -1,10 +1,14 @@
import MonkeyError from "../utils/error.js";
import * as db from "../init/db.js";
import { ObjectId, type Filter, Collection, type WithId } from "mongodb";
+import {
+ ConfigPreset,
+ DBConfigPreset as SharedDBConfigPreset,
+} from "@monkeytype/shared-types";
const MAX_PRESETS = 10;
-type DBConfigPreset = MonkeyTypes.WithObjectId<SharedTypes.DBConfigPreset>;
+type DBConfigPreset = MonkeyTypes.WithObjectId<SharedDBConfigPreset>;
function getPresetKeyFilter(
uid: string,
@@ -34,7 +38,7 @@ export async function getPresets(uid: string): Promise<DBConfigPreset[]> {
export async function addPreset(
uid: string,
name: string,
- config: SharedTypes.ConfigPreset
+ config: ConfigPreset
): Promise<PresetCreationResult> {
const presets = await getPresets(uid);
if (presets.length >= MAX_PRESETS) {
@@ -56,7 +60,7 @@ export async function editPreset(
uid: string,
presetId: string,
name: string,
- config: SharedTypes.ConfigPreset | null | undefined
+ config: ConfigPreset | null | undefined
): Promise<void> {
const presetUpdates =
config !== undefined && config !== null && Object.keys(config).length > 0
diff --git a/backend/src/dal/psa.ts b/backend/src/dal/psa.ts
index cd103f38c..6aaf976f5 100644
--- a/backend/src/dal/psa.ts
+++ b/backend/src/dal/psa.ts
@@ -1,7 +1,8 @@
+import { PSA } from "@monkeytype/shared-types";
import * as db from "../init/db.js";
-type PSA = MonkeyTypes.WithObjectId<SharedTypes.PSA>;
+type DBPSA = MonkeyTypes.WithObjectId<PSA>;
-export async function get(): Promise<PSA[]> {
- return await db.collection<PSA>("psa").find().toArray();
+export async function get(): Promise<DBPSA[]> {
+ return await db.collection<DBPSA>("psa").find().toArray();
}
diff --git a/backend/src/dal/public.ts b/backend/src/dal/public.ts
index 5c7fb6983..d46e651f4 100644
--- a/backend/src/dal/public.ts
+++ b/backend/src/dal/public.ts
@@ -1,12 +1,13 @@
import * as db from "../init/db.js";
import { roundTo2 } from "../utils/misc.js";
import MonkeyError from "../utils/error.js";
+import { PublicTypingStats, SpeedHistogram } from "@monkeytype/shared-types";
-type PublicTypingStatsDB = SharedTypes.PublicTypingStats & { _id: "stats" };
+type PublicTypingStatsDB = PublicTypingStats & { _id: "stats" };
type PublicSpeedStatsDB = {
_id: "speedStatsHistogram";
- english_time_15: SharedTypes.SpeedHistogram;
- english_time_60: SharedTypes.SpeedHistogram;
+ english_time_15: SpeedHistogram;
+ english_time_60: SpeedHistogram;
};
export async function updateStats(
diff --git a/backend/src/dal/result.ts b/backend/src/dal/result.ts
index 05a6ca37d..ddfb270e7 100644
--- a/backend/src/dal/result.ts
+++ b/backend/src/dal/result.ts
@@ -7,12 +7,11 @@ import {
} from "mongodb";
import MonkeyError from "../utils/error.js";
import * as db from "../init/db.js";
-
+import { DBResult as SharedDBResult } from "@monkeytype/shared-types";
import { getUser, getTags } from "./user.js";
+import { Mode } from "@monkeytype/shared-types/config";
-type DBResult = MonkeyTypes.WithObjectId<
- SharedTypes.DBResult<SharedTypes.Config.Mode>
->;
+type DBResult = MonkeyTypes.WithObjectId<SharedDBResult<Mode>>;
export const getResultCollection = (): Collection<DBResult> =>
db.collection<DBResult>("results");
diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts
index 86765508f..30c6ae5f8 100644
--- a/backend/src/dal/user.ts
+++ b/backend/src/dal/user.ts
@@ -15,13 +15,25 @@ import { flattenObjectDeep, isToday, isYesterday } from "../utils/misc.js";
import { getCachedConfiguration } from "../init/configuration.js";
import { getDayOfYear } from "date-fns";
import { UTCDate } from "@date-fns/utc";
+import {
+ AllRewards,
+ Badge,
+ Configuration,
+ CustomTheme,
+ DBResult,
+ MonkeyMail,
+ ResultFilters,
+ UserInventory,
+ UserProfileDetails,
+ UserQuoteRatings,
+ UserStreak,
+} from "@monkeytype/shared-types";
+import { Mode, Mode2 } from "@monkeytype/shared-types/config";
+import { PersonalBest } from "@monkeytype/shared-types/user";
const SECONDS_PER_HOUR = 3600;
-type Result = Omit<
- SharedTypes.DBResult<SharedTypes.Config.Mode>,
- "_id" | "name"
->;
+type Result = Omit<DBResult<Mode>, "_id" | "name">;
// Export for use in tests
export const getUsersCollection = (): Collection<MonkeyTypes.DBUser> =>
@@ -183,7 +195,7 @@ export async function optOutOfLeaderboards(uid: string): Promise<void> {
export async function updateQuoteRatings(
uid: string,
- quoteRatings: SharedTypes.UserQuoteRatings
+ quoteRatings: UserQuoteRatings
): Promise<boolean> {
await updateUser(
{ uid },
@@ -274,7 +286,7 @@ export async function isDiscordIdAvailable(
export async function addResultFilterPreset(
uid: string,
- resultFilter: SharedTypes.ResultFilters,
+ resultFilter: ResultFilters,
maxFiltersPerUser: number
): Promise<ObjectId> {
if (maxFiltersPerUser === 0) {
@@ -400,8 +412,8 @@ export async function removeTagPb(uid: string, _id: string): Promise<void> {
export async function updateLbMemory(
uid: string,
- mode: SharedTypes.Config.Mode,
- mode2: SharedTypes.Config.Mode2<SharedTypes.Config.Mode>,
+ mode: Mode,
+ mode2: Mode2<Mode>,
language: string,
rank: number
): Promise<void> {
@@ -650,7 +662,7 @@ export async function incrementTestActivity(
export async function addTheme(
uid: string,
- { name, colors }: Omit<SharedTypes.CustomTheme, "_id">
+ { name, colors }: Omit<CustomTheme, "_id">
): Promise<{ _id: ObjectId; name: string }> {
const _id = new ObjectId();
@@ -694,7 +706,7 @@ export async function removeTheme(uid: string, id: string): Promise<void> {
export async function editTheme(
uid: string,
id: string,
- { name, colors }: Omit<SharedTypes.CustomTheme, "_id">
+ { name, colors }: Omit<CustomTheme, "_id">
): Promise<void> {
const themeId = new ObjectId(id);
@@ -721,7 +733,7 @@ export async function getPersonalBests(
uid: string,
mode: string,
mode2?: string
-): Promise<SharedTypes.PersonalBest> {
+): Promise<PersonalBest> {
const user = await getPartialUser(uid, "get personal bests", [
"personalBests",
]);
@@ -854,8 +866,8 @@ export async function recordAutoBanEvent(
export async function updateProfile(
uid: string,
- profileDetailUpdates: Partial<SharedTypes.UserProfileDetails>,
- inventory?: SharedTypes.UserInventory
+ profileDetailUpdates: Partial<UserProfileDetails>,
+ inventory?: UserInventory
): Promise<void> {
const profileUpdates = _.omitBy(
flattenObjectDeep(profileDetailUpdates, "profileDetails"),
@@ -888,12 +900,12 @@ export async function getInbox(
type AddToInboxBulkEntry = {
uid: string;
- mail: SharedTypes.MonkeyMail[];
+ mail: MonkeyMail[];
};
export async function addToInboxBulk(
entries: AddToInboxBulkEntry[],
- inboxConfig: SharedTypes.Configuration["users"]["inbox"]
+ inboxConfig: Configuration["users"]["inbox"]
): Promise<void> {
const { enabled, maxMail } = inboxConfig;
@@ -920,8 +932,8 @@ export async function addToInboxBulk(
export async function addToInbox(
uid: string,
- mail: SharedTypes.MonkeyMail[],
- inboxConfig: SharedTypes.Configuration["users"]["inbox"]
+ mail: MonkeyMail[],
+ inboxConfig: Configuration["users"]["inbox"]
): Promise<void> {
const { enabled, maxMail } = inboxConfig;
@@ -966,9 +978,9 @@ export async function updateInbox(
lang: "js",
args: ["$inbox", "$xp", "$inventory", deleteSet, readSet],
body: function (
- inbox: SharedTypes.MonkeyMail[],
+ inbox: MonkeyMail[],
xp: number,
- inventory: SharedTypes.UserInventory,
+ inventory: UserInventory,
deletedIds: string[],
readIds: string[]
): Pick<MonkeyTypes.DBUser, "xp" | "inventory" | "inbox"> {
@@ -981,10 +993,7 @@ export async function updateInbox(
);
//flatMap rewards
- const rewards: SharedTypes.AllRewards[] = [
- ...toBeRead,
- ...toBeDeleted,
- ]
+ const rewards: AllRewards[] = [...toBeRead, ...toBeDeleted]
.filter((it) => it.read === false)
.reduce((arr, current) => {
return [...arr, ...current.rewards];
@@ -997,7 +1006,7 @@ export async function updateInbox(
const badgesToClaim = rewards
.filter((it) => it.type === "badge")
- .map((it) => it.item as SharedTypes.Badge);
+ .map((it) => it.item as Badge);
if (inventory === null)
inventory = {
@@ -1006,7 +1015,7 @@ export async function updateInbox(
if (inventory.badges === null) inventory.badges = [];
const uniqueBadgeIds = new Set();
- const newBadges: SharedTypes.Badge[] = [];
+ const newBadges: Badge[] = [];
for (const badge of [...inventory.badges, ...badgesToClaim]) {
if (uniqueBadgeIds.has(badge.id)) continue;
@@ -1055,7 +1064,7 @@ export async function updateStreak(
timestamp: number
): Promise<number> {
const user = await getPartialUser(uid, "calculate streak", ["streak"]);
- const streak: SharedTypes.UserStreak = {
+ const streak: UserStreak = {
lastResultTimestamp: user.streak?.lastResultTimestamp ?? 0,
length: user.streak?.length ?? 0,
maxLength: user.streak?.maxLength ?? 0,
diff --git a/backend/src/init/configuration.ts b/backend/src/init/configuration.ts
index e6a04fe52..dc853a17c 100644
--- a/backend/src/init/configuration.ts
+++ b/backend/src/init/configuration.ts
@@ -4,12 +4,13 @@ import { ObjectId } from "mongodb";
import Logger from "../utils/logger.js";
import { identity } from "../utils/misc.js";
import { BASE_CONFIGURATION } from "../constants/base-configuration.js";
+import { Configuration } from "@monkeytype/shared-types";
const CONFIG_UPDATE_INTERVAL = 10 * 60 * 1000; // 10 Minutes
function mergeConfigurations(
- baseConfiguration: SharedTypes.Configuration,
- liveConfiguration: Partial<SharedTypes.Configuration>
+ baseConfiguration: Configuration,
+ liveConfiguration: Partial<Configuration>
): void {
if (
!_.isPlainObject(baseConfiguration) ||
@@ -45,7 +46,7 @@ let serverConfigurationUpdated = false;
export async function getCachedConfiguration(
attemptCacheUpdate = false
-): Promise<SharedTypes.Configuration> {
+): Promise<Configuration> {
if (
attemptCacheUpdate &&
lastFetchTime < Date.now() - CONFIG_UPDATE_INTERVAL
@@ -57,7 +58,7 @@ export async function getCachedConfiguration(
return configuration;
}
-export async function getLiveConfiguration(): Promise<SharedTypes.Configuration> {
+export async function getLiveConfiguration(): Promise<Configuration> {
lastFetchTime = Date.now();
const configurationCollection = db.collection("configuration");
@@ -71,7 +72,7 @@ export async function getLiveConfiguration(): Promise<SharedTypes.Configuration>
const liveConfigurationWithoutId = _.omit(
liveConfiguration,
"_id"
- ) as SharedTypes.Configuration;
+ ) as Configuration;
mergeConfigurations(baseConfiguration, liveConfigurationWithoutId);
await pushConfiguration(baseConfiguration);
@@ -92,9 +93,7 @@ export async function getLiveConfiguration(): Promise<SharedTypes.Configuration>
return configuration;
}
-async function pushConfiguration(
- configuration: SharedTypes.Configuration
-): Promise<void> {
+async function pushConfiguration(configuration: Configuration): Promise<void> {
if (serverConfigurationUpdated) {
return;
}
@@ -111,7 +110,7 @@ async function pushConfiguration(
}
export async function patchConfiguration(
- configurationUpdates: Partial<SharedTypes.Configuration>
+ configurationUpdates: Partial<Configuration>
): Promise<boolean> {
try {
const currentConfiguration = _.cloneDeep(configuration);
diff --git a/backend/src/jobs/update-leaderboards.ts b/backend/src/jobs/update-leaderboards.ts
index 0d81758d6..52d6ef01f 100644
--- a/backend/src/jobs/update-leaderboards.ts
+++ b/backend/src/jobs/update-leaderboards.ts
@@ -2,21 +2,20 @@ import { CronJob } from "cron";
import GeorgeQueue from "../queues/george-queue.js";
import * as LeaderboardsDAL from "../dal/leaderboards.js";
import { getCachedConfiguration } from "../init/configuration.js";
+import { LeaderboardEntry } from "@monkeytype/shared-types";
const CRON_SCHEDULE = "30 14/15 * * * *";
const RECENT_AGE_MINUTES = 10;
const RECENT_AGE_MILLISECONDS = RECENT_AGE_MINUTES * 60 * 1000;
-async function getTop10(
- leaderboardTime: string
-): Promise<SharedTypes.LeaderboardEntry[]> {
+async function getTop10(leaderboardTime: string): Promise<LeaderboardEntry[]> {
return (await LeaderboardsDAL.get(
"time",
leaderboardTime,
"english",
0,
10
- )) as SharedTypes.LeaderboardEntry[]; //can do that because gettop10 will not be called during an update
+ )) as LeaderboardEntry[]; //can do that because gettop10 will not be called during an update
}
async function updateLeaderboardAndNotifyChanges(
diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts
index 52db0625d..fbead9964 100644
--- a/backend/src/middlewares/auth.ts
+++ b/backend/src/middlewares/auth.ts
@@ -13,6 +13,7 @@ import {
} from "../utils/prometheus.js";
import crypto from "crypto";
import { performance } from "perf_hooks";
+import { Configuration } from "@monkeytype/shared-types";
type RequestAuthenticationOptions = {
isPublic?: boolean;
@@ -105,7 +106,7 @@ function authenticateRequest(authOptions = DEFAULT_OPTIONS): Handler {
async function authenticateWithAuthHeader(
authHeader: string,
- configuration: SharedTypes.Configuration,
+ configuration: Configuration,
options: RequestAuthenticationOptions
): Promise<MonkeyTypes.DecodedToken> {
if (authHeader === undefined || authHeader === "") {
@@ -221,7 +222,7 @@ async function authenticateWithBearerToken(
async function authenticateWithApeKey(
key: string,
- configuration: SharedTypes.Configuration,
+ configuration: Configuration,
options: RequestAuthenticationOptions
): Promise<MonkeyTypes.DecodedToken> {
if (!configuration.apeKeys.acceptKeys) {
diff --git a/backend/src/middlewares/configuration.ts b/backend/src/middlewares/configuration.ts
index 388e60a79..42e180943 100644
--- a/backend/src/middlewares/configuration.ts
+++ b/backend/src/middlewares/configuration.ts
@@ -1,5 +1,6 @@
import type { Response, NextFunction, RequestHandler } from "express";
import MonkeyError from "../utils/error.js";
+import { Configuration } from "@monkeytype/shared-types";
export type ValidationOptions<T> = {
criteria: (data: T) => boolean;
@@ -11,7 +12,7 @@ export type ValidationOptions<T> = {
* the criteria.
*/
export function validate(
- options: ValidationOptions<SharedTypes.Configuration>
+ options: ValidationOptions<Configuration>
): RequestHandler {
const {
criteria,
diff --git a/backend/src/queues/george-queue.ts b/backend/src/queues/george-queue.ts
index 1a0d3e3eb..ee4a7c1d7 100644
--- a/backend/src/queues/george-queue.ts
+++ b/backend/src/queues/george-queue.ts
@@ -1,3 +1,4 @@
+import { LeaderboardEntry } from "@monkeytype/shared-types";
import { type LbEntryWithRank } from "../utils/daily-leaderboards.js";
import { MonkeyQueue } from "./monkey-queue.js";
@@ -61,7 +62,7 @@ class GeorgeQueue extends MonkeyQueue<GeorgeTask> {
}
async announceLeaderboardUpdate(
- newRecords: SharedTypes.LeaderboardEntry[],
+ newRecords: LeaderboardEntry[],
leaderboardId: string
): Promise<void> {
const taskName = "announceLeaderboardUpdate";
diff --git a/backend/src/queues/later-queue.ts b/backend/src/queues/later-queue.ts
index 46c2e064f..da904f610 100644
--- a/backend/src/queues/later-queue.ts
+++ b/backend/src/queues/later-queue.ts
@@ -5,6 +5,7 @@ import {
getCurrentDayTimestamp,
getCurrentWeekTimestamp,
} from "../utils/misc.js";
+import { ValidModeRule } from "@monkeytype/shared-types";
const QUEUE_NAME = "later";
@@ -20,7 +21,7 @@ export type LaterTask<T extends LaterTaskType> = {
export type LaterTaskContexts = {
"daily-leaderboard-results": {
yesterdayTimestamp: number;
- modeRule: SharedTypes.ValidModeRule;
+ modeRule: ValidModeRule;
};
"weekly-xp-leaderboard-results": {
lastWeekTimestamp: number;
@@ -85,7 +86,7 @@ class LaterQueue extends MonkeyQueue<LaterTask<LaterTaskType>> {
async scheduleForTomorrow(
taskName: LaterTaskType,
taskId: string,
- modeRule: SharedTypes.ValidModeRule
+ modeRule: ValidModeRule
): Promise<void> {
const currentDayTimestamp = getCurrentDayTimestamp();
const jobId = `${taskName}:${currentDayTimestamp}:${taskId}`;
diff --git a/backend/src/services/weekly-xp-leaderboard.ts b/backend/src/services/weekly-xp-leaderboard.ts
index 288bf82df..e777f170b 100644
--- a/backend/src/services/weekly-xp-leaderboard.ts
+++ b/backend/src/services/weekly-xp-leaderboard.ts
@@ -1,3 +1,4 @@
+import { Configuration } from "@monkeytype/shared-types";
import * as RedisClient from "../init/redis.js";
import LaterQueue from "../queues/later-queue.js";
import { getCurrentWeekTimestamp } from "../utils/misc.js";
@@ -59,7 +60,7 @@ export class WeeklyXpLeaderboard {
}
public async addResult(
- weeklyXpLeaderboardConfig: SharedTypes.Configuration["leaderboards"]["weeklyXp"],
+ weeklyXpLeaderboardConfig: Configuration["leaderboards"]["weeklyXp"],
opts: AddResultOpts
): Promise<number> {
const { entry, xpGained, timeTypedSeconds } = opts;
@@ -121,7 +122,7 @@ export class WeeklyXpLeaderboard {
public async getResults(
minRank: number,
maxRank: number,
- weeklyXpLeaderboardConfig: SharedTypes.Configuration["leaderboards"]["weeklyXp"]
+ weeklyXpLeaderboardConfig: Configuration["leaderboards"]["weeklyXp"]
): Promise<WeeklyXpLeaderboardEntry[]> {
const connection = RedisClient.getConnection();
if (!connection || !weeklyXpLeaderboardConfig.enabled) {
@@ -166,7 +167,7 @@ export class WeeklyXpLeaderboard {
public async getRank(
uid: string,
- weeklyXpLeaderboardConfig: SharedTypes.Configuration["leaderboards"]["weeklyXp"]
+ weeklyXpLeaderboardConfig: Configuration["leaderboards"]["weeklyXp"]
): Promise<WeeklyXpLeaderboardEntry | null> {
const connection = RedisClient.getConnection();
if (!connection || !weeklyXpLeaderboardConfig.enabled) {
@@ -202,7 +203,7 @@ export class WeeklyXpLeaderboard {
}
export function get(
- weeklyXpLeaderboardConfig: SharedTypes.Configuration["leaderboards"]["weeklyXp"],
+ weeklyXpLeaderboardConfig: Configuration["leaderboards"]["weeklyXp"],
customTimestamp?: number
): WeeklyXpLeaderboard | null {
const { enabled } = weeklyXpLeaderboardConfig;
diff --git a/backend/src/utils/daily-leaderboards.ts b/backend/src/utils/daily-leaderboards.ts
index e259a5e89..04fb298cb 100644
--- a/backend/src/utils/daily-leaderboards.ts
+++ b/backend/src/utils/daily-leaderboards.ts
@@ -2,6 +2,7 @@ import _ from "lodash";
import * as RedisClient from "../init/redis.js";
import LaterQueue from "../queues/later-queue.js";
import { getCurrentDayTimestamp, matchesAPattern, kogascore } from "./misc.js";
+import { Configuration, ValidModeRule } from "@monkeytype/shared-types";
type DailyLeaderboardEntry = {
uid: string;
@@ -37,9 +38,9 @@ export class DailyLeaderboard {
private leaderboardScoresKeyName: string;
private leaderboardModeKey: string;
private customTime: number;
- private modeRule: SharedTypes.ValidModeRule;
+ private modeRule: ValidModeRule;
- constructor(modeRule: SharedTypes.ValidModeRule, customTime = -1) {
+ constructor(modeRule: ValidModeRule, customTime = -1) {
const { language, mode, mode2 } = modeRule;
this.leaderboardModeKey = `${language}:${mode}:${mode2}`;
@@ -68,7 +69,7 @@ export class DailyLeaderboard {
public async addResult(
entry: DailyLeaderboardEntry,
- dailyLeaderboardsConfig: SharedTypes.Configuration["dailyLeaderboards"]
+ dailyLeaderboardsConfig: Configuration["dailyLeaderboards"]
): Promise<number> {
const connection = RedisClient.getConnection();
if (!connection || !dailyLeaderboardsConfig.enabled) {
@@ -124,7 +125,7 @@ export class DailyLeaderboard {
public async getResults(
minRank: number,
maxRank: number,
- dailyLeaderboardsConfig: SharedTypes.Configuration["dailyLeaderboards"],
+ dailyLeaderboardsConfig: Configuration["dailyLeaderboards"],
premiumFeaturesEnabled: boolean
): Promise<LbEntryWithRank[]> {
const connection = RedisClient.getConnection();
@@ -167,7 +168,7 @@ export class DailyLeaderboard {
public async getRank(
uid: string,
- dailyLeaderboardsConfig: SharedTypes.Configuration["dailyLeaderboards"]
+ dailyLeaderboardsConfig: Configuration["dailyLeaderboards"]
): Promise<GetRankResponse | null> {
const connection = RedisClient.getConnection();
if (!connection || !dailyLeaderboardsConfig.enabled) {
@@ -210,7 +211,7 @@ export class DailyLeaderboard {
export async function purgeUserFromDailyLeaderboards(
uid: string,
- configuration: SharedTypes.Configuration["dailyLeaderboards"]
+ configuration: Configuration["dailyLeaderboards"]
): Promise<void> {
const connection = RedisClient.getConnection();
if (!connection || !configuration.enabled) {
@@ -222,8 +223,8 @@ export async function purgeUserFromDailyLeaderboards(
}
function isValidModeRule(
- modeRule: SharedTypes.ValidModeRule,
- modeRules: SharedTypes.ValidModeRule[]
+ modeRule: ValidModeRule,
+ modeRules: ValidModeRule[]
): boolean {
const { language, mode, mode2 } = modeRule;
@@ -239,7 +240,7 @@ export function getDailyLeaderboard(
language: string,
mode: string,
mode2: string,
- dailyLeaderboardsConfig: SharedTypes.Configuration["dailyLeaderboards"],
+ dailyLeaderboardsConfig: Configuration["dailyLeaderboards"],
customTimestamp = -1
): DailyLeaderboard | null {
const { validModeRules, enabled } = dailyLeaderboardsConfig;
diff --git a/backend/src/utils/monkey-mail.ts b/backend/src/utils/monkey-mail.ts
index ceeb2d8bd..45f2a7eb1 100644
--- a/backend/src/utils/monkey-mail.ts
+++ b/backend/src/utils/monkey-mail.ts
@@ -1,10 +1,9 @@
+import { MonkeyMail } from "@monkeytype/shared-types";
import { v4 } from "uuid";
-type MonkeyMailOptions = Partial<Omit<SharedTypes.MonkeyMail, "id" | "read">>;
+type MonkeyMailOptions = Partial<Omit<MonkeyMail, "id" | "read">>;
-export function buildMonkeyMail(
- options: MonkeyMailOptions
-): SharedTypes.MonkeyMail {
+export function buildMonkeyMail(options: MonkeyMailOptions): MonkeyMail {
return {
id: v4(),
subject: options.subject ?? "",
diff --git a/backend/src/utils/pb.ts b/backend/src/utils/pb.ts
index bd20329d0..fdb38275d 100644
--- a/backend/src/utils/pb.ts
+++ b/backend/src/utils/pb.ts
@@ -1,16 +1,16 @@
import _ from "lodash";
import FunboxList from "../constants/funbox-list.js";
+import { PersonalBest, PersonalBests } from "@monkeytype/shared-types/user";
+import { DBResult } from "@monkeytype/shared-types";
+import { Mode, Mode2 } from "@monkeytype/shared-types/config";
type CheckAndUpdatePbResult = {
isPb: boolean;
- personalBests: SharedTypes.PersonalBests;
+ personalBests: PersonalBests;
lbPersonalBests?: MonkeyTypes.LbPersonalBests;
};
-type Result = Omit<
- SharedTypes.DBResult<SharedTypes.Config.Mode>,
- "_id" | "name"
->;
+type Result = Omit<DBResult<Mode>, "_id" | "name">;
export function canFunboxGetPb(result: Result): boolean {
const funbox = result.funbox;
@@ -30,20 +30,20 @@ export function canFunboxGetPb(result: Result): boolean {
}
export function checkAndUpdatePb(
- userPersonalBests: SharedTypes.PersonalBests,
+ userPersonalBests: PersonalBests,
lbPersonalBests: MonkeyTypes.LbPersonalBests | undefined,
result: Result
): CheckAndUpdatePbResult {
const mode = result.mode;
- const mode2 = result.mode2 as SharedTypes.Config.Mode2<"time">;
+ const mode2 = result.mode2 as Mode2<"time">;
const userPb = userPersonalBests ?? {};
userPb[mode] ??= {};
userPb[mode][mode2] ??= [];
- const personalBestMatch = (
- userPb[mode][mode2] as SharedTypes.PersonalBest[]
- ).find((pb) => matchesPersonalBest(result, pb));
+ const personalBestMatch = (userPb[mode][mode2] as PersonalBest[]).find((pb) =>
+ matchesPersonalBest(result, pb)
+ );
let isPb = true;
@@ -67,7 +67,7 @@ export function checkAndUpdatePb(
function matchesPersonalBest(
result: Result,
- personalBest: SharedTypes.PersonalBest
+ personalBest: PersonalBest
): boolean {
if (
result.difficulty === undefined ||
@@ -98,7 +98,7 @@ function matchesPersonalBest(
}
function updatePersonalBest(
- personalBest: SharedTypes.PersonalBest,
+ personalBest: PersonalBest,
result: Result
): boolean {
if (personalBest.wpm >= result.wpm) {
@@ -133,7 +133,7 @@ function updatePersonalBest(
return true;
}
-function buildPersonalBest(result: Result): SharedTypes.PersonalBest {
+function buildPersonalBest(result: Result): PersonalBest {
if (
result.difficulty === undefined ||
result.language === undefined ||
@@ -162,7 +162,7 @@ function buildPersonalBest(result: Result): SharedTypes.PersonalBest {
}
function updateLeaderboardPersonalBests(
- userPersonalBests: SharedTypes.PersonalBests,
+ userPersonalBests: PersonalBests,
lbPersonalBests: MonkeyTypes.LbPersonalBests,
result: Result
): void {
@@ -171,7 +171,7 @@ function updateLeaderboardPersonalBests(
}
const mode = result.mode;
- const mode2 = result.mode2 as SharedTypes.Config.Mode2<"time">;
+ const mode2 = result.mode2 as Mode2<"time">;
lbPersonalBests[mode] = lbPersonalBests[mode] ?? {};
const lbMode2 = lbPersonalBests[mode][mode2] as MonkeyTypes.LbPersonalBests;
@@ -181,7 +181,7 @@ function updateLeaderboardPersonalBests(
const bestForEveryLanguage = {};
- userPersonalBests[mode][mode2].forEach((pb: SharedTypes.PersonalBest) => {
+ userPersonalBests[mode][mode2].forEach((pb: PersonalBest) => {
const language = pb.language;
if (
bestForEveryLanguage[language] === undefined ||
@@ -191,20 +191,17 @@ function updateLeaderboardPersonalBests(
}
});
- _.each(
- bestForEveryLanguage,
- (pb: SharedTypes.PersonalBest, language: string) => {
- const languageDoesNotExist =
- lbPersonalBests[mode][mode2][language] === undefined;
-
- if (
- languageDoesNotExist ||
- lbPersonalBests[mode][mode2][language].wpm < pb.wpm
- ) {
- lbPersonalBests[mode][mode2][language] = pb;
- }
+ _.each(bestForEveryLanguage, (pb: PersonalBest, language: string) => {
+ const languageDoesNotExist =
+ lbPersonalBests[mode][mode2][language] === undefined;
+
+ if (
+ languageDoesNotExist ||
+ lbPersonalBests[mode][mode2][language].wpm < pb.wpm
+ ) {
+ lbPersonalBests[mode][mode2][language] = pb;
}
- );
+ });
}
function shouldUpdateLeaderboardPersonalBests(result: Result): boolean {
diff --git a/backend/src/utils/prometheus.ts b/backend/src/utils/prometheus.ts
index 55c5f0a96..d13c2da0d 100644
--- a/backend/src/utils/prometheus.ts
+++ b/backend/src/utils/prometheus.ts
@@ -1,3 +1,5 @@
+import { Result } from "@monkeytype/shared-types";
+import { Mode } from "@monkeytype/shared-types/config";
import "dotenv/config";
import { Counter, Histogram, Gauge } from "prom-client";
@@ -88,9 +90,7 @@ export function setLeaderboard(
leaderboardUpdate.set({ language, mode, mode2, step: "index" }, times[3]);
}
-export function incrementResult(
- res: SharedTypes.Result<SharedTypes.Config.Mode>
-): void {
+export function incrementResult(res: Result<Mode>): void {
const {
mode,
mode2,
diff --git a/backend/src/utils/result.ts b/backend/src/utils/result.ts
index b3487a78c..d009b6e63 100644
--- a/backend/src/utils/result.ts
+++ b/backend/src/utils/result.ts
@@ -1,11 +1,11 @@
+import { CompletedEvent, DBResult } from "@monkeytype/shared-types";
+import { Mode } from "@monkeytype/shared-types/config";
import { ObjectId } from "mongodb";
-type Result = MonkeyTypes.WithObjectId<
- SharedTypes.DBResult<SharedTypes.Config.Mode>
->;
+type Result = MonkeyTypes.WithObjectId<DBResult<Mode>>;
export function buildDbResult(
- completedEvent: SharedTypes.CompletedEvent,
+ completedEvent: CompletedEvent,
userName: string,
isPb: boolean
): Result {
diff --git a/backend/src/utils/validation.ts b/backend/src/utils/validation.ts
index ff8b4578b..d184b4056 100644
--- a/backend/src/utils/validation.ts
+++ b/backend/src/utils/validation.ts
@@ -3,6 +3,7 @@ import { replaceHomoglyphs } from "../constants/homoglyphs.js";
import { profanities } from "../constants/profanities.js";
import { intersect, sanitizeString } from "./misc.js";
import { default as FunboxList } from "../constants/funbox-list.js";
+import { CompletedEvent } from "@monkeytype/shared-types";
export function inRange(value: number, min: number, max: number): boolean {
return value >= min && value <= max;
@@ -48,7 +49,7 @@ export function isTagPresetNameValid(name: string): boolean {
return VALID_NAME_PATTERN.test(name);
}
-export function isTestTooShort(result: SharedTypes.CompletedEvent): boolean {
+export function isTestTooShort(result: CompletedEvent): boolean {
const { mode, mode2, customText, testDuration, bailedOut } = result;
if (mode === "time") {
diff --git a/backend/src/workers/later-worker.ts b/backend/src/workers/later-worker.ts
index 4b3bdc181..d43b20903 100644
--- a/backend/src/workers/later-worker.ts
+++ b/backend/src/workers/later-worker.ts
@@ -19,6 +19,7 @@ import LaterQueue, {
} from "../queues/later-queue.js";
import { recordTimeToCompleteJob } from "../utils/prometheus.js";
import { WeeklyXpLeaderboard } from "../services/weekly-xp-leaderboard.js";
+import { MonkeyMail } from "@monkeytype/shared-types";
async function handleDailyLeaderboardResults(
ctx: LaterTaskContexts["daily-leaderboard-results"]
@@ -48,7 +49,7 @@ async function handleDailyLeaderboardResults(
if (inboxConfig.enabled && xpRewardBrackets.length > 0) {
const mailEntries: {
uid: string;
- mail: SharedTypes.MonkeyMail[];
+ mail: MonkeyMail[];
}[] = [];
allResults.forEach((entry) => {
@@ -137,7 +138,7 @@ async function handleWeeklyXpLeaderboardResults(
const mailEntries: {
uid: string;
- mail: SharedTypes.MonkeyMail[];
+ mail: MonkeyMail[];
}[] = [];
allResults.forEach((entry) => {