summary refs log tree commit diff
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/assets/background.pngbin0 -> 319351 bytes
-rw-r--r--api/assets/fosscord-login.css23
-rw-r--r--api/assets/schemas.json15
-rw-r--r--api/client_test/index.html8
-rw-r--r--api/scripts/generate_schema.js22
-rw-r--r--api/src/middlewares/RateLimit.ts24
-rw-r--r--api/src/routes/guilds/#guild_id/index.ts1
-rw-r--r--api/src/routes/guilds/#guild_id/member-verification.ts14
-rw-r--r--api/src/routes/guilds/#guild_id/members/#member_id/index.ts3
-rw-r--r--api/src/routes/guilds/#guild_id/roles/#role_id/index.ts1
-rw-r--r--api/src/routes/guilds/#guild_id/welcome-screen.ts (renamed from api/src/routes/guilds/#guild_id/welcome_screen.ts)4
-rw-r--r--api/src/routes/guilds/index.ts2
-rw-r--r--api/src/routes/store/published-listings/skus/#sku_id/subscription-plans.ts16
-rw-r--r--api/src/routes/users/@me/index.ts7
14 files changed, 99 insertions, 41 deletions
diff --git a/api/assets/background.png b/api/assets/background.png
new file mode 100644
index 00000000..58369ab8
--- /dev/null
+++ b/api/assets/background.png
Binary files differdiff --git a/api/assets/fosscord-login.css b/api/assets/fosscord-login.css
index d507c545..ca0af064 100644
--- a/api/assets/fosscord-login.css
+++ b/api/assets/fosscord-login.css
@@ -14,7 +14,7 @@
 }
 h3.title-jXR8lp.marginBottom8-AtZOdT.base-1x0h_U.size24-RIRrxO::after {
 	margin-top: -32px;
-	content: "Welcome to Fosscord!";
+	content: "Welcome to Slowcord!";
 	visibility: visible;
 	display: block;
 }
@@ -62,7 +62,22 @@ h3.title-jXR8lp.marginBottom8-AtZOdT.base-1x0h_U.size24-RIRrxO::after {
 	margin-top: -16px;
 }
 
-/* shrink login box to same size as register */
-.authBoxExpanded-2jqaBe {
-	width: 480px !important;
+/* funny styling */
+.wrapper-6URcxg {
+	justify-content: flex-start !important;
+
+	background: url("/assets/background.png");
+	background-size: 100% 100%;
+	background-repeat: no-repeat;
+}
+
+.authBoxExpanded-2jqaBe,
+.authBox-hW6HRx {
+	width: max(40vw, 500px) !important;
+	height: 100vh !important;
+	padding: 100px !important;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	border-radius: 0 !important;
 }
diff --git a/api/assets/schemas.json b/api/assets/schemas.json
index 03c240a0..ae2426d1 100644
--- a/api/assets/schemas.json
+++ b/api/assets/schemas.json
@@ -6505,9 +6505,6 @@
             }
         },
         "additionalProperties": false,
-        "required": [
-            "name"
-        ],
         "definitions": {
             "Embed": {
                 "type": "object",
@@ -6867,6 +6864,9 @@
             "preferred_locale": {
                 "type": "string"
             },
+            "premium_progress_bar_enabled": {
+                "type": "boolean"
+            },
             "name": {
                 "maxLength": 100,
                 "type": "string"
@@ -6891,9 +6891,6 @@
             }
         },
         "additionalProperties": false,
-        "required": [
-            "name"
-        ],
         "definitions": {
             "Embed": {
                 "type": "object",
@@ -7213,6 +7210,9 @@
                 "items": {
                     "type": "string"
                 }
+            },
+            "nick": {
+                "type": "string"
             }
         },
         "additionalProperties": false,
@@ -10509,8 +10509,7 @@
                     "additionalProperties": false,
                     "required": [
                         "channel_id",
-                        "description",
-                        "emoji_name"
+                        "description"
                     ]
                 }
             },
