Skip to content

Commit

Permalink
migrate all, add backend implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
fehmer committed Sep 5, 2024
1 parent 85f5670 commit 3f07c81
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 153 deletions.
28 changes: 21 additions & 7 deletions backend/scripts/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,14 @@ export function getOpenApi(): OpenAPIObject {
setOperationId: "concatenated-path",
operationMapper: (operation, route) => {
const metadata = route.metadata as EndpointMetadata;
if (!operation.description?.trim()?.endsWith("."))
operation.description += ".";
operation.description += "\n\n";

addAuth(operation, metadata);
addTags(operation, metadata);
addRateLimit(operation, metadata);

addRequiredConfiguration(operation, metadata);
addTags(operation, metadata);
return operation;
},
}
Expand All @@ -169,6 +172,10 @@ function addAuth(
const includeInPublic = auth.isPublic === true || auth.acceptApeKeys === true;
operation["x-public"] = includeInPublic ? "yes" : "no";
operation.security = security;

if (roles.length !== 0) {
operation.description += ` **Required roles:** ${roles.join(", ")}\n\n`;
}
}

function getRequiredRoles(
Expand Down Expand Up @@ -199,9 +206,6 @@ function addRateLimit(
const okResponse = operation.responses["200"];
if (okResponse === undefined) return;

if (!operation.description?.trim()?.endsWith("."))
operation.description += ".";

operation.description += getRateLimitDescription(metadata.rateLimit);

okResponse["headers"] = {
Expand All @@ -223,7 +227,7 @@ function addRateLimit(
function getRateLimitDescription(limit: RateLimit | ApeKeyRateLimit): string {
const limits = getLimits(limit);

let result = ` This operation can be called up to ${
let result = ` **Rate limit:** This operation can be called up to ${
limits.limiter.max
} times ${formatWindow(limits.limiter.window)} for regular users`;

Expand All @@ -233,7 +237,7 @@ function getRateLimitDescription(limit: RateLimit | ApeKeyRateLimit): string {
)} with ApeKeys`;
}

return result + ".";
return result + ".\n\n";
}

function formatWindow(window: Window): string {
Expand Down Expand Up @@ -261,6 +265,16 @@ function formatWindow(window: Window): string {
}
}

function addRequiredConfiguration(
operation: OperationObject,
metadata: EndpointMetadata | undefined
): void {
if (metadata === undefined || metadata.requireConfiguration === undefined)
return;

operation.description += `**Required configuration:** This operation can only be called if the [configuration](#tag/configuration/operation/configuration.get) for \`${metadata.requireConfiguration.path}\` is \`true\`.\n\n`;
}

//detect if we run this as a main
if (require.main === module) {
const args = process.argv.slice(2);
Expand Down
15 changes: 0 additions & 15 deletions backend/src/api/routes/ape-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,18 @@ import { initServer } from "@ts-rest/express";
import * as ApeKeyController from "../controllers/ape-key";
import { callController } from "../ts-rest-adapter";

import { validate } from "../../middlewares/configuration";

const commonMiddleware = [
validate({
criteria: (configuration) => {
return configuration.apeKeys.endpointsEnabled;
},
invalidMessage: "ApeKeys are currently disabled.",
}),
];

const s = initServer();
export default s.router(apeKeysContract, {
get: {
middleware: commonMiddleware,
handler: async (r) => callController(ApeKeyController.getApeKeys)(r),
},
add: {
middleware: commonMiddleware,
handler: async (r) => callController(ApeKeyController.generateApeKey)(r),
},
save: {
middleware: commonMiddleware,
handler: async (r) => callController(ApeKeyController.editApeKey)(r),
},
delete: {
middleware: commonMiddleware,
handler: async (r) => callController(ApeKeyController.deleteApeKey)(r),
},
});
2 changes: 2 additions & 0 deletions backend/src/api/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { MonkeyValidationError } from "@monkeytype/contracts/schemas/api";
import { authenticateTsRestRequest } from "../../middlewares/auth";
import { rateLimitRequest } from "../../middlewares/rate-limit";
import { checkRequiredRole } from "../../middlewares/permission";
import { checkRequiredConfiguration } from "../../middlewares/configuration";

const pathOverride = process.env["API_PATH_OVERRIDE"];
const BASE_ROUTE = pathOverride !== undefined ? `/${pathOverride}` : "";
Expand Down Expand Up @@ -115,6 +116,7 @@ function applyTsRestApiRoutes(app: IRouter): void {
},
globalMiddleware: [
authenticateTsRestRequest(),
checkRequiredConfiguration(),
rateLimitRequest(),
checkRequiredRole(),
],
Expand Down
20 changes: 0 additions & 20 deletions backend/src/api/routes/leaderboards.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
import { initServer } from "@ts-rest/express";
import { validate } from "../../middlewares/configuration";
import * as LeaderboardController from "../controllers/leaderboard";

import { leaderboardsContract } from "@monkeytype/contracts/leaderboards";
import { callController } from "../ts-rest-adapter";

const requireDailyLeaderboardsEnabled = validate({
criteria: (configuration) => {
return configuration.dailyLeaderboards.enabled;
},
invalidMessage: "Daily leaderboards are not available at this time.",
});

const requireWeeklyXpLeaderboardEnabled = validate({
criteria: (configuration) => {
return configuration.leaderboards.weeklyXp.enabled;
},
invalidMessage: "Weekly XP leaderboards are not available at this time.",
});

const s = initServer();
export default s.router(leaderboardsContract, {
get: {
Expand All @@ -30,22 +14,18 @@ export default s.router(leaderboardsContract, {
callController(LeaderboardController.getRankFromLeaderboard)(r),
},
getDaily: {
middleware: [requireDailyLeaderboardsEnabled],
handler: async (r) =>
callController(LeaderboardController.getDailyLeaderboard)(r),
},
getDailyRank: {
middleware: [requireDailyLeaderboardsEnabled],
handler: async (r) =>
callController(LeaderboardController.getDailyLeaderboardRank)(r),
},
getWeeklyXp: {
middleware: [requireWeeklyXpLeaderboardEnabled],
handler: async (r) =>
callController(LeaderboardController.getWeeklyXpLeaderboardResults)(r),
},
getWeeklyXpRank: {
middleware: [requireWeeklyXpLeaderboardEnabled],
handler: async (r) =>
callController(LeaderboardController.getWeeklyXpLeaderboardRank)(r),
},
Expand Down
18 changes: 0 additions & 18 deletions backend/src/api/routes/quotes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { quotesContract } from "@monkeytype/contracts/quotes";
import { initServer } from "@ts-rest/express";
import { validate } from "../../middlewares/configuration";
import * as QuoteController from "../controllers/quote";
import { callController } from "../ts-rest-adapter";

Expand All @@ -14,15 +13,6 @@ export default s.router(quotesContract, {
callController(QuoteController.isSubmissionEnabled)(r),
},
add: {
middleware: [
validate({
criteria: (configuration) => {
return configuration.quotes.submissionsEnabled;
},
invalidMessage:
"Quote submission is disabled temporarily. The queue is quite long and we need some time to catch up.",
}),
],
handler: async (r) => callController(QuoteController.addQuote)(r),
},
approveSubmission: {
Expand All @@ -38,14 +28,6 @@ export default s.router(quotesContract, {
handler: async (r) => callController(QuoteController.submitRating)(r),
},
report: {
middleware: [
validate({
criteria: (configuration) => {
return configuration.quotes.reporting.enabled;
},
invalidMessage: "Quote reporting is unavailable.",
}),
],
handler: async (r) => callController(QuoteController.reportQuote)(r),
},
});
9 changes: 0 additions & 9 deletions backend/src/api/routes/results.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import { resultsContract } from "@monkeytype/contracts/results";
import { initServer } from "@ts-rest/express";
import { validate } from "../../middlewares/configuration";
import * as ResultController from "../controllers/result";
import { callController } from "../ts-rest-adapter";

const validateResultSavingEnabled = validate({
criteria: (configuration) => {
return configuration.results.savingEnabled;
},
invalidMessage: "Results are not being saved at this time.",
});

const s = initServer();
export default s.router(resultsContract, {
get: {
handler: async (r) => callController(ResultController.getResults)(r),
},
add: {
middleware: [validateResultSavingEnabled],
handler: async (r) => callController(ResultController.addResult)(r),
},
updateTags: {
Expand Down
53 changes: 0 additions & 53 deletions backend/src/api/routes/users.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,14 @@
import { usersContract } from "@monkeytype/contracts/users";
import { initServer } from "@ts-rest/express";
import { validate } from "../../middlewares/configuration";
import * as UserController from "../controllers/user";
import { callController } from "../ts-rest-adapter";

const requireFilterPresetsEnabled = validate({
criteria: (configuration) => {
return configuration.results.filterPresets.enabled;
},
invalidMessage: "Result filter presets are not available at this time.",
});

const requireDiscordIntegrationEnabled = validate({
criteria: (configuration) => {
return configuration.users.discordIntegration.enabled;
},
invalidMessage: "Discord integration is not available at this time",
});

const requireProfilesEnabled = validate({
criteria: (configuration) => {
return configuration.users.profiles.enabled;
},
invalidMessage: "Profiles are not available at this time",
});

const requireInboxEnabled = validate({
criteria: (configuration) => {
return configuration.users.inbox.enabled;
},
invalidMessage: "Your inbox is not available at this time.",
});

const s = initServer();
export default s.router(usersContract, {
get: {
handler: async (r) => callController(UserController.getUser)(r),
},
create: {
middleware: [
validate({
criteria: (configuration) => {
return configuration.users.signUp;
},
invalidMessage: "Sign up is temporarily disabled",
}),
],
handler: async (r) => callController(UserController.createNewUser)(r),
},
getNameAvailability: {
Expand Down Expand Up @@ -80,12 +43,10 @@ export default s.router(usersContract, {
callController(UserController.optOutOfLeaderboards)(r),
},
addResultFilterPreset: {
middleware: [requireFilterPresetsEnabled],
handler: async (r) =>
callController(UserController.addResultFilterPreset)(r),
},
removeResultFilterPreset: {
middleware: [requireFilterPresetsEnabled],
handler: async (r) =>
callController(UserController.removeResultFilterPreset)(r),
},
Expand Down Expand Up @@ -117,11 +78,9 @@ export default s.router(usersContract, {
handler: async (r) => callController(UserController.editCustomTheme)(r),
},
getDiscordOAuth: {
middleware: [requireDiscordIntegrationEnabled],
handler: async (r) => callController(UserController.getOauthLink)(r),
},
linkDiscord: {
middleware: [requireDiscordIntegrationEnabled],
handler: async (r) => callController(UserController.linkDiscord)(r),
},
unlinkDiscord: {
Expand All @@ -143,30 +102,18 @@ export default s.router(usersContract, {
handler: async (r) => callController(UserController.removeFavoriteQuote)(r),
},
getProfile: {
middleware: [requireProfilesEnabled],
handler: async (r) => callController(UserController.getProfile)(r),
},
updateProfile: {
middleware: [requireProfilesEnabled],
handler: async (r) => callController(UserController.updateProfile)(r),
},
getInbox: {
middleware: [requireInboxEnabled],
handler: async (r) => callController(UserController.getInbox)(r),
},
updateInbox: {
middleware: [requireInboxEnabled],
handler: async (r) => callController(UserController.updateInbox)(r),
},
report: {
middleware: [
validate({
criteria: (configuration) => {
return configuration.quotes.reporting.enabled;
},
invalidMessage: "User reporting is unavailable.",
}),
],
handler: async (r) => callController(UserController.reportUser)(r),
},
verificationEmail: {
Expand Down
Loading

0 comments on commit 3f07c81

Please sign in to comment.