|
| 1 | +import{Api}from"coder/site/src/api/api" |
| 2 | +import{Workspace,GetInboxNotificationResponse}from"coder/site/src/api/typesGenerated" |
| 3 | +import{ProxyAgent}from"proxy-agent" |
| 4 | +import*asvscodefrom"vscode" |
| 5 | +import{WebSocket}from"ws" |
| 6 | +import{errToStr}from"./api-helper" |
| 7 | +import{typeStorage}from"./storage" |
| 8 | + |
| 9 | +// These are the template IDs of our notifications. |
| 10 | +// Maybe in the future we should avoid hardcoding |
| 11 | +// these in both coderd and here. |
| 12 | +constTEMPLATE_WORKSPACE_OUT_OF_MEMORY="a9d027b4-ac49-4fb1-9f6d-45af15f64e7a" |
| 13 | +constTEMPLATE_WORKSPACE_OUT_OF_DISK="f047f6a3-5713-40f7-85aa-0394cce9fa3a" |
| 14 | + |
| 15 | +exportclassInboximplementsvscode.Disposable{ |
| 16 | +readonly #storage:Storage |
| 17 | + #disposed=false |
| 18 | + #socket:WebSocket |
| 19 | + |
| 20 | +constructor(workspace:Workspace,httpAgent:ProxyAgent,restClient:Api,storage:Storage){ |
| 21 | +this.#storage=storage |
| 22 | + |
| 23 | +constbaseUrlRaw=restClient.getAxiosInstance().defaults.baseURL |
| 24 | +if(!baseUrlRaw){ |
| 25 | +thrownewError("No base URL set on REST client") |
| 26 | +} |
| 27 | + |
| 28 | +constwatchTemplates=[TEMPLATE_WORKSPACE_OUT_OF_DISK,TEMPLATE_WORKSPACE_OUT_OF_MEMORY] |
| 29 | +constwatchTemplatesParam=encodeURIComponent(watchTemplates.join(",")) |
| 30 | + |
| 31 | +constwatchTargets=[workspace.id] |
| 32 | +constwatchTargetsParam=encodeURIComponent(watchTargets.join(",")) |
| 33 | + |
| 34 | +// We shouldn't need to worry about this throwing. Whilst `baseURL` could |
| 35 | +// be an invalid URL, that would've caused issues before we got to here. |
| 36 | +constbaseUrl=newURL(baseUrlRaw) |
| 37 | +constsocketProto=baseUrl.protocol==="https:" ?"wss:" :"ws:" |
| 38 | +constsocketUrl=`${socketProto}//${baseUrl.host}/api/v2/notifications/inbox/watch?format=plaintext&templates=${watchTemplatesParam}&targets=${watchTargetsParam}` |
| 39 | + |
| 40 | +constcoderSessionTokenHeader="Coder-Session-Token" |
| 41 | +this.#socket=newWebSocket(newURL(socketUrl),{ |
| 42 | +followRedirects:true, |
| 43 | +agent:httpAgent, |
| 44 | +headers:{ |
| 45 | +[coderSessionTokenHeader]:restClient.getAxiosInstance().defaults.headers.common[coderSessionTokenHeader]as |
| 46 | +|string |
| 47 | +|undefined, |
| 48 | +}, |
| 49 | +}) |
| 50 | + |
| 51 | +this.#socket.on("open",()=>{ |
| 52 | +this.#storage.writeToCoderOutputChannel("Listening to Coder Inbox") |
| 53 | +}) |
| 54 | + |
| 55 | +this.#socket.on("error",(error)=>{ |
| 56 | +this.notifyError(error) |
| 57 | +this.dispose() |
| 58 | +}) |
| 59 | + |
| 60 | +this.#socket.on("message",(data)=>{ |
| 61 | +try{ |
| 62 | +constinboxMessage=JSON.parse(data.toString())asGetInboxNotificationResponse |
| 63 | + |
| 64 | +vscode.window.showInformationMessage(inboxMessage.notification.title) |
| 65 | +}catch(error){ |
| 66 | +this.notifyError(error) |
| 67 | +} |
| 68 | +}) |
| 69 | +} |
| 70 | + |
| 71 | +dispose(){ |
| 72 | +if(!this.#disposed){ |
| 73 | +this.#storage.writeToCoderOutputChannel("No longer listening to Coder Inbox") |
| 74 | +this.#socket.close() |
| 75 | +this.#disposed=true |
| 76 | +} |
| 77 | +} |
| 78 | + |
| 79 | +privatenotifyError(error:unknown){ |
| 80 | +constmessage=errToStr(error,"Got empty error while monitoring Coder Inbox") |
| 81 | +this.#storage.writeToCoderOutputChannel(message) |
| 82 | +} |
| 83 | +} |