diff --git a/api/client_test/index.html b/api/client_test/index.html
index b438b492..7a3e4695 100644
--- a/api/client_test/index.html
+++ b/api/client_test/index.html
@@ -71,10 +71,10 @@
 			}
 		</script>
 	 	<script src="/assets/checkLocale.js"></script>
-		<script src="/assets/1e18f2aac02e172db283.js"></script>
-		<script src="/assets/681e53cdfefa5b82249a.js"></script>
-		<script src="/assets/7a036838c0a0e73f59d8.js"></script>
-		<script src="/assets/b6cf2184a7a05e7525ce.js"></script>
+		 <script src="/assets/83ace7450e110d16319e.js"></script>
+		 <script src="/assets/e02290aaa8dac5d195c2.js"></script>
+		 <script src="/assets/4f3b3c576b879a5f75d1.js"></script>
+		 <script src="/assets/699456246fdfe7589855.js"></script>
 		<!-- plugin marker -->
 	</body>
 </html>
diff --git a/api/scripts/generate_schema.js b/api/scripts/generate_schema.js
index 7e742ec1..b56c3fbc 100644
--- a/api/scripts/generate_schema.js
+++ b/api/scripts/generate_schema.js
@@ -27,7 +27,16 @@ const Excluded = [
 	"Response",
 	"e.Response",
 	"request.Response",
-	"supertest.Response"
+	"supertest.Response",
+
+	// TODO: Figure out how to exclude schemas from node_modules?
+	"SomeJSONSchema",
+	"UncheckedPartialSchema",
+	"PartialSchema",
+	"UncheckedPropertiesSchema",
+	"PropertiesSchema",
+	"AsyncSchema",
+	"AnySchema",
 ];
 
 function modify(obj) {
@@ -39,11 +48,18 @@ function modify(obj) {
 }
 
 function main() {
-	const program = TJS.getProgramFromFiles(walk(path.join(__dirname, "..", "src", "routes")), compilerOptions);
+	const files = 		[
+		...walk(path.join(__dirname, "..", "src", "routes")),
+		...walk(path.join(__dirname, "..", "..", "util", "src")),
+	];
+	const program = TJS.getProgramFromFiles(
+		files,
+		compilerOptions
+	);
 	const generator = TJS.buildGenerator(program, settings);
 	if (!generator || !program) return;
 
-	const schemas = generator.getUserSymbols().filter((x) => (x.endsWith("Schema") || x.endsWith("Response")) && !Excluded.includes(x));
+	let schemas = generator.getUserSymbols().filter((x) => (x.endsWith("Schema") || x.endsWith("Response")) && !Excluded.includes(x));
 	console.log(schemas);
 
 	var definitions = {};
diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts
index 13f1602c..1a38cfcf 100644
--- a/api/src/middlewares/RateLimit.ts
+++ b/api/src/middlewares/RateLimit.ts
@@ -1,4 +1,4 @@
-import { Config, getRights, listenEvent, Rights } from "@fosscord/util";
+import { Config, listenEvent } from "@fosscord/util";
 import { NextFunction, Request, Response, Router } from "express";
 import { getIpAdress } from "@fosscord/api";
 import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
@@ -9,7 +9,6 @@ import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
 
 /*
 ? bucket limit? Max actions/sec per bucket?
-(ANSWER: a small fosscord instance might not need a complex rate limiting system)
 
 TODO: delay database requests to include multiple queries
 TODO: different for methods (GET/POST)
@@ -45,12 +44,6 @@ export default function rateLimit(opts: {
 	onlyIp?: boolean;
 }): any {
 	return async (req: Request, res: Response, next: NextFunction): Promise<any> => {
-		// exempt user? if so, immediately short circuit
-		if (req.user_id) {
-			const rights = await getRights(req.user_id);
-			if (rights.has("BYPASS_RATE_LIMITS")) return;
-		}
-
 		const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
 		var executor_id = getIpAdress(req);
 		if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
@@ -60,12 +53,12 @@ export default function rateLimit(opts: {
 		if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET;
 		else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY;
 
-		let offender = Cache.get(executor_id + bucket_id);
+		const offender = Cache.get(executor_id + bucket_id);
 
 		if (offender) {
-			let reset = offender.expires_at.getTime();
-			let resetAfterMs = reset - Date.now();
-			let resetAfterSec = Math.ceil(resetAfterMs / 1000);
+			const reset = offender.expires_at.getTime();
+			const resetAfterMs = reset - Date.now();
+			const resetAfterSec = resetAfterMs / 1000;
 
 			if (resetAfterMs <= 0) {
 				offender.hits = 0;
@@ -77,11 +70,6 @@ export default function rateLimit(opts: {
 
 			if (offender.blocked) {
 				const global = bucket_id === "global";
-				// each block violation pushes the expiry one full window further
-				reset += opts.window * 1000;
-				offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000);
-				resetAfterMs = reset - Date.now();
-				resetAfterSec = Math.ceil(resetAfterMs / 1000);
 
 				console.log("blocked bucket: " + bucket_id, { resetAfterMs });
 				return (
@@ -163,7 +151,7 @@ export async function initRateLimits(app: Router) {
 	app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
 }
 
-async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) {
+async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) {
 	const id = opts.executor_id + opts.bucket_id;
 	var limit = Cache.get(id);
 	if (!limit) {
diff --git a/api/src/routes/guilds/#guild_id/index.ts b/api/src/routes/guilds/#guild_id/index.ts
index 4ec3df72..45e30a74 100644
--- a/api/src/routes/guilds/#guild_id/index.ts
+++ b/api/src/routes/guilds/#guild_id/index.ts
@@ -20,6 +20,7 @@ export interface GuildUpdateSchema extends Omit<GuildCreateSchema, "channels"> {
 	afk_timeout?: number;
 	afk_channel_id?: string;
 	preferred_locale?: string;
+	premium_progress_bar_enabled?: boolean;
 }
 
 router.get("/", route({}), async (req: Request, res: Response) => {
diff --git a/api/src/routes/guilds/#guild_id/member-verification.ts b/api/src/routes/guilds/#guild_id/member-verification.ts
new file mode 100644
index 00000000..265a1b35
--- /dev/null
+++ b/api/src/routes/guilds/#guild_id/member-verification.ts
@@ -0,0 +1,14 @@
+import { Router, Request, Response } from "express";
+import { route } from "@fosscord/api";
+const router = Router();
+
+router.get("/",route({}), async (req: Request, res: Response) => {
+	// TODO: member verification
+
+	res.status(404).json({
+		message: "Unknown Guild Member Verification Form",
+		code: 10068
+	});
+});
+
+export default router;
diff --git a/api/src/routes/guilds/#guild_id/members/#member_id/index.ts b/api/src/routes/guilds/#guild_id/members/#member_id/index.ts
index c285abb3..2ff89eae 100644
--- a/api/src/routes/guilds/#guild_id/members/#member_id/index.ts
+++ b/api/src/routes/guilds/#guild_id/members/#member_id/index.ts
@@ -7,6 +7,7 @@ const router = Router();
 
 export interface MemberChangeSchema {
 	roles?: string[];
+	nick?: string;
 }
 
 router.get("/", route({}), async (req: Request, res: Response) => {
@@ -34,6 +35,8 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re
 		member.roles = body.roles.map((x) => new Role({ id: x })); // foreign key constraint will fail if role doesn't exist
 	}
 
+	if (body.nick) member.nick = body.nick;
+
 	await member.save();
 
 	member.roles = member.roles.filter((x) => x.id !== everyone.id);
diff --git a/api/src/routes/guilds/#guild_id/roles/#role_id/index.ts b/api/src/routes/guilds/#guild_id/roles/#role_id/index.ts
index 2ad01682..f3d707e0 100644
--- a/api/src/routes/guilds/#guild_id/roles/#role_id/index.ts
+++ b/api/src/routes/guilds/#guild_id/roles/#role_id/index.ts
@@ -42,6 +42,7 @@ router.patch("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }
 	const body = req.body as RoleModifySchema;
 
 	if (body.icon) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string);
+	else body.icon = undefined;
 
 	const role = new Role({
 		...body,
diff --git a/api/src/routes/guilds/#guild_id/welcome_screen.ts b/api/src/routes/guilds/#guild_id/welcome-screen.ts
index 7141f17e..5c7a9daa 100644
--- a/api/src/routes/guilds/#guild_id/welcome_screen.ts
+++ b/api/src/routes/guilds/#guild_id/welcome-screen.ts
@@ -10,7 +10,7 @@ export interface GuildUpdateWelcomeScreenSchema {
 		channel_id: string;
 		description: string;
 		emoji_id?: string;
-		emoji_name: string;
+		emoji_name?: string;
 	}[];
 	enabled?: boolean;
 	description?: string;
@@ -36,6 +36,8 @@ router.patch("/", route({ body: "GuildUpdateWelcomeScreenSchema", permission: "M
 	if (body.description) guild.welcome_screen.description = body.description;
 	if (body.enabled != null) guild.welcome_screen.enabled = body.enabled;
 
+	await guild.save();
+
 	res.sendStatus(204);
 });
 
diff --git a/api/src/routes/guilds/index.ts b/api/src/routes/guilds/index.ts
index 10721413..489dea49 100644
--- a/api/src/routes/guilds/index.ts
+++ b/api/src/routes/guilds/index.ts
@@ -9,7 +9,7 @@ export interface GuildCreateSchema {
 	/**
 	 * @maxLength 100
 	 */
-	name: string;
+	name?: string;
 	region?: string;
 	icon?: string | null;
 	channels?: ChannelModifySchema[];
diff --git a/api/src/routes/store/published-listings/skus/#sku_id/subscription-plans.ts b/api/src/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
index 723a5160..03162ec8 100644
--- a/api/src/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
+++ b/api/src/routes/store/published-listings/skus/#sku_id/subscription-plans.ts
@@ -5,6 +5,22 @@ const router: Router = Router();
 
 const skus = new Map([
 	[
+		"978380684370378762",
+		[
+			{
+				id: "978380692553465866",
+				name: "Nitro Lite Monthly",
+				interval: 1,
+				interval_count: 1,
+				tag_inclusive: true,
+				sku_id: "978380684370378762",
+				currency: "usd",
+				price: 0,
+				price_tier: null,
+			}
+		]
+	],
+	[
 		"521842865731534868",
 		[
 			{
diff --git a/api/src/routes/users/@me/index.ts b/api/src/routes/users/@me/index.ts
index c3f6bfe0..6be9d3f2 100644
--- a/api/src/routes/users/@me/index.ts
+++ b/api/src/routes/users/@me/index.ts
@@ -2,6 +2,7 @@ import { Router, Request, Response } from "express";
 import { User, PrivateUserProjection, emitEvent, UserUpdateEvent, handleFile, FieldErrors, adjustEmail, Config } from "@fosscord/util";
 import { route } from "@fosscord/api";
 import bcrypt from "bcrypt";
+import { HTTPError } from "lambert-server";
 
 const router: Router = Router();
 
@@ -31,11 +32,13 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res: Response) => {
 	const body = req.body as UserModifySchema;
 
+	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
+
+	if (user.email == "demo@maddy.k.vu") throw new HTTPError("Demo user, sorry", 400);
+
 	if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string);
 	if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
 
-	const user = await User.findOneOrFail({ where: { id: req.user_id }, select: [...PrivateUserProjection, "data"] });
-
 	if (body.password) {
 		if (user.data?.hash) {
 			const same_password = await bcrypt.compare(body.password, user.data.hash || "");