From ace70841b08ac7e5f7ac199450718bd5dcaf2f02 Mon Sep 17 00:00:00 2001 From: Charmunks Date: Thu, 6 Nov 2025 14:20:04 -0500 Subject: [PATCH] space limit --- client/src/lib/Dashboard.svelte | 6 ++++- src/api/spaces/space.route.js | 2 +- src/api/users/auth.route.js | 3 ++- src/utils/spaces.js | 4 +++- src/utils/user.js | 39 +++++++++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/client/src/lib/Dashboard.svelte b/client/src/lib/Dashboard.svelte index 319103b..f501a9c 100644 --- a/client/src/lib/Dashboard.svelte +++ b/client/src/lib/Dashboard.svelte @@ -48,7 +48,11 @@ newSpacePassword = ''; await loadSpaces(); } else { - error = data.error || ERROR_MESSAGES.CREATE_FAILED; + if (response.status === 403 && data.error?.includes('Maximum space limit')) { + error = data.error; + } else { + error = data.error || ERROR_MESSAGES.CREATE_FAILED; + } } } catch (err) { error = ERROR_MESSAGES.NETWORK_ERROR; diff --git a/src/api/spaces/space.route.js b/src/api/spaces/space.route.js index cbbbbf7..ee81ab2 100644 --- a/src/api/spaces/space.route.js +++ b/src/api/spaces/space.route.js @@ -25,7 +25,7 @@ router.post("/create", async (req, res) => { }); } - const statusCode = err.message.includes("Missing") || err.message.includes("Invalid authorization") ? 400 : 500; + const statusCode = err.statusCode || (err.message.includes("Missing") || err.message.includes("Invalid authorization") ? 400 : 500); res.status(statusCode).json({ error: err.message }); } }); diff --git a/src/api/users/auth.route.js b/src/api/users/auth.route.js index b46fe5d..2691c01 100644 --- a/src/api/users/auth.route.js +++ b/src/api/users/auth.route.js @@ -108,7 +108,8 @@ router.post('/signup', async (req, res) => { .insert({ email, username, - authorization: authToken + authorization: authToken, + max_spaces: 3 }) .returning(['id', 'email', 'username', 'authorization']); diff --git a/src/utils/spaces.js b/src/utils/spaces.js index b5ae8b3..9c0a6e8 100644 --- a/src/utils/spaces.js +++ b/src/utils/spaces.js @@ -1,7 +1,7 @@ import Docker from "dockerode"; import getPort from "get-port"; import pg from "./db.js"; -import { getUser } from "./user.js"; +import { getUser, checkUserSpaceLimit } from "./user.js"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; @@ -53,6 +53,8 @@ export const createContainer = async (password, type, authorization) => { throw new Error("Invalid authorization token"); } + await checkUserSpaceLimit(user.id); + const config = containerConfigs[type.toLowerCase()]; if (!config) { const error = new Error("Invalid container type"); diff --git a/src/utils/user.js b/src/utils/user.js index 10cc58d..f2e9a70 100644 --- a/src/utils/user.js +++ b/src/utils/user.js @@ -13,4 +13,43 @@ export const getUser = async (authorization) => { console.error('Error fetching user:', error); throw error; } +}; + +export const checkUserSpaceLimit = async (userId) => { + if (!userId) { + throw new Error("User ID is required"); + } + + try { + const user = await pg('users') + .where('id', userId) + .first(); + + if (!user) { + throw new Error("User not found"); + } + + const userSpaces = await pg('spaces') + .where('user_id', userId) + .count('id as count') + .first(); + + const currentSpaceCount = parseInt(userSpaces.count) || 0; + const maxSpaces = user.max_spaces ?? 3; + + if (currentSpaceCount >= maxSpaces) { + const error = new Error(`Maximum space limit reached (${maxSpaces} spaces)`); + error.statusCode = 403; + throw error; + } + + return { + currentSpaceCount, + maxSpaces, + canCreateSpace: currentSpaceCount < maxSpaces + }; + } catch (error) { + console.error('Error checking space limit:', error); + throw error; + } }; \ No newline at end of file