@@ -12,11 +12,12 @@ import { Checkbox } from "@/components/ui/checkbox"
1212import { Badge } from "@/components/ui/badge"
1313import { Alert , AlertDescription } from "@/components/ui/alert"
1414import { Slider } from "@/components/ui/slider"
15- import { Rocket , CheckCircle , HardDrive , Cpu , MapPin } from "lucide-react"
15+ import { Rocket , CheckCircle , HardDrive , Cpu , MapPin , Loader2 } from "lucide-react"
1616import { useToast } from "@/hooks/use-toast"
1717import { Textarea } from "@/components/ui/textarea"
1818import { DockerImageSearch } from "@/components/search/docker-image-search"
1919import { GitHubRepoSearch } from "@/components/search/github-repo-search"
20+ import { storeSSHKey } from "@/lib/ssh-key-utils"
2021
2122const INSTANCE_TYPES = [
2223 { value : "t3.micro" , label : "t3.micro" , vcpu : 2 , ram : 1 , cost : 7.59 } ,
@@ -54,6 +55,7 @@ const DEV_TOOLS = ["git", "docker", "nodejs", "python3", "nginx"]
5455
5556export function CreateManager ( { credentials, onSuccess } : { credentials : any ; onSuccess : ( ) => void } ) {
5657 const { toast } = useToast ( )
58+ const [ isLaunching , setIsLaunching ] = useState ( false )
5759 const [ formData , setFormData ] = useState ( {
5860 instanceName : "" ,
5961 region : credentials . region || "us-east-1" ,
@@ -71,36 +73,116 @@ export function CreateManager({ credentials, onSuccess }: { credentials: any; on
7173 dokployApiKey : "" ,
7274 } )
7375
74- const handleSubmit = ( e : React . FormEvent ) => {
76+ const handleSubmit = async ( e : React . FormEvent ) => {
7577 e . preventDefault ( )
7678
79+ setIsLaunching ( true )
80+
7781 const sanitizedKeyName = formData . keyName && formData . keyName . trim ( ) !== "" ? formData . keyName : ""
7882
83+ const sanitizedConfig = {
84+ ...formData ,
85+ keyName : sanitizedKeyName ,
86+ createdAt : new Date ( ) . toISOString ( ) ,
87+ }
88+
7989 const newManager = {
8090 managerId : `mgr-${ Date . now ( ) } ` ,
81- config : {
82- ...formData ,
83- keyName : sanitizedKeyName ,
84- createdAt : new Date ( ) . toISOString ( ) ,
85- } ,
91+ config : sanitizedConfig ,
8692 status : {
87- state : "not-launched " ,
93+ state : "launching " ,
8894 instanceId : null ,
8995 } ,
9096 costEstimate : calculateCost ( ) ,
9197 }
9298
99+ // Save the manager config to localStorage first
93100 const existing = localStorage . getItem ( "ec2Managers" )
94101 const managers = existing ? JSON . parse ( existing ) : [ ]
95102 managers . push ( newManager )
96103 localStorage . setItem ( "ec2Managers" , JSON . stringify ( managers ) )
97104
98- toast ( {
99- title : "Manager Created" ,
100- description : `${ formData . instanceName } is ready to launch` ,
101- } )
105+ try {
106+ // Launch the instance immediately
107+ const response = await fetch ( "/api/servers/create" , {
108+ method : "POST" ,
109+ headers : { "Content-Type" : "application/json" } ,
110+ body : JSON . stringify ( {
111+ accessKeyId : credentials . accessKeyId ,
112+ secretAccessKey : credentials . secretAccessKey ,
113+ region : sanitizedConfig . region || credentials . region ,
114+ config : sanitizedConfig ,
115+ } ) ,
116+ } )
117+
118+ if ( ! response . ok ) {
119+ const error = await response . json ( )
120+ throw new Error ( error . message || `HTTP ${ response . status } ` )
121+ }
102122
103- onSuccess ( )
123+ const result = await response . json ( )
124+
125+ // Store SSH key if provided
126+ if ( result . sshKey ) {
127+ storeSSHKey ( result . sshKey . keyName , {
128+ privateKey : result . sshKey . privateKey ,
129+ publicKey : result . sshKey . publicKey ,
130+ fingerprint : result . sshKey . fingerprint ,
131+ } )
132+ }
133+
134+ // Update the manager with the launched instance details
135+ const updatedManagers = managers . map ( ( mgr : any ) =>
136+ mgr . managerId === newManager . managerId
137+ ? {
138+ ...mgr ,
139+ config : {
140+ ...sanitizedConfig ,
141+ keyName : result . sshKey ?. keyName || sanitizedConfig . keyName ,
142+ } ,
143+ status : {
144+ state : "pending" ,
145+ instanceId : result . instanceId ,
146+ publicIp : result . elasticIp ,
147+ allocationId : result . allocationId ,
148+ } ,
149+ }
150+ : mgr
151+ )
152+ localStorage . setItem ( "ec2Managers" , JSON . stringify ( updatedManagers ) )
153+
154+ toast ( {
155+ title : "Instance Launched Successfully!" ,
156+ description : `${ formData . instanceName } is now starting with IP ${ result . elasticIp || "pending" } ` ,
157+ } )
158+
159+ // Only call onSuccess after successful launch
160+ onSuccess ( )
161+ } catch ( error ) {
162+ console . error ( "Launch error:" , error )
163+
164+ // Update manager status to failed
165+ const updatedManagers = managers . map ( ( mgr : any ) =>
166+ mgr . managerId === newManager . managerId
167+ ? {
168+ ...mgr ,
169+ status : {
170+ state : "launch-failed" ,
171+ instanceId : null ,
172+ } ,
173+ }
174+ : mgr
175+ )
176+ localStorage . setItem ( "ec2Managers" , JSON . stringify ( updatedManagers ) )
177+
178+ toast ( {
179+ title : "Launch Failed" ,
180+ description : error instanceof Error ? error . message : "Unknown error" ,
181+ variant : "destructive" ,
182+ } )
183+ } finally {
184+ setIsLaunching ( false )
185+ }
104186 }
105187
106188 const calculateCost = ( ) => {
@@ -187,6 +269,16 @@ export function CreateManager({ credentials, onSuccess }: { credentials: any; on
187269
188270 return (
189271 < form onSubmit = { handleSubmit } >
272+ { isLaunching && (
273+ < Alert className = "mb-6 border-blue-500 bg-blue-50 dark:bg-blue-950/20" >
274+ < Loader2 className = "h-4 w-4 animate-spin text-blue-600" />
275+ < AlertDescription className = "text-blue-900 dark:text-blue-100" >
276+ < strong > Launching your instance...</ strong >
277+ < br />
278+ This may take a few minutes. Please wait while we set up your EC2 instance with Dokploy.
279+ </ AlertDescription >
280+ </ Alert >
281+ ) }
190282 < div className = "grid gap-6 md:grid-cols-2" >
191283 { /* Basic Configuration */ }
192284 < Card >
@@ -311,9 +403,18 @@ export function CreateManager({ credentials, onSuccess }: { credentials: any; on
311403 </ div >
312404 </ CardContent >
313405 < CardFooter className = "border-t pt-6" >
314- < Button type = "submit" size = "lg" className = "w-full" >
315- < Rocket className = "h-4 w-4 mr-2" />
316- Create Manager
406+ < Button type = "submit" size = "lg" className = "w-full" disabled = { isLaunching } >
407+ { isLaunching ? (
408+ < >
409+ < Loader2 className = "h-4 w-4 mr-2 animate-spin" />
410+ Launching Instance...
411+ </ >
412+ ) : (
413+ < >
414+ < Rocket className = "h-4 w-4 mr-2" />
415+ Launch Instance
416+ </ >
417+ ) }
317418 </ Button >
318419 </ CardFooter >
319420 </ Card >
0 commit comments