diff --git a/assets/openapi.json b/assets/openapi.json
index 095d60d3..aa965def 100644
--- a/assets/openapi.json
+++ b/assets/openapi.json
@@ -205,37 +205,6 @@
"type"
]
},
-<<<<<<< HEAD
- "AckBulkSchema": {
- "type": "object",
- "properties": {
- "read_states": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "channel_id": {
- "type": "string"
- },
- "message_id": {
- "type": "string"
- },
- "read_state_type": {
- "type": "integer"
- }
- },
- "additionalProperties": false,
- "required": [
- "channel_id",
- "message_id",
- "read_state_type"
- ]
- }
- }
- },
- "required": [
- "read_states"
-=======
"RouteResponse": {
"type": "object",
"properties": {
@@ -268,7 +237,6 @@
"code",
"errors",
"message"
->>>>>>> 288a50d8d (Clean up imports in schemas/api/bots)
]
},
"ActivitySchema": {
@@ -1666,12 +1634,20 @@
"type"
]
},
- "GatewayBotResponse": {
+ "GatewayResponse": {
"type": "object",
"properties": {
"url": {
"type": "string"
- },
+ }
+ },
+ "required": [
+ "url"
+ ]
+ },
+ "GatewayBotResponse": {
+ "type": "object",
+ "properties": {
"shards": {
"type": "integer"
},
@@ -1698,22 +1674,14 @@
"reset_after",
"total"
]
- }
- },
- "required": [
- "session_start_limit",
- "shards",
- "url"
- ]
- },
- "GatewayResponse": {
- "type": "object",
- "properties": {
+ },
"url": {
"type": "string"
}
},
"required": [
+ "session_start_limit",
+ "shards",
"url"
]
},
@@ -2276,14 +2244,8 @@
"type": "boolean"
},
"channel_id": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Snowflake"
- },
- {
- "type": "null"
- }
- ]
+ "type": "string",
+ "nullable": true
}
},
"required": [
@@ -2530,6 +2492,34 @@
"location"
]
},
+ "PartialConnectedAccountResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "verified": {
+ "type": "boolean"
+ },
+ "metadata": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": true
+ }
+ },
+ "required": [
+ "id",
+ "name",
+ "type",
+ "verified"
+ ]
+ },
"PreloadMessagesResponseSchema": {
"type": "array",
"items": {
@@ -3632,6 +3622,72 @@
"type"
]
},
+ "CreateReportSchema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "variant": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "breadcrumbs": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "elements": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "guild_id": {
+ "type": "string"
+ },
+ "stage_instance_id": {
+ "type": "string"
+ },
+ "guild_scheduled_event_id": {
+ "type": "string"
+ },
+ "reported_user_id": {
+ "type": "string"
+ },
+ "application_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "string"
+ },
+ "widget_id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "breadcrumbs",
+ "language",
+ "name",
+ "variant",
+ "version"
+ ]
+ },
"IdentifySchema": {
"type": "object",
"properties": {
@@ -5097,34 +5153,36 @@
"user_profile"
]
},
+ "UserRelationResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "discriminator": {
+ "type": "string"
+ },
+ "avatar": {
+ "type": "string"
+ },
+ "public_flags": {
+ "type": "integer"
+ }
+ },
+ "required": [
+ "discriminator",
+ "id",
+ "public_flags",
+ "username"
+ ]
+ },
"UserRelationsResponse": {
"type": "array",
"items": {
- "additionalProperties": false,
- "type": "object",
- "properties": {
- "id": {
- "type": "string"
- },
- "username": {
- "type": "string"
- },
- "discriminator": {
- "type": "string"
- },
- "avatar": {
- "type": "string"
- },
- "public_flags": {
- "type": "integer"
- }
- },
- "required": [
- "discriminator",
- "id",
- "public_flags",
- "username"
- ]
+ "$ref": "#/components/schemas/UserRelationResponse"
}
},
"UserRelationshipsResponse": {
@@ -5369,6 +5427,37 @@
}
}
},
+ "AckBulkSchema": {
+ "type": "object",
+ "properties": {
+ "read_states": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "read_state_type": {
+ "$ref": "#/components/schemas/ReadStateType"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "channel_id",
+ "message_id",
+ "read_state_type"
+ ]
+ }
+ }
+ },
+ "required": [
+ "read_states"
+ ]
+ },
"AcknowledgeDeleteSchema": {
"type": "object",
"properties": {
@@ -5630,12 +5719,12 @@
"additionalProperties": true
},
"original_response_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/components/schemas/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
},
"interacted_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/components/schemas/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
},
"triggering_interaction_metadata": {
"$ref": "#/components/schemas/MessageInteractionSchema"
@@ -5644,8 +5733,8 @@
"$ref": "#/components/schemas/PublicUser"
},
"target_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/components/schemas/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
}
},
"required": [
@@ -7250,72 +7339,6 @@
"name"
]
},
- "CreateReportSchema": {
- "type": "object",
- "properties": {
- "version": {
- "type": "string"
- },
- "variant": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "language": {
- "type": "string"
- },
- "breadcrumbs": {
- "type": "array",
- "items": {
- "type": "integer"
- }
- },
- "elements": {
- "type": "object",
- "additionalProperties": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "channel_id": {
- "type": "string"
- },
- "message_id": {
- "type": "string"
- },
- "guild_id": {
- "type": "string"
- },
- "stage_instance_id": {
- "type": "string"
- },
- "guild_scheduled_event_id": {
- "type": "string"
- },
- "reported_user_id": {
- "type": "string"
- },
- "application_id": {
- "type": "string"
- },
- "user_id": {
- "type": "string"
- },
- "widget_id": {
- "type": "string"
- }
- },
- "required": [
- "breadcrumbs",
- "language",
- "name",
- "variant",
- "version"
- ]
- },
"SessionsLogoutSchema": {
"type": "object",
"properties": {
@@ -13307,10 +13330,6 @@
"code"
]
},
- "Snowflake": {
- "description": "A container for useful snowflake-related methods.",
- "type": "object"
- },
"HubDirectoryEntry": {
"type": "object",
"properties": {
diff --git a/assets/schemas.json b/assets/schemas.json
index 7de87741..e28447eb 100644
--- a/assets/schemas.json
+++ b/assets/schemas.json
@@ -186,33 +186,6 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
-<<<<<<< HEAD
- "AckBulkSchema": {
- "type": "object",
- "properties": {
- "read_states": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "channel_id": {
- "type": "string"
- },
- "message_id": {
- "type": "string"
- },
- "read_state_type": {
- "type": "integer"
- }
- },
- "additionalProperties": false,
- "required": [
- "channel_id",
- "message_id",
- "read_state_type"
- ]
- }
-=======
"RouteResponse": {
"type": "object",
"properties": {
@@ -241,18 +214,13 @@
},
"errors": {
"$ref": "#/definitions/ErrorList"
->>>>>>> 288a50d8d (Clean up imports in schemas/api/bots)
}
},
"additionalProperties": false,
"required": [
-<<<<<<< HEAD
- "read_states"
-=======
"code",
"errors",
"message"
->>>>>>> 288a50d8d (Clean up imports in schemas/api/bots)
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
@@ -1765,12 +1733,22 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
- "GatewayBotResponse": {
+ "GatewayResponse": {
"type": "object",
"properties": {
"url": {
"type": "string"
- },
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "url"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
+ "GatewayBotResponse": {
+ "type": "object",
+ "properties": {
"shards": {
"type": "integer"
},
@@ -1797,25 +1775,15 @@
"reset_after",
"total"
]
- }
- },
- "additionalProperties": false,
- "required": [
- "session_start_limit",
- "shards",
- "url"
- ],
- "$schema": "http://json-schema.org/draft-07/schema#"
- },
- "GatewayResponse": {
- "type": "object",
- "properties": {
+ },
"url": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
+ "session_start_limit",
+ "shards",
"url"
],
"$schema": "http://json-schema.org/draft-07/schema#"
@@ -2414,13 +2382,9 @@
"type": "boolean"
},
"channel_id": {
- "anyOf": [
- {
- "$ref": "#/definitions/Snowflake"
- },
- {
- "type": "null"
- }
+ "type": [
+ "null",
+ "string"
]
}
},
@@ -2685,6 +2649,36 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "PartialConnectedAccountResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "verified": {
+ "type": "boolean"
+ },
+ "metadata": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": true
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "name",
+ "type",
+ "verified"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"PreloadMessagesResponseSchema": {
"type": "array",
"items": {
@@ -3852,6 +3846,74 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "CreateReportSchema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "variant": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "breadcrumbs": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "elements": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "guild_id": {
+ "type": "string"
+ },
+ "stage_instance_id": {
+ "type": "string"
+ },
+ "guild_scheduled_event_id": {
+ "type": "string"
+ },
+ "reported_user_id": {
+ "type": "string"
+ },
+ "application_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "string"
+ },
+ "widget_id": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "breadcrumbs",
+ "language",
+ "name",
+ "variant",
+ "version"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"IdentifySchema": {
"type": "object",
"properties": {
@@ -5405,34 +5467,38 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "UserRelationResponse": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "discriminator": {
+ "type": "string"
+ },
+ "avatar": {
+ "type": "string"
+ },
+ "public_flags": {
+ "type": "integer"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "discriminator",
+ "id",
+ "public_flags",
+ "username"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"UserRelationsResponse": {
"type": "array",
"items": {
- "additionalProperties": false,
- "type": "object",
- "properties": {
- "id": {
- "type": "string"
- },
- "username": {
- "type": "string"
- },
- "discriminator": {
- "type": "string"
- },
- "avatar": {
- "type": "string"
- },
- "public_flags": {
- "type": "integer"
- }
- },
- "required": [
- "discriminator",
- "id",
- "public_flags",
- "username"
- ]
+ "$ref": "#/definitions/UserRelationResponse"
},
"$schema": "http://json-schema.org/draft-07/schema#"
},
@@ -5705,6 +5771,39 @@
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "AckBulkSchema": {
+ "type": "object",
+ "properties": {
+ "read_states": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "read_state_type": {
+ "$ref": "#/definitions/ReadStateType"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "channel_id",
+ "message_id",
+ "read_state_type"
+ ]
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "read_states"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"AcknowledgeDeleteSchema": {
"type": "object",
"properties": {
@@ -5972,12 +6071,12 @@
"additionalProperties": true
},
"original_response_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/definitions/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
},
"interacted_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/definitions/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
},
"triggering_interaction_metadata": {
"$ref": "#/definitions/MessageInteractionSchema"
@@ -5986,8 +6085,8 @@
"$ref": "#/definitions/PublicUser"
},
"target_message_id": {
- "description": "A container for useful snowflake-related methods.",
- "$ref": "#/definitions/Snowflake"
+ "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```",
+ "type": "string"
}
},
"additionalProperties": false,
@@ -7717,74 +7816,6 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
- "CreateReportSchema": {
- "type": "object",
- "properties": {
- "version": {
- "type": "string"
- },
- "variant": {
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "language": {
- "type": "string"
- },
- "breadcrumbs": {
- "type": "array",
- "items": {
- "type": "integer"
- }
- },
- "elements": {
- "type": "object",
- "additionalProperties": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "channel_id": {
- "type": "string"
- },
- "message_id": {
- "type": "string"
- },
- "guild_id": {
- "type": "string"
- },
- "stage_instance_id": {
- "type": "string"
- },
- "guild_scheduled_event_id": {
- "type": "string"
- },
- "reported_user_id": {
- "type": "string"
- },
- "application_id": {
- "type": "string"
- },
- "user_id": {
- "type": "string"
- },
- "widget_id": {
- "type": "string"
- }
- },
- "additionalProperties": false,
- "required": [
- "breadcrumbs",
- "language",
- "name",
- "variant",
- "version"
- ],
- "$schema": "http://json-schema.org/draft-07/schema#"
- },
"SessionsLogoutSchema": {
"type": "object",
"properties": {
@@ -14212,12 +14243,6 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
- "Snowflake": {
- "description": "A container for useful snowflake-related methods.",
- "type": "object",
- "additionalProperties": false,
- "$schema": "http://json-schema.org/draft-07/schema#"
- },
"HubDirectoryEntry": {
"type": "object",
"properties": {
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index 5c40ce1d..79929f5e 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -17,7 +17,7 @@
*/
import { route, verifyCaptcha } from "@spacebar/api";
-import { Config, FieldErrors, Invite, User, ValidRegistrationToken, generateToken, IpDataClient, AbuseIpDbClient } from "@spacebar/util";
+import { Config, FieldErrors, Invite, User, ValidRegistrationToken, generateToken, IpDataClient, AbuseIpDbClient, TimeSpan } from "@spacebar/util";
import bcrypt from "bcrypt";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
@@ -26,6 +26,15 @@ import { RegisterSchema } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });
+const recentlyBlockedIps: {
+ [ip: string]: {
+ firstHit: Date;
+ lastHit: Date;
+ hits: number;
+ reason: string;
+ };
+} = {};
+
router.post(
"/",
route({
@@ -82,7 +91,7 @@ router.post(
throw FieldErrors({
email: {
code: "DISABLED",
- message: "registration is disabled on this instance",
+ message: "Registration is disabled on this instance",
},
});
}
@@ -124,20 +133,44 @@ router.post(
}
}
+ //region IP checks
+ const cacheBlockedIp = (ip: string, reason: string) => {
+ recentlyBlockedIps[ip] = {
+ firstHit: new Date(),
+ lastHit: new Date(),
+ hits: 0,
+ reason,
+ };
+ console.log(`[Register] ${ip} blocked from registration:`, reason);
+ };
+
+ if (!regTokenUsed && recentlyBlockedIps[ip]) {
+ if (new TimeSpan(recentlyBlockedIps[ip].firstHit.getTime(), new Date().getTime()).totalHours >= 24) delete recentlyBlockedIps[ip];
+ else {
+ recentlyBlockedIps[ip].lastHit = new Date();
+ recentlyBlockedIps[ip].hits++;
+ console.log(
+ `[Register] ${ip} blocked from registration: blocked since ${recentlyBlockedIps[ip].firstHit} with ${recentlyBlockedIps[ip].hits} hits and reason:`,
+ recentlyBlockedIps[ip].reason,
+ );
+ throw new HTTPError("Your IP is blocked from registration");
+ }
+ }
+
if (!regTokenUsed && register.enableAbuseIpDb) {
const blacklist = await AbuseIpDbClient.getBlacklist();
if (blacklist) {
const entry = blacklist.data.find((e) => e.ipAddress === ip);
if (entry && entry.abuseConfidenceScore >= register.blockAbuseIpDbAboveScore) {
- console.log(`[Register] ${ip} blocked from registration: AbuseIPDB score ${entry.abuseConfidenceScore} >= ${register.blockAbuseIpDbAboveScore} (BLACKLIST)`);
+ cacheBlockedIp(ip, `AbuseIPDB score ${entry.abuseConfidenceScore} >= ${register.blockAbuseIpDbAboveScore} (BLACKLIST)`);
throw new HTTPError("Your IP is blocked from registration");
}
}
const checkIp = await AbuseIpDbClient.checkIpAddress(ip);
if (checkIp?.data && checkIp.data.abuseConfidenceScore >= register.blockAbuseIpDbAboveScore) {
- console.log(`[Register] ${ip} blocked from registration: AbuseIPDB score ${checkIp.data.abuseConfidenceScore} >= ${register.blockAbuseIpDbAboveScore} (CHECK)`);
+ cacheBlockedIp(ip, `AbuseIPDB score ${checkIp.data.abuseConfidenceScore} >= ${register.blockAbuseIpDbAboveScore} (CHECK)`);
throw new HTTPError("Your IP is blocked from registration");
}
}
@@ -153,25 +186,26 @@ router.post(
.map(([key]) => key.replace("is_", ""));
const blockedCategories = new Set(categories).intersection(new Set(register.blockIpDataCoThreatTypes));
if (blockedCategories.size > 0) {
- console.log(`[Register] ${ip} blocked from registration: IPData.co threat types ${Array.from(blockedCategories).join(", ")}`);
+ cacheBlockedIp(ip, `IPData.co threat types ${Array.from(blockedCategories).join(", ")}`);
throw new HTTPError("Your IP is blocked from registration");
}
if (ipData.asn.type && register.blockAsnTypes.includes(ipData.asn.type)) {
- console.log(`[Register] ${ip} blocked from registration: IPData.co ASN type ${ipData.asn.type} is blocked`);
+ cacheBlockedIp(ip, `IPData.co ASN type ${ipData.asn.type} is blocked`);
throw new HTTPError("Your IP is blocked from registration");
} else if (!ipData.asn.type) {
console.log("[Register] IPData.co response missing asn.type field", ipData);
}
if (ipData.asn.asn && register.blockAsns.includes(ipData.asn.asn)) {
- console.log(`[Register] ${ip} blocked from registration: IPData.co ASN ${ipData.asn.name} is blocked`);
+ cacheBlockedIp(ip, `IPData.co ASN ${ipData.asn.name} is blocked`);
throw new HTTPError("Your IP is blocked from registration");
} else if (!ipData.asn.asn) {
console.log("[Register] IPData.co response missing asn.asn field", ipData);
}
}
}
+ //endregion
// TODO: gift_code_sku_id?
// TODO: check password strength
diff --git a/src/api/routes/reporting/index.ts b/src/api/routes/reporting/index.ts
index 76b5169a..27d284d7 100644
--- a/src/api/routes/reporting/index.ts
+++ b/src/api/routes/reporting/index.ts
@@ -16,14 +16,13 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import { route } from "@spacebar/api";
-import { Request, Response, Router } from "express";
-import { ReportMenuType, ReportMenuTypeNames } from "../../../schemas/api/reports/ReportMenu";
+import fs from "node:fs";
import path from "node:path";
+import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
-import { CreateReportSchema } from "../../../schemas/api/reports/CreateReport";
+import { route } from "@spacebar/api";
+import { ReportMenuType, ReportMenuTypeNames, CreateReportSchema } from "@spacebar/schemas";
import { FieldErrors } from "@spacebar/util";
-import fs from "node:fs";
const router = Router({ mergeParams: true });
if (process.env.LOG_ROUTES !== "false") console.log("[Server] Registering reporting menu routes...");
|