-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
95 lines (83 loc) · 3.79 KB
/
Copy pathserver.js
File metadata and controls
95 lines (83 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*---------------------------------------------------------------------------------------------
* LevelCode — reference update-feed server
*
* A tiny, dependency-free implementation of the Code-OSS update-feed contract that BOTH the
* built-in updater and the notify-only levelcode-updater extension speak:
*
* GET /api/update/{target}/{quality}/{commit}
* → 204 if {commit} is already the latest build
* → 200 { version, productVersion, url, sha256hash, timestamp } otherwise
*
* The "latest build" per (target, quality) is read from a releases file (RELEASES_FILE, default
* ./releases.json). Use it to dev/test the updater, to self-host an update feed, and as the spec
* LevelCode Cloud implements for managed releases.
*
* Run: PORT=9696 RELEASES_FILE=./releases.json node server.js
*--------------------------------------------------------------------------------------------*/
// @ts-check
'use strict';
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = parseInt(process.env.PORT || '9696', 10);
const LOG = require.main === module;
function releasesFile() { return process.env.RELEASES_FILE || path.join(__dirname, 'releases.json'); }
let warnedExample = false;
/** { "<target>": { "<quality>": { commit, productVersion, url, sha256hash, timestamp } } }.
* Falls back to the committed releases.example.json (with a warning) so the notify flow works
* out of the box in dev — real self-hosts create their own releases.json. */
function readReleases() {
try { return JSON.parse(fs.readFileSync(releasesFile(), 'utf8')); } catch { /* fall through to the example */ }
try {
const data = JSON.parse(fs.readFileSync(path.join(__dirname, 'releases.example.json'), 'utf8'));
if (LOG && !warnedExample) { warnedExample = true; console.log('[update] no releases.json found — using releases.example.json (edit it, or create releases.json).'); }
return data;
} catch { return {}; }
}
/** The latest release for a target+quality, or null. */
function latestRelease(target, quality) {
const rel = (readReleases()[target] || {})[quality];
return rel && rel.commit ? rel : null;
}
function send(res, status, body, headers) {
res.writeHead(status, Object.assign({ 'Cache-Control': 'no-store' }, headers || {}));
res.end(body);
}
const server = http.createServer((req, res) => {
if (LOG) {
res.on('finish', () => {
const u = req.url || '';
if (u !== '/' && u !== '/health') { // log everything (incl. misdirected /v1 → 404)
console.log('[update] ' + (req.method || 'GET') + ' ' + u + ' → ' + res.statusCode);
}
});
}
const url = (req.url || '').split('?')[0];
const method = req.method || 'GET';
if (url === '/' || url === '/health') {
return send(res, 200, 'LevelCode reference update-feed server\n', { 'Content-Type': 'text/plain' });
}
const m = /^\/api\/update\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(url);
if (m && method === 'GET') {
const [, target, quality, commit] = m;
const rel = latestRelease(target, quality);
if (!rel || rel.commit === commit) { return send(res, 204, ''); } // up to date (or nothing published)
return send(res, 200, JSON.stringify({
version: rel.commit,
productVersion: rel.productVersion,
url: rel.url,
sha256hash: rel.sha256hash,
timestamp: rel.timestamp || 0,
releaseNotesUrl: rel.releaseNotesUrl
}), { 'Content-Type': 'application/json' });
}
return send(res, 404, 'Not found: ' + method + ' ' + url);
});
if (require.main === module) {
server.listen(PORT, () => {
// eslint-disable-next-line no-console
console.log('LevelCode reference update-feed server on http://localhost:' + PORT + ' (releases: ' + releasesFile() + ')');
console.log('Watch this terminal — every updater check prints as an [update] line.');
});
}
module.exports = { server, latestRelease };