"use server"; import type { DeploymentFormInput } from "@/lib/schemas"; import type { Deployment, DeploymentStatus } from "@/lib/types"; import { analyzeDeploymentLogs as analyzeLogsFlow, type AnalyzeDeploymentLogsInput } from "@/ai/flows/analyze-deployment-logs"; import { getDb } from "@/lib/mongodb"; import { ObjectId } from 'mongodb'; import { getLoggedInUser } from "./auth"; import { getPlatformApiKey } from "./admin"; import { revalidatePath } from "next/cache"; const defaultGithubRepoUrl = "https://github.com/DavidCyrilTech/Anita-V4"; const DEPLOYMENT_COST = 10; // GLOBAL_DEPLOYMENT_LIMIT removed const HEROKU_API_BASE_URL = "https://api.heroku.com"; const HEROKU_STACK = "heroku-22"; async function getHerokuApiKey(): Promise { const result = await getPlatformApiKey(); if (result.success && result.apiKey && result.apiKey.trim() !== "") { return result.apiKey; } console.warn("Heroku API Key not found or is empty in database settings via Admin Panel. Deployments will fail."); return null; } async function herokuApiCall( endpoint: string, method: 'GET' | 'POST' | 'PATCH' | 'DELETE', apiKey: string, body?: any ): Promise { const headers = { 'Accept': 'application/vnd.heroku+json; version=3', 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }; const options: RequestInit = { method, headers, }; if (body) { options.body = JSON.stringify(body); } try { const response = await fetch(`${HEROKU_API_BASE_URL}${endpoint}`, options); if (!response.ok) { if (method === 'DELETE' && response.status === 404) { return { status: 404, message: "Resource not found on Heroku (already deleted or never existed)." }; } const errorBody = await response.json().catch(() => ({ message: response.statusText })); console.error(`Heroku API Error (${method} ${endpoint}): ${response.status}`, errorBody); throw new Error(`Heroku API Error: ${errorBody.message || response.statusText} (Status: ${response.status})`); } if (response.status === 204 || response.headers.get("content-length") === "0") { return null; } return response.json(); } catch (error) { console.error(`Network or parsing error during Heroku API call (${method} ${endpoint}):`, error); throw error; } } export async function createNewDeployment(data: DeploymentFormInput): Promise<{ success: boolean; message: string; deployment?: Deployment }> { const user = await getLoggedInUser(); if (!user) { return { success: false, message: "You must be logged in to create a deployment." }; } const herokuApiKey = await getHerokuApiKey(); if (!herokuApiKey) { return { success: false, message: "Platform API Key is not configured. Deployment cannot proceed." }; } try { const db = await getDb(); const deploymentsCollection = db.collection("deployments"); const usersCollection = db.collection("users"); // Removed GLOBAL_DEPLOYMENT_LIMIT check here if (user.role !== 'admin') { const freshUser = await usersCollection.findOne({ _id: user._id }); if (!freshUser || freshUser.coins < DEPLOYMENT_COST) { return { success: false, message: `Insufficient coins. You need ${DEPLOYMENT_COST} coins. You have ${freshUser?.coins || 0}.` }; } } const appNameInput = data.PLATFORM_APP_NAME || `anita-bot-${user.name.toLowerCase().replace(/[^a-z0-9]/g, '').slice(0,8)}-${new ObjectId().toString().slice(-4)}`; const herokuAppName = appNameInput.toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 30); const appNameProvidedByUser = !!data.PLATFORM_APP_NAME; const existingDeployment = await deploymentsCollection.findOne({ appName: herokuAppName }); if (existingDeployment) { return { success: false, message: `A deployment with app name '${herokuAppName}' already exists in our records. Please choose a different name or let one be auto-generated.` }; } const now = new Date(); const initialLogs: string[] = [ `[SYSTEM] Info: ${now.toISOString()} - Deployment creation initiated by user ${user.email} for Heroku app: ${herokuAppName}.`, `[SYSTEM] Info: ${now.toISOString()} - Target GitHub Repo: ${defaultGithubRepoUrl}` ]; let herokuApp; try { initialLogs.push(`[SYSTEM] Info: ${now.toISOString()} - Attempting to create Heroku app '${herokuAppName}'...`); herokuApp = await herokuApiCall('/apps', 'POST', herokuApiKey, { name: herokuAppName, region: 'us', stack: HEROKU_STACK }); initialLogs.push(`[SYSTEM] Success: ${new Date().toISOString()} - Heroku app '${herokuAppName}' (ID: ${herokuApp.id}) created successfully. Web URL: ${herokuApp.web_url}`); } catch (error: any) { if (error.message && error.message.includes("Name") && error.message.includes("is already taken") && error.message.includes("(Status: 422)")) { initialLogs.push(`[SYSTEM] Error: ${new Date().toISOString()} - Heroku app name '${herokuAppName}' is already taken.`); let userMessage = `The app name '${herokuAppName}' is already taken on Heroku. `; if (appNameProvidedByUser) { userMessage += "Please choose a different name for your app and try again."; } else { userMessage += "This name was auto-generated. Please try deploying again, as a new name will be generated."; } return { success: false, message: userMessage }; } initialLogs.push(`[SYSTEM] Error: ${new Date().toISOString()} - Failed to create Heroku app: ${error.message}`); const tempFailDeployment: Omit = { id: herokuAppName, userId: user._id, appName: herokuAppName, status: 'failed', createdAt: now.toISOString(), region: 'us', logs: initialLogs, envVariables: { ...data }, githubRepoUrl: defaultGithubRepoUrl, }; await deploymentsCollection.insertOne(tempFailDeployment); return { success: false, message: `Failed to create Heroku app: ${error.message}` }; } initialLogs.push(`[SYSTEM] Info: ${new Date().toISOString()} - Setting environment variables for '${herokuAppName}'...`); const envVarsToSet = { ...data }; delete envVarsToSet.PLATFORM_APP_NAME; const herokuConfigVars = { ...envVarsToSet, GITHUB_REPO_URL: defaultGithubRepoUrl }; try { await herokuApiCall(`/apps/${herokuApp.id}/config-vars`, 'PATCH', herokuApiKey, herokuConfigVars); initialLogs.push(`[SYSTEM] Success: ${new Date().toISOString()} - Environment variables set successfully.`); } catch (error: any) { initialLogs.push(`[SYSTEM] Error: ${new Date().toISOString()} - Failed to set environment variables: ${error.message}. Cleaning up Heroku app.`); await herokuApiCall(`/apps/${herokuApp.id}`, 'DELETE', herokuApiKey).catch(delErr => console.error("Cleanup error after failing to set config vars:", delErr)); return { success: false, message: `Failed to set Heroku environment variables: ${error.message}` }; } initialLogs.push(`[SYSTEM] Info: ${new Date().toISOString()} - Triggering build from GitHub repository '${defaultGithubRepoUrl}'...`); let build; try { build = await herokuApiCall(`/apps/${herokuApp.id}/builds`, 'POST', herokuApiKey, { source_blob: { url: `${defaultGithubRepoUrl}/tarball/main/`, version: 'main' }, }); initialLogs.push(`[SYSTEM] Success: ${new Date().toISOString()} - Build initiated successfully. Build ID: ${build.id}, Status: ${build.status}.`); } catch (error: any) { initialLogs.push(`[SYSTEM] Error: ${new Date().toISOString()} - Failed to trigger build: ${error.message}. Cleaning up Heroku app.`); await herokuApiCall(`/apps/${herokuApp.id}`, 'DELETE', herokuApiKey).catch(delErr => console.error("Cleanup error after failing to trigger build:", delErr)); return { success: false, message: `Failed to trigger Heroku build: ${error.message}` }; } const newDeploymentData: Omit = { id: herokuApp.id, userId: user._id, appName: herokuApp.name, status: build.status === 'succeeded' ? 'succeeded' : 'deploying', createdAt: now.toISOString(), lastDeployedAt: build.status === 'succeeded' ? new Date(build.updated_at).toISOString() : undefined, region: herokuApp.region.name, url: herokuApp.web_url, logs: initialLogs, envVariables: { ...data }, githubRepoUrl: defaultGithubRepoUrl, }; const result = await deploymentsCollection.insertOne(newDeploymentData); if (!result.insertedId) { initialLogs.push(`[SYSTEM] Error: ${new Date().toISOString()} - Failed to save deployment to local database. Critical error. Heroku app '${herokuApp.name}' might be orphaned.`); await herokuApiCall(`/apps/${herokuApp.id}`, 'DELETE', herokuApiKey).catch(delErr => console.error("Critical: Cleanup error for orphaned Heroku app:", delErr)); return { success: false, message: "Critical: Failed to save deployment to local database after Heroku setup." }; } if (user.role !== 'admin') { await usersCollection.updateOne( { _id: user._id }, { $inc: { coins: -DEPLOYMENT_COST } } ); } revalidatePath("/dashboard"); revalidatePath(`/dashboard/deployments/${newDeploymentData.id}`); return { success: true, message: `Heroku deployment for '${newDeploymentData.appName}' initiated! ${user.role !== 'admin' ? `${DEPLOYMENT_COST} coins deducted.` : ''} Build status: ${build.status}. Check deployment details for updates.`, deployment: { ...newDeploymentData, _id: result.insertedId.toString() } }; } catch (error: any) { console.error("Error creating new Heroku deployment:", error); const message = error.message || "An unexpected error occurred during Heroku deployment."; return { success: false, message }; } } export async function getDeployments(): Promise { const user = await getLoggedInUser(); if (!user) return []; try { const db = await getDb(); const query = user.role === 'admin' ? {} : { userId: user._id }; const deployments = await db.collection("deployments") .find(query) .sort({ createdAt: -1 }) .toArray(); return deployments.map(d => ({ ...d, _id: d._id?.toString() })); } catch (error) { console.error("Error fetching deployments:", error); return []; } } export async function getDeploymentById(id: string): Promise { const user = await getLoggedInUser(); if (!user) return null; try { const db = await getDb(); const deployment = await db.collection("deployments").findOne({ id: id }); if (!deployment) return null; if (deployment.userId !== user._id && user.role !== 'admin') return null; if (deployment.status === 'deploying' || deployment.status === 'pending') { const herokuApiKey = await getHerokuApiKey(); if (herokuApiKey) { try { const formation = await herokuApiCall(`/apps/${deployment.id}/formation/web`, 'GET', herokuApiKey); let appStatusBasedOnDynos: DeploymentStatus = deployment.status; if (formation && formation.quantity > 0) { if(deployment.status === 'deploying') appStatusBasedOnDynos = 'succeeded'; } else if (formation && formation.quantity === 0 && deployment.status !== 'stopped') { appStatusBasedOnDynos = 'stopped'; } const builds = await herokuApiCall(`/apps/${deployment.id}/builds`, 'GET', herokuApiKey); let newStatusFromBuild = deployment.status; let lastDeployedAtFromBuild = deployment.lastDeployedAt; if (builds && builds.length > 0) { const latestBuild = builds.sort((a:any, b:any) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0]; newStatusFromBuild = latestBuild.status === 'succeeded' ? 'succeeded' : latestBuild.status === 'failed' ? 'failed' : 'deploying'; lastDeployedAtFromBuild = new Date(latestBuild.updated_at).toISOString(); } let finalNewStatus = deployment.status; if (newStatusFromBuild === 'succeeded' || newStatusFromBuild === 'failed') { finalNewStatus = newStatusFromBuild; } else if (appStatusBasedOnDynos !== deployment.status) { finalNewStatus = appStatusBasedOnDynos; } if (finalNewStatus !== deployment.status || (finalNewStatus === 'succeeded' && lastDeployedAtFromBuild !== deployment.lastDeployedAt)) { deployment.status = finalNewStatus; deployment.lastDeployedAt = finalNewStatus === 'succeeded' ? lastDeployedAtFromBuild : deployment.lastDeployedAt; const statusUpdateLog = `[SYSTEM] Info: ${new Date().toISOString()} - Heroku status sync. App status: ${finalNewStatus}. Latest build status: ${newStatusFromBuild}.`; const updatedLogs = [...(deployment.logs || []), statusUpdateLog].slice(-500); await db.collection("deployments").updateOne( { id: deployment.id }, { $set: { status: finalNewStatus, lastDeployedAt: deployment.lastDeployedAt, logs: updatedLogs } } ); deployment.logs = updatedLogs; revalidatePath(`/dashboard/deployments/${deployment.id}`); } } catch (herokuError: any) { const errorLog = `[SYSTEM] Warning: ${new Date().toISOString()} - Could not fetch live status from Heroku: ${herokuError.message}`; const updatedLogs = [...(deployment.logs || []), errorLog].slice(-500); await db.collection("deployments").updateOne( { id: deployment.id }, { $set: { logs: updatedLogs } } ); deployment.logs = updatedLogs; } } } return { ...deployment, _id: deployment._id?.toString() }; } catch (error) { console.error(`Error fetching deployment by ID '${id}':`, error); return null; } } export async function getDeploymentLogs(deploymentId: string): Promise { const user = await getLoggedInUser(); if (!user) return [`[SYSTEM] Error: Unauthorized to fetch logs.`]; const herokuApiKey = await getHerokuApiKey(); if (!herokuApiKey) return [`[SYSTEM] Error: Heroku API Key not configured. Cannot fetch live logs.`]; const db = await getDb(); const deploymentsCollection = db.collection("deployments"); const deployment = await deploymentsCollection.findOne({ id: deploymentId }); if (!deployment) return [`[SYSTEM] Info: No deployment found with ID ${deploymentId}.`]; if (deployment.userId !== user._id && user.role !== 'admin') { return [`[SYSTEM] Error: Unauthorized to fetch logs for this deployment.`]; } const timestamp = new Date().toISOString(); let fetchedHerokuLogs: string[] = []; try { const logSession = await herokuApiCall( `/apps/${deployment.id}/log-sessions`, 'POST', herokuApiKey, { lines: 200, source: 'app', tail: false } ); if (logSession && logSession.logplex_url) { const logStreamResponse = await fetch(logSession.logplex_url); if (logStreamResponse.ok) { const rawLogs = await logStreamResponse.text(); fetchedHerokuLogs = rawLogs.split('\\n').filter(line => line.trim() !== ''); if (fetchedHerokuLogs.length > 0) { fetchedHerokuLogs.unshift(`[SYSTEM] Info: ${timestamp} - Successfully fetched ${fetchedHerokuLogs.length} recent log lines from Heroku.`); } else { fetchedHerokuLogs.push(`[SYSTEM] Info: ${timestamp} - No recent logs returned from Heroku for this app.`); } const currentDbLogs = deployment.logs || []; const combinedLogs = [...currentDbLogs, ...fetchedHerokuLogs].slice(-500); await deploymentsCollection.updateOne( { id: deployment.id }, { $set: { logs: combinedLogs } } ); revalidatePath(`/dashboard/deployments/${deployment.id}`); return combinedLogs; } else { const errorMsg = `[SYSTEM] Error: ${timestamp} - Failed to fetch logs from Heroku logplex_url. Status: ${logStreamResponse.status}`; fetchedHerokuLogs = [errorMsg]; } } else { const errorMsg = `[SYSTEM] Error: ${timestamp} - Failed to create Heroku log session. Response: ${JSON.stringify(logSession)}`; fetchedHerokuLogs = [errorMsg]; } } catch (error: any) { console.error(`Error fetching live logs from Heroku for '${deployment.appName}':`, error); const errorMsg = `[SYSTEM] Error: ${timestamp} - Exception during Heroku log fetch: ${error.message}`; fetchedHerokuLogs = [errorMsg]; } const currentDbLogs = deployment.logs || []; const logsToStore = [...currentDbLogs, ...fetchedHerokuLogs].slice(-500); await deploymentsCollection.updateOne( { id: deployment.id }, { $set: { logs: logsToStore } } ); revalidatePath(`/dashboard/deployments/${deployment.id}`); return logsToStore; } export async function controlDeployment(deploymentId: string, action: "start" | "stop" | "restart"): Promise<{ success: boolean; message: string; newStatus?: DeploymentStatus }> { const user = await getLoggedInUser(); if (!user) return { success: false, message: "Unauthorized." }; const herokuApiKey = await getHerokuApiKey(); if (!herokuApiKey) return { success: false, message: "Heroku API Key is not configured." }; const db = await getDb(); const deploymentsCollection = db.collection("deployments"); const deployment = await deploymentsCollection.findOne({ id: deploymentId }); if (!deployment) return { success: false, message: "Deployment not found." }; if (deployment.userId !== user._id && user.role !== 'admin') { return { success: false, message: "Unauthorized to control this deployment." }; } const herokuAppIdOrName = deployment.id; let newDbStatus: DeploymentStatus = deployment.status; const timestamp = new Date().toISOString(); let logMessage = `[SYSTEM] Info: ${timestamp} - User ${user.email} requested Heroku action '${action}' on app '${deployment.appName}'.`; let herokuSuccess = false; try { switch (action) { case "start": logMessage += ` Attempting to scale web dynos to 1.`; await herokuApiCall(`/apps/${herokuAppIdOrName}/formation/web`, 'PATCH', herokuApiKey, { quantity: 1 }); newDbStatus = 'deploying'; logMessage += ` Heroku accepted scale command. App is starting. Status set to 'deploying'.`; herokuSuccess = true; break; case "stop": logMessage += ` Attempting to scale web dynos to 0.`; await herokuApiCall(`/apps/${herokuAppIdOrName}/formation/web`, 'PATCH', herokuApiKey, { quantity: 0 }); newDbStatus = 'stopped'; logMessage += ` Heroku accepted scale command. App should be stopping. Status set to 'stopped'.`; herokuSuccess = true; break; case "restart": logMessage += ` Attempting to restart all dynos.`; await herokuApiCall(`/apps/${herokuAppIdOrName}/dynos`, 'DELETE', herokuApiKey); newDbStatus = 'deploying'; logMessage += ` Heroku accepted restart command. App is restarting. Status set to 'deploying'.`; herokuSuccess = true; break; } if (herokuSuccess) { const updatedLogs = [...(deployment.logs || []), logMessage].slice(-500); await deploymentsCollection.updateOne( { id: deploymentId }, { $set: { status: newDbStatus, logs: updatedLogs, lastDeployedAt: new Date().toISOString() } } ); revalidatePath(`/dashboard/deployments/${deploymentId}`); return { success: true, message: `Heroku action '${action}' on '${deployment.appName}' initiated. New status: ${newDbStatus}.`, newStatus: newDbStatus }; } else { logMessage += ` Failed to execute Heroku command for '${action}'.`; const updatedLogs = [...(deployment.logs || []), logMessage].slice(-500); await deploymentsCollection.updateOne({ id: deploymentId }, { $set: {logs: updatedLogs }}); return { success: false, message: `Failed to execute Heroku command for '${action}'.` }; } } catch (error: any) { logMessage += ` Error during Heroku action '${action}': ${error.message}.`; const updatedLogs = [...(deployment.logs || []), logMessage].slice(-500); const currentDeploymentRecord = await deploymentsCollection.findOne({ id: deploymentId }); if (currentDeploymentRecord) { await deploymentsCollection.updateOne({ id: deploymentId }, { $set: { logs: updatedLogs } }); } console.error(`Error controlling Heroku deployment '${deployment.appName}':`, error); return { success: false, message: `Heroku API operation failed: ${error.message}` }; } } export async function deleteDeployment(deploymentId: string): Promise<{ success: boolean; message: string }> { const user = await getLoggedInUser(); if (!user) return { success: false, message: "Unauthorized. Please log in." }; const herokuApiKey = await getHerokuApiKey(); if (!herokuApiKey) return { success: false, message: "Heroku API Key is not configured. Cannot delete deployment." }; const db = await getDb(); const deploymentsCollection = db.collection("deployments"); const deployment = await deploymentsCollection.findOne({ id: deploymentId }); if (!deployment) return { success: false, message: "Deployment not found in our records." }; if (deployment.userId !== user._id && user.role !== 'admin') { return { success: false, message: "Unauthorized. You can only delete your own deployments." }; } const timestamp = new Date().toISOString(); const initialDbLogs = deployment.logs || []; let logMessage = `[SYSTEM] Info: ${timestamp} - User ${user.email} initiated deletion for deployment '${deployment.appName}' (ID: ${deployment.id}).`; let herokuAppDeleted = false; try { logMessage += ` Attempting to delete Heroku app '${deployment.appName}'.`; await herokuApiCall(`/apps/${deployment.id}`, 'DELETE', herokuApiKey); logMessage += ` Heroku app '${deployment.appName}' delete command accepted by Heroku.`; herokuAppDeleted = true; } catch (error: any) { if (error.message && error.message.includes("(Status: 404)") || (typeof error === 'object' && error !== null && 'status' in error && error.status === 404)) { logMessage += ` Heroku app '${deployment.appName}' not found on Heroku (Status 404). Assuming already deleted or never fully created. Proceeding with local cleanup.`; herokuAppDeleted = true; } else { logMessage += ` Error deleting Heroku app '${deployment.appName}': ${error.message}. Local record will NOT be deleted.`; const updatedLogs = [...initialDbLogs, logMessage].slice(-500); const currentDeploymentRecord = await deploymentsCollection.findOne({ id: deploymentId }); if (currentDeploymentRecord) { await deploymentsCollection.updateOne({ id: deploymentId }, { $set: { logs: updatedLogs } }); revalidatePath(`/dashboard/deployments/${deploymentId}`); } return { success: false, message: `Failed to delete Heroku app: ${error.message}.` }; } } if (herokuAppDeleted) { try { const deleteResult = await deploymentsCollection.deleteOne({ id: deployment.id }); if (deleteResult.deletedCount === 0) { logMessage += ` Warning: Heroku app was targeted for deletion, but local record for '${deployment.appName}' not found or already deleted.`; console.warn(logMessage); } else { logMessage += ` Local database record for deployment '${deployment.appName}' deleted successfully.`; } revalidatePath("/dashboard"); revalidatePath(`/dashboard/deployments`); return { success: true, message: `Deployment '${deployment.appName}' and associated Heroku app have been successfully deleted (or confirmed deleted).` }; } catch (dbError: any) { logMessage += ` Critical Error: Heroku app '${deployment.appName}' was deleted (or confirmed deleted), but failed to delete local database record: ${dbError.message}. Manual cleanup may be required.`; console.error(logMessage); return { success: false, message: `Heroku app deleted, but failed to clean up local record: ${dbError.message}` }; } } return { success: false, message: "An unknown error occurred during the deletion process." }; } export async function analyzeDeploymentLogs(logs: string): Promise<{ success: boolean; analysis?: string; error?: string }> { if (!logs.trim()) { return { success: false, error: "Log content cannot be empty." }; } try { const input: AnalyzeDeploymentLogsInput = { deploymentLogs: logs }; const result = await analyzeLogsFlow(input); return { success: true, analysis: result.analysisResult }; } catch (error) { console.error("AI Log Analysis Error:", error); return { success: false, error: "Failed to analyze logs. Please try again." }; } } export async function updateDeploymentEnvVariables( deploymentId: string, newEnvVars: DeploymentFormInput ): Promise<{ success: boolean; message: string }> { const user = await getLoggedInUser(); if (!user) { return { success: false, message: "Unauthorized. Please log in." }; } const herokuApiKey = await getHerokuApiKey(); if (!herokuApiKey) { return { success: false, message: "Heroku API Key is not configured." }; } const db = await getDb(); const deploymentsCollection = db.collection("deployments"); const deployment = await deploymentsCollection.findOne({ id: deploymentId }); if (!deployment) { return { success: false, message: "Deployment not found." }; } if (deployment.userId !== user._id && user.role !== 'admin') { return { success: false, message: "Unauthorized to modify this deployment." }; } const timestamp = new Date().toISOString(); let logMessage = `[SYSTEM] Info: ${timestamp} - User ${user.email} initiated environment variable update for '${deployment.appName}'.`; try { const herokuConfigVars = { ...newEnvVars }; if ('PLATFORM_APP_NAME' in herokuConfigVars) { delete (herokuConfigVars as any).PLATFORM_APP_NAME; } if (!(herokuConfigVars as any).GITHUB_REPO_URL) { (herokuConfigVars as any).GITHUB_REPO_URL = deployment.githubRepoUrl || defaultGithubRepoUrl; } logMessage += ` Attempting to update config vars on Heroku.`; await herokuApiCall(`/apps/${deployment.id}/config-vars`, 'PATCH', herokuApiKey, herokuConfigVars); logMessage += ` Heroku config vars updated successfully.`; logMessage += ` Attempting to restart Heroku app '${deployment.appName}' for changes to take effect.`; await herokuApiCall(`/apps/${deployment.id}/dynos`, 'DELETE', herokuApiKey); logMessage += ` Heroku app restart command accepted. Status set to 'deploying'.`; const updatedLogs = [...(deployment.logs || []), logMessage].slice(-500); await deploymentsCollection.updateOne( { id: deployment.id }, { $set: { envVariables: newEnvVars, logs: updatedLogs, status: 'deploying', lastDeployedAt: new Date().toISOString() } } ); revalidatePath(`/dashboard/deployments/${deployment.id}`); return { success: true, message: "Environment variables updated and app restart initiated. Status is 'deploying'." }; } catch (error: any) { logMessage += ` Error during environment variable update or restart: ${error.message}.`; const updatedLogs = [...(deployment.logs || []), logMessage].slice(-500); const currentDeploymentRecord = await deploymentsCollection.findOne({ id: deploymentId }); if (currentDeploymentRecord) { await deploymentsCollection.updateOne({ id: deployment.id }, { $set: { logs: updatedLogs } }); } console.error(`Error updating env variables for '${deployment.appName}':`, error); return { success: false, message: `Failed to update environment variables: ${error.message}` }; } }