1
1
import { field , logger } from "@coder/logger"
2
+ import zip from "adm-zip"
2
3
import * as cp from "child_process"
3
4
import * as fs from "fs-extra"
4
5
import * as http from "http"
@@ -10,29 +11,35 @@ import { Readable, Writable } from "stream"
10
11
import * as tar from "tar-fs"
11
12
import * as url from "url"
12
13
import * as util from "util"
13
- import zip from "adm-zip"
14
14
import * as zlib from "zlib"
15
15
import { HttpCode , HttpError } from "../../common/http"
16
16
import { HttpProvider , HttpProviderOptions , HttpResponse , Route } from "../http"
17
+ import { settings } from "../settings"
17
18
import { tmpdir } from "../util"
18
19
import { ipcMain } from "../wrapper"
19
20
20
21
export interface Update {
22
+ checked : number
21
23
version : string
22
24
}
23
25
24
26
/**
25
27
* Update HTTP provider.
26
28
*/
27
29
export 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.
29
32
30
33
public constructor ( options : HttpProviderOptions , public readonly enabled : boolean ) {
31
34
super ( options )
32
35
}
33
36
34
37
public async handleRequest ( route : Route , request : http . IncomingMessage ) : Promise < HttpResponse | undefined > {
35
38
switch ( route . base ) {
39
+ case "/check" :
40
+ this . ensureMethod ( request )
41
+ this . getUpdate ( true )
42
+ return { redirect : "/login" }
36
43
case "/" : {
37
44
this . ensureMethod ( request , [ "GET" , "POST" ] )
38
45
if ( route . requestPath !== "/index.html" ) {
@@ -70,29 +77,38 @@ export class UpdateHttpProvider extends HttpProvider {
70
77
/**
71
78
* Query for and return the latest update.
72
79
*/
73
- public async getUpdate ( ) : Promise < Update | undefined > {
80
+ public async getUpdate ( force ?: boolean ) : Promise < Update > {
74
81
if ( ! this . enabled ) {
75
82
throw new Error ( "updates are not enabled" )
76
83
}
77
84
78
85
if ( ! this . update ) {
79
- this . update = this . _getUpdate ( )
86
+ this . update = this . _getUpdate ( force )
87
+ this . update . then ( ( ) => ( this . update = undefined ) )
80
88
}
81
89
82
90
return this . update
83
91
}
84
92
85
- private async _getUpdate ( ) : Promise < Update | undefined > {
93
+ private async _getUpdate ( force ?: boolean ) : Promise < Update > {
86
94
const url = "https://api.github.com/repos/cdr/code-server/releases/latest"
95
+ const now = Date . now ( )
87
96
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
93
106
} catch ( error ) {
94
107
logger . error ( "Failed to get latest version" , field ( "error" , error . message ) )
95
- return undefined
108
+ return {
109
+ checked : now ,
110
+ version : "unknown" ,
111
+ }
96
112
}
97
113
}
98
114
@@ -103,10 +119,14 @@ export class UpdateHttpProvider extends HttpProvider {
103
119
/**
104
120
* Return true if the currently installed version is the latest.
105
121
*/
106
- private isLatestVersion ( latest : Update ) : boolean {
122
+ public isLatestVersion ( latest : Update ) : boolean {
107
123
const version = this . currentVersion
108
124
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
+ }
110
130
}
111
131
112
132
private async getUpdateHtml ( ) : Promise < string > {
@@ -115,8 +135,8 @@ export class UpdateHttpProvider extends HttpProvider {
115
135
}
116
136
117
137
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" )
120
140
}
121
141
122
142
return `<button type="submit" class="apply">
@@ -128,7 +148,7 @@ export class UpdateHttpProvider extends HttpProvider {
128
148
public async tryUpdate ( route : Route ) : Promise < HttpResponse > {
129
149
try {
130
150
const update = await this . getUpdate ( )
131
- if ( ! update ) {
151
+ if ( this . isLatestVersion ( update ) ) {
132
152
throw new Error ( "no update available" )
133
153
}
134
154
await this . downloadUpdate ( update )
0 commit comments