11import { field , logger } from "@coder/logger"
2+ import zip from "adm-zip"
23import * as cp from "child_process"
34import * as fs from "fs-extra"
45import * as http from "http"
@@ -10,29 +11,35 @@ import { Readable, Writable } from "stream"
1011import * as tar from "tar-fs"
1112import * as url from "url"
1213import * as util from "util"
13- import zip from "adm-zip"
1414import * as zlib from "zlib"
1515import { HttpCode , HttpError } from "../../common/http"
1616import { HttpProvider , HttpProviderOptions , HttpResponse , Route } from "../http"
17+ import { settings } from "../settings"
1718import { tmpdir } from "../util"
1819import { ipcMain } from "../wrapper"
1920
2021export interface Update {
22+ checked : number
2123 version : string
2224}
2325
2426/**
2527 * Update HTTP provider.
2628 */
2729export class UpdateHttpProvider extends HttpProvider {
28- private update ?: Promise < Update | undefined >
30+ private update ?: Promise < Update >
31+ private updateInterval = 1000 * 60 * 60 * 24 // Milliseconds between update checks.
2932
3033 public constructor ( options : HttpProviderOptions , public readonly enabled : boolean ) {
3134 super ( options )
3235 }
3336
3437 public async handleRequest ( route : Route , request : http . IncomingMessage ) : Promise < HttpResponse | undefined > {
3538 switch ( route . base ) {
39+ case "/check" :
40+ this . ensureMethod ( request )
41+ this . getUpdate ( true )
42+ return { redirect : "/login" }
3643 case "/" : {
3744 this . ensureMethod ( request , [ "GET" , "POST" ] )
3845 if ( route . requestPath !== "/index.html" ) {
@@ -70,29 +77,38 @@ export class UpdateHttpProvider extends HttpProvider {
7077 /**
7178 * Query for and return the latest update.
7279 */
73- public async getUpdate ( ) : Promise < Update | undefined > {
80+ public async getUpdate ( force ?: boolean ) : Promise < Update > {
7481 if ( ! this . enabled ) {
7582 throw new Error ( "updates are not enabled" )
7683 }
7784
7885 if ( ! this . update ) {
79- this . update = this . _getUpdate ( )
86+ this . update = this . _getUpdate ( force )
87+ this . update . then ( ( ) => ( this . update = undefined ) )
8088 }
8189
8290 return this . update
8391 }
8492
85- private async _getUpdate ( ) : Promise < Update | undefined > {
93+ private async _getUpdate ( force ?: boolean ) : Promise < Update > {
8694 const url = "https://api.github.com/repos/cdr/code-server/releases/latest"
95+ const now = Date . now ( )
8796 try {
88- const buffer = await this . request ( url )
89- const data = JSON . parse ( buffer . toString ( ) )
90- const latest = { version : data . name }
91- logger . debug ( "Got latest version" , field ( "latest" , latest . version ) )
92- return this . isLatestVersion ( latest ) ? undefined : latest
97+ let { update } = ! force ? await settings . read ( ) : { update : undefined }
98+ if ( ! update || update . checked + this . updateInterval < now ) {
99+ const buffer = await this . request ( url )
100+ const data = JSON . parse ( buffer . toString ( ) )
101+ update = { checked : now , version : data . name as string }
102+ settings . write ( { update } )
103+ }
104+ logger . debug ( "Got latest version" , field ( "latest" , update . version ) )
105+ return update
93106 } catch ( error ) {
94107 logger . error ( "Failed to get latest version" , field ( "error" , error . message ) )
95- return undefined
108+ return {
109+ checked : now ,
110+ version : "unknown" ,
111+ }
96112 }
97113 }
98114
@@ -103,10 +119,14 @@ export class UpdateHttpProvider extends HttpProvider {
103119 /**
104120 * Return true if the currently installed version is the latest.
105121 */
106- private isLatestVersion ( latest : Update ) : boolean {
122+ public isLatestVersion ( latest : Update ) : boolean {
107123 const version = this . currentVersion
108124 logger . debug ( "Comparing versions" , field ( "current" , version ) , field ( "latest" , latest . version ) )
109- return latest . version === version || semver . lt ( latest . version , version )
125+ try {
126+ return latest . version === version || semver . lt ( latest . version , version )
127+ } catch ( error ) {
128+ return true
129+ }
110130 }
111131
112132 private async getUpdateHtml ( ) : Promise < string > {
@@ -115,8 +135,8 @@ export class UpdateHttpProvider extends HttpProvider {
115135 }
116136
117137 const update = await this . getUpdate ( )
118- if ( ! update ) {
119- return "No updates available"
138+ if ( this . isLatestVersion ( update ) ) {
139+ throw new Error ( "No update available" )
120140 }
121141
122142 return `<button type="submit" class="apply">
@@ -128,7 +148,7 @@ export class UpdateHttpProvider extends HttpProvider {
128148 public async tryUpdate ( route : Route ) : Promise < HttpResponse > {
129149 try {
130150 const update = await this . getUpdate ( )
131- if ( ! update ) {
151+ if ( this . isLatestVersion ( update ) ) {
132152 throw new Error ( "no update available" )
133153 }
134154 await this . downloadUpdate ( update )
0 commit comments