|
| 1 | +importinquirerfrom"inquirer"; |
| 2 | +importgotfrom"got"; |
| 3 | +importorafrom"ora"; |
| 4 | +importchalkfrom"chalk"; |
| 5 | +import{ |
| 6 | +createDroplet, |
| 7 | +Droplet, |
| 8 | +DropletV4Network, |
| 9 | +getDroplet, |
| 10 | +}from"../lib/digitalOcean"; |
| 11 | +importwaitUntilfrom"async-wait-until"; |
| 12 | + |
| 13 | +constgetUserDataScript=async()=> |
| 14 | +got( |
| 15 | +"https://raw.githubusercontent.com/cdr/deploy-code-server/main/deploy-vm/launch-code-server.sh" |
| 16 | +).text(); |
| 17 | + |
| 18 | +constisPermissionError=(error:unknown)=>{ |
| 19 | +returnerrorinstanceofgot.HTTPError&&error.response.statusCode===401; |
| 20 | +}; |
| 21 | + |
| 22 | +constgetPublicIp=(droplet:Droplet)=>{ |
| 23 | +constnetwork=droplet.networks.v4.find( |
| 24 | +(network)=>network.type==="public" |
| 25 | +); |
| 26 | +returnnetwork?.ip_address; |
| 27 | +}; |
| 28 | + |
| 29 | +constisCodeServerLive=async(droplet:Droplet)=>{ |
| 30 | +try{ |
| 31 | +constresponse=awaitgot(`http://${getPublicIp(droplet)}`,{retry:0}); |
| 32 | +returnresponse.statusCode===200; |
| 33 | +}catch{ |
| 34 | +returnfalse; |
| 35 | +} |
| 36 | +}; |
| 37 | + |
| 38 | +consthandleErrorLog=(error:unknown)=>{ |
| 39 | +if(isPermissionError(error)){ |
| 40 | +console.log( |
| 41 | +chalk.red( |
| 42 | +chalk.bold("Invalid token."), |
| 43 | +"Please, verify your token and try again." |
| 44 | +) |
| 45 | +); |
| 46 | +}else{ |
| 47 | +console.log(chalk.red.bold("Something wrong happened")); |
| 48 | +console.log( |
| 49 | +chalk.red( |
| 50 | +"You may have to delete the droplet manually on your Digital Ocean dashboard." |
| 51 | +) |
| 52 | +); |
| 53 | +} |
| 54 | +}; |
| 55 | + |
| 56 | +constoneMinute=1000*60; |
| 57 | +constfiveMinutes=oneMinute*5; |
| 58 | + |
| 59 | +constwaitUntilBeActive=(droplet:Droplet,token:string)=>{ |
| 60 | +returnwaitUntil( |
| 61 | +async()=>{ |
| 62 | +constdropletInfo=awaitgetDroplet({ token,id:droplet.id}); |
| 63 | +returndropletInfo.status==="active"; |
| 64 | +}, |
| 65 | +{timeout:fiveMinutes,intervalBetweenAttempts:oneMinute/2} |
| 66 | +); |
| 67 | +}; |
| 68 | + |
| 69 | +constwaitUntilHasPublicIp=(droplet:Droplet,token:string)=>{ |
| 70 | +returnwaitUntil( |
| 71 | +async()=>{ |
| 72 | +constdropletInfo=awaitgetDroplet({ token,id:droplet.id}); |
| 73 | +constip=getPublicIp(dropletInfo); |
| 74 | +returnip!==undefined; |
| 75 | +}, |
| 76 | +{timeout:fiveMinutes,intervalBetweenAttempts:oneMinute/2} |
| 77 | +); |
| 78 | +}; |
| 79 | + |
| 80 | +constwaitUntilCodeServerIsLive=(droplet:Droplet,token:string)=>{ |
| 81 | +returnwaitUntil( |
| 82 | +async()=>{ |
| 83 | +constdropletInfo=awaitgetDroplet({ token,id:droplet.id}); |
| 84 | +returnisCodeServerLive(dropletInfo); |
| 85 | +}, |
| 86 | +{timeout:fiveMinutes*2,intervalBetweenAttempts:oneMinute/2} |
| 87 | +); |
| 88 | +}; |
| 89 | + |
| 90 | +exportconstdeployDigitalOcean=async()=>{ |
| 91 | +letspinner:ora.Ora; |
| 92 | + |
| 93 | +console.log( |
| 94 | +chalk.blue( |
| 95 | +"You can create a token on", |
| 96 | +chalk.bold("https://cloud.digitalocean.com/account/api/tokens") |
| 97 | +) |
| 98 | +); |
| 99 | +const{ token}=awaitinquirer.prompt([ |
| 100 | +{name:"token",message:"Your Digital Ocean token:",type:"password"}, |
| 101 | +]); |
| 102 | + |
| 103 | +try{ |
| 104 | +letspinner=ora("Creating droplet and installing code-server").start(); |
| 105 | +letdroplet=awaitcreateDroplet({ |
| 106 | +userData:awaitgetUserDataScript(), |
| 107 | + token, |
| 108 | +}); |
| 109 | +spinner.stop(); |
| 110 | +console.log(chalk.green("✅ Droplet created")); |
| 111 | + |
| 112 | +spinner=ora("Waiting droplet to be active").start(); |
| 113 | +awaitwaitUntilBeActive(droplet,token); |
| 114 | +spinner.stop(); |
| 115 | +console.log(chalk.green("✅ Droplet active")); |
| 116 | + |
| 117 | +spinner=ora("Waiting droplet to have a public IP").start(); |
| 118 | +awaitwaitUntilHasPublicIp(droplet,token); |
| 119 | +spinner.stop(); |
| 120 | +console.log(chalk.green("✅ Public IP is available")); |
| 121 | + |
| 122 | +spinner=ora( |
| 123 | +"Waiting code-server to be live. It can take up to 5 minutes." |
| 124 | +).start(); |
| 125 | +awaitwaitUntilCodeServerIsLive(droplet,token); |
| 126 | +droplet=awaitgetDroplet({ token,id:droplet.id}); |
| 127 | +spinner.stop(); |
| 128 | +console.log( |
| 129 | +chalk.green( |
| 130 | +`🚀 Your code-server is live. You can access it on`, |
| 131 | +chalk.bold(`http://${getPublicIp(droplet)}`) |
| 132 | +) |
| 133 | +); |
| 134 | +}catch(error){ |
| 135 | +spinner.stop(); |
| 136 | +handleErrorLog(error); |
| 137 | +} |
| 138 | +}; |