diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bfdd39..25e6755 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,14 +38,14 @@ jobs: - name: Install client dependencies run: | cd client - npm ci + npm i env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Install dependencies - run: npm ci + run: npm i env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run build - run: npm run build \ No newline at end of file + run: npm run build diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index a4731b2..712fb81 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -33,14 +33,10 @@ jobs: cd /home/modl/modl-admin git stash git pull origin dev - if git diff --name-only HEAD@{1} HEAD | grep -q "^package.*\.json$"; then - npm ci - fi - if git diff --name-only HEAD@{1} HEAD | grep -q "^client/package.*\.json$"; then - cd client - npm ci - cd .. - fi + npm i + cd client + npm i + cd .. npm run build pm2 reload modl-admin --wait-ready if pm2 describe modl-admin | grep -q "online"; then diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index abddfdc..c7be6b5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,14 +33,10 @@ jobs: cd /home/modl/modl-admin git stash git pull origin main - if git diff --name-only HEAD@{1} HEAD | grep -q "^package.*\.json$"; then - npm ci - fi - if git diff --name-only HEAD@{1} HEAD | grep -q "^client/package.*\.json$"; then - cd client - npm ci - cd .. - fi + npm i + cd client + npm i + cd .. npm run build pm2 reload modl-admin --wait-ready if pm2 describe modl-admin | grep -q "online"; then diff --git a/package.json b/package.json index d16fdfd..d99fa16 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "author": "modl-gg", "license": "AGPL-3.0-only", "dependencies": { - "@modl-gg/shared-web": "^1.0.0", + "@modl-gg/shared-web": "1.0.1", "@tanstack/react-query-devtools": "^5.81.2", "@vitejs/plugin-react": "^4.6.0", "compression": "^1.7.4", diff --git a/server/db/connectionManager.ts b/server/db/connectionManager.ts new file mode 100644 index 0000000..4100262 --- /dev/null +++ b/server/db/connectionManager.ts @@ -0,0 +1,60 @@ +import mongoose, { Connection } from 'mongoose'; + +let globalConnection: Connection | null = null; + +/** + * Get or create a connection to the global MongoDB database + * This function returns the existing mongoose connection or creates a new one + */ +export async function connectToGlobalModlDb(): Promise { + try { + // If we already have a connection and it's ready, return it + if (globalConnection && globalConnection.readyState === 1) { + return globalConnection; + } + + // If mongoose is already connected, use the existing connection + if (mongoose.connection.readyState === 1) { + globalConnection = mongoose.connection; + return globalConnection; + } + + // If not connected, establish a new connection + const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/modl-global'; + + // Set connection options for better timeout handling + const connectionOptions = { + serverSelectionTimeoutMS: 30000, // 30 seconds + socketTimeoutMS: 30000, // 30 seconds + bufferMaxEntries: 0, + bufferCommands: false, + }; + + await mongoose.connect(mongoUri, connectionOptions); + globalConnection = mongoose.connection; + + console.log('✅ Global MongoDB connection established via connectionManager'); + return globalConnection; + } catch (error) { + console.error('❌ Failed to connect to global MongoDB:', error); + throw error; + } +} + +/** + * Close the global connection + */ +export async function closeGlobalConnection(): Promise { + if (globalConnection) { + await globalConnection.close(); + globalConnection = null; + console.log('🔒 Global MongoDB connection closed'); + } +} + +/** + * Get the current connection status + */ +export function getConnectionStatus(): number { + return globalConnection?.readyState ?? mongoose.connection.readyState; +} \ No newline at end of file diff --git a/server/routes/servers.ts b/server/routes/servers.ts index 4ea7502..b78b8fe 100644 --- a/server/routes/servers.ts +++ b/server/routes/servers.ts @@ -3,6 +3,7 @@ import mongoose, { Schema, model, Document, Model } from 'mongoose'; import { IModlServer as IModlServerShared, ApiResponse, ModlServerSchema } from '@modl-gg/shared-web'; import { requireAuth } from '../middleware/authMiddleware'; import { discordWebhookService } from '../services/DiscordWebhookService'; +import { connectToGlobalModlDb } from '../db/connectionManager'; type IModlServer = IModlServerShared & Document; @@ -165,8 +166,9 @@ router.get('/:id/stats', async (req: Request, res: Response) => { }); } - // Connect to the specific server's database - const serverDb = mongoose.connection.useDb(server.databaseName, { useCache: true }); + // Connect to the specific server's database using connection manager for proper timeout settings + const globalConnection = await connectToGlobalModlDb(); + const serverDb = globalConnection.useDb(server.databaseName, { useCache: true }); // Fetch stats from the server's database const [ @@ -460,18 +462,21 @@ router.post('/:id/reset-database', async (req: Request, res: Response) => { // Only drop the database if it exists and is configured if (server.databaseName) { try { - // Check if main connection is ready before attempting database operations - if (mongoose.connection.readyState !== 1) { - console.warn(`MongoDB connection not ready (state: ${mongoose.connection.readyState}). Skipping database drop for ${server.databaseName}`); + // Use connection manager to get a connection with proper timeout settings + const globalConnection = await connectToGlobalModlDb(); + + if (globalConnection.readyState !== 1) { + console.warn(`Global MongoDB connection not ready (state: ${globalConnection.readyState}). Skipping database drop for ${server.databaseName}`); } else { - const serverDb = mongoose.connection.useDb(server.databaseName, { useCache: true }); - - // Add timeout to prevent hanging + // Use the global connection to access the specific server database + const serverDb = globalConnection.useDb(server.databaseName, { useCache: false }); + + // Add timeout to prevent hanging - increased to 30 seconds to match connection timeout settings const dropPromise = serverDb.dropDatabase(); - const timeoutPromise = new Promise((_, reject) => - setTimeout(() => reject(new Error('Database drop operation timed out after 5 seconds')), 5000) + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Database drop operation timed out after 30 seconds')), 30000) ); - + await Promise.race([dropPromise, timeoutPromise]); console.log(`Database ${server.databaseName} dropped for server ${server.serverName}`); }