diff options
author | Miodec <[email protected]> | 2024-07-18 16:36:46 +0200 |
---|---|---|
committer | Miodec <[email protected]> | 2024-07-18 16:36:46 +0200 |
commit | 6d1e070b827d7598b132c9cdfd2155ac43930524 (patch) | |
tree | 1c022c2539048e04fce3cbdb74fb373f6d3ae184 | |
parent | a80fed24bca401cbd3fdc4204fca75477962bf66 (diff) | |
download | monkeytype-6d1e070b827d7598b132c9cdfd2155ac43930524.tar.gz monkeytype-6d1e070b827d7598b132c9cdfd2155ac43930524.zip |
fully fix backend
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) => { |