feat: auto-update data, not fully tested yet

This commit is contained in:
Vjacheslav Trushkin 2022-10-14 21:51:39 +03:00
parent 3ae315a7b2
commit f8ed72e24b
5 changed files with 116 additions and 0 deletions

View File

@ -31,6 +31,17 @@ export const appConfig: AppConfig = {
// Log stuff
log: true,
// Enable update
allowUpdate: true,
// Required parameter to include in `/update` query to trigger update
// Value must match environment variable `APP_UPDATE_SECRET`
updateRequiredParam: 'secret',
// Update check throttling
// Delay to wait between successful update request and actual update
updateThrottle: 60,
};
/**

View File

@ -84,3 +84,26 @@ export function updateIconSets(): number {
prefixes = Array.from(newPrefixes);
return prefixes.length;
}
/**
* Trigger update
*/
export function triggerIconSetsUpdate() {
if (!importers) {
return;
}
console.log('Checking for updates...');
(async () => {
let updated = false;
for (let i = 0; i < importers?.length; i++) {
updated = (await importers[i].checkForUpdate()) || updated;
}
return updated;
})()
.then((updated) => {
console.log(updated ? 'Update complete' : 'Nothing to update');
updateIconSets();
})
.catch(console.error);
}

View File

@ -5,6 +5,7 @@ import { iconNameRoutePartialRegEx, iconNameRouteRegEx, splitIconName } from '..
import { generateIconsDataResponse } from './responses/icons';
import { generateLastModifiedResponse } from './responses/modified';
import { generateSVGResponse } from './responses/svg';
import { generateUpdateResponse } from './responses/update';
import { initVersionResponse, versionResponse } from './responses/version';
/**
@ -105,6 +106,14 @@ export async function startHTTPServer() {
});
});
// Update icon sets
server.get('/update', (req, res) => {
generateUpdateResponse(req.query, res);
});
server.post('/update', (req, res) => {
generateUpdateResponse(req.query, res);
});
// Options
server.options('/*', (req, res) => {
res.send(200);

View File

@ -0,0 +1,64 @@
import type { FastifyReply, FastifyRequest } from 'fastify';
import { appConfig } from '../../config/app';
import { triggerIconSetsUpdate } from '../../data/icon-sets';
import { runWhenLoaded } from '../../data/loading';
let pendingUpdate = false;
let lastError = 0;
const envKey = 'APP_UPDATE_SECRET';
function logError(msg: string) {
const time = Date.now();
// Do not log error too often
if (time > lastError + 3600000) {
lastError = time;
console.error(msg);
}
}
function checkKey(query: Record<string, string>): boolean {
if (appConfig.updateRequiredParam) {
const expectedValue = process.env[envKey];
if (!expectedValue) {
// Missing env variable
logError(`Cannot process update request: missing env variable "${envKey}"`);
return false;
}
const value = query[appConfig.updateRequiredParam];
if (value !== expectedValue) {
return false;
}
// Success
return true;
}
// No param
logError(
'Auto-update can be triggered by anyone. Set `updateRequiredParam` config or UPDATE_REQUIRED_PARAM env variable to require secret to trigger update'
);
return true;
}
/**
* Generate icons data
*/
export function generateUpdateResponse(query: FastifyRequest['query'], res: FastifyReply) {
if (appConfig.allowUpdate && checkKey((query || {}) as Record<string, string>) && !pendingUpdate) {
pendingUpdate = true;
runWhenLoaded(() => {
const delay = appConfig.updateThrottle;
console.log('Will check for update in', delay, 'seconds...');
setTimeout(() => {
triggerIconSetsUpdate();
pendingUpdate = false;
}, delay * 1000);
});
}
// Fake 404
res.send(404);
}

View File

@ -23,4 +23,13 @@ export interface AppConfig {
// Logging
log: boolean;
// Allow update
allowUpdate: boolean;
// Update key
updateRequiredParam: string;
// Update check throttling
updateThrottle: number;
}