import { toast } from 'react-toastify'; import ReactModal from 'react-modal'; import { useState } from 'react'; import type { DeploySettingsDatabase } from '~/lib/replay/Deploy'; import { generateRandomId } from '~/lib/replay/ReplayProtocolClient'; import { workbenchStore } from '~/lib/stores/workbench'; import { chatStore } from '~/lib/stores/chat'; import { database } from '~/lib/persistence/db'; import { deployRepository } from '~/lib/replay/Deploy'; ReactModal.setAppElement('#root'); // Component for deploying a chat to production. enum DeployStatus { NotStarted, Started, Succeeded, } export function DeployChatButton() { const [isModalOpen, setIsModalOpen] = useState(false); const [deploySettings, setDeploySettings] = useState(null); const [error, setError] = useState(null); const [status, setStatus] = useState(DeployStatus.NotStarted); const handleOpenModal = async () => { const chatId = chatStore.currentChat.get()?.id; if (!chatId) { toast.error('No chat open'); return; } const existingSettings = await database.getChatDeploySettings(chatId); setIsModalOpen(true); setStatus(DeployStatus.NotStarted); if (existingSettings) { setDeploySettings(existingSettings); } else { setDeploySettings({}); } }; const handleDeploy = async () => { setError(null); const chatId = chatStore.currentChat.get()?.id; if (!chatId) { setError('No chat open'); return; } if (!deploySettings?.netlify?.authToken) { setError('Netlify Auth Token is required'); return; } if (deploySettings?.netlify?.siteId) { if (deploySettings.netlify.createInfo) { setError('Cannot specify both a Netlify Site ID and a Netlify Account Slug'); return; } } else if (!deploySettings?.netlify?.createInfo) { setError('Either a Netlify Site ID or a Netlify Account Slug is required'); return; } else { // Add a default site name if one isn't provided. if (!deploySettings.netlify.createInfo?.siteName) { deploySettings.netlify.createInfo.siteName = `nut-app-${generateRandomId()}`; } } if ( deploySettings?.supabase?.databaseURL || deploySettings?.supabase?.anonKey || deploySettings?.supabase?.postgresURL ) { if (!deploySettings.supabase.databaseURL) { setError('Supabase Database URL is required'); return; } if (!deploySettings.supabase.anonKey) { setError('Supabase Anonymous Key is required'); return; } if (!deploySettings.supabase.postgresURL) { setError('Supabase Postgres URL is required'); return; } } const repositoryId = workbenchStore.repositoryId.get(); if (!repositoryId) { setError('No repository ID found'); return; } setStatus(DeployStatus.Started); // Write out to the database before we start trying to deploy. await database.updateChatDeploySettings(chatId, deploySettings); console.log('DeploymentStarting', repositoryId, deploySettings); const result = await deployRepository(repositoryId, deploySettings); console.log('DeploymentResult', repositoryId, deploySettings, result); if (result.error) { setStatus(DeployStatus.NotStarted); setError(result.error); return; } let newSettings = deploySettings; // Update netlify settings so future deployments will reuse the site. if (deploySettings?.netlify?.createInfo && result.netlifySiteId) { newSettings = { ...deploySettings, netlify: { authToken: deploySettings.netlify.authToken, siteId: result.netlifySiteId }, }; } // Update database with the deployment result. newSettings = { ...newSettings, siteURL: result.siteURL, repositoryId, }; setDeploySettings(newSettings); setStatus(DeployStatus.Succeeded); // Update the database with the new settings. await database.updateChatDeploySettings(chatId, newSettings); }; return ( <> {isModalOpen && (
{status === DeployStatus.Succeeded ? ( <>
Deployment Succeeded
) : ( <>

Deploy

Deploy this chat's app to production.
{deploySettings?.siteURL && ( )}
{ const netlify = { authToken: e.target.value, siteId: deploySettings?.netlify?.siteId || '', createInfo: deploySettings?.netlify?.createInfo || undefined, }; setDeploySettings({ ...deploySettings, netlify, }); }} /> { const netlify = { authToken: deploySettings?.netlify?.authToken || '', siteId: e.target.value, createInfo: deploySettings?.netlify?.createInfo || undefined, }; setDeploySettings({ ...deploySettings, netlify, }); }} /> { const createInfo = { accountSlug: e.target.value, siteName: deploySettings?.netlify?.createInfo?.siteName || '', }; const netlify = { authToken: deploySettings?.netlify?.authToken || '', siteId: deploySettings?.netlify?.siteId || '', createInfo, }; setDeploySettings({ ...deploySettings, netlify, }); }} /> { const createInfo = { accountSlug: deploySettings?.netlify?.createInfo?.accountSlug || '', siteName: e.target.value, }; const netlify = { authToken: deploySettings?.netlify?.authToken || '', siteId: deploySettings?.netlify?.siteId || '', createInfo, }; setDeploySettings({ ...deploySettings, netlify, }); }} /> { const supabase = { databaseURL: e.target.value, anonKey: deploySettings?.supabase?.anonKey || '', postgresURL: deploySettings?.supabase?.postgresURL || '', }; setDeploySettings({ ...deploySettings, supabase, }); }} /> { const supabase = { databaseURL: deploySettings?.supabase?.databaseURL || '', anonKey: e.target.value, postgresURL: deploySettings?.supabase?.postgresURL || '', }; setDeploySettings({ ...deploySettings, supabase, }); }} /> { const supabase = { databaseURL: deploySettings?.supabase?.databaseURL || '', anonKey: deploySettings?.supabase?.anonKey || '', postgresURL: e.target.value, }; setDeploySettings({ ...deploySettings, supabase, }); }} />
{status === DeployStatus.Started && (
)} {status === DeployStatus.NotStarted && ( )}
{error &&
{error}
} )}
)} ); }