mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding latest version section to the admin panel.
Summary: Update for the admin page to show the latest available version information. - Latest version is read from docs.getgrist.com by default - It sends basic information (installationId, deployment type, and version) - Checks are done only on the page itself - The actual request is routed through the API (to avoid CORS) Test Plan: Added new test Reviewers: paulfitz Reviewed By: paulfitz Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D4238
This commit is contained in:
@@ -2,7 +2,7 @@ import {ApiError} from 'app/common/ApiError';
|
||||
import {ICustomWidget} from 'app/common/CustomWidget';
|
||||
import {delay} from 'app/common/delay';
|
||||
import {DocCreationInfo} from 'app/common/DocListAPI';
|
||||
import {encodeUrl, getSlugIfNeeded, GristDeploymentType, GristDeploymentTypes,
|
||||
import {commonUrls, encodeUrl, getSlugIfNeeded, GristDeploymentType, GristDeploymentTypes,
|
||||
GristLoadConfig, IGristUrlState, isOrgInPathOnly, parseSubdomain,
|
||||
sanitizePathTail} from 'app/common/gristUrls';
|
||||
import {getOrgUrlInfo} from 'app/common/gristUrls';
|
||||
@@ -1853,6 +1853,35 @@ export class FlexServer implements GristServer {
|
||||
|
||||
return resp.status(200).send();
|
||||
}));
|
||||
|
||||
// GET api/checkUpdates
|
||||
// Retrieves the latest version of the client from Grist SAAS endpoint.
|
||||
this.app.get('/api/install/updates', adminPageMiddleware, expressWrap(async (req, res) => {
|
||||
// Prepare data for the telemetry that endpoint might expect.
|
||||
const installationId = (await this.getActivations().current()).id;
|
||||
const deploymentType = this.getDeploymentType();
|
||||
const currentVersion = version.version;
|
||||
const response = await fetch(process.env.GRIST_TEST_VERSION_CHECK_URL || commonUrls.versionCheck, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
installationId,
|
||||
deploymentType,
|
||||
currentVersion,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
res.status(response.status);
|
||||
if (response.headers.get('content-type')?.includes('application/json')) {
|
||||
const data = await response.json();
|
||||
res.json(data);
|
||||
} else {
|
||||
res.send(await response.text());
|
||||
}
|
||||
} else {
|
||||
res.json(await response.json());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Get the HTML template sent for document pages.
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { ApiError } from "app/common/ApiError";
|
||||
import { MapWithTTL } from "app/common/AsyncCreate";
|
||||
import { GristDeploymentType } from "app/common/gristUrls";
|
||||
import { LatestVersion } from 'app/common/InstallAPI';
|
||||
import { naturalCompare } from "app/common/SortFunc";
|
||||
import { RequestWithLogin } from "app/server/lib/Authorizer";
|
||||
import { expressWrap } from 'app/server/lib/expressWrap';
|
||||
import { GristServer } from "app/server/lib/GristServer";
|
||||
import { optIntegerParam, optStringParam } from "app/server/lib/requestUtils";
|
||||
import { rateLimit } from 'express-rate-limit';
|
||||
import { AbortController, AbortSignal } from 'node-abort-controller';
|
||||
import type * as express from "express";
|
||||
import fetch from "node-fetch";
|
||||
import {expressWrap} from 'app/server/lib/expressWrap';
|
||||
|
||||
|
||||
// URL to show to the client where the new version for docker based deployments can be found.
|
||||
@@ -67,8 +69,18 @@ export class UpdateManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Rate limit the requests to the version API, so that we don't get spammed.
|
||||
// 30 requests per second, per IP. The requests are cached so, we should be fine, but make
|
||||
// sure it doesn't get out of hand. On dev laptop I could go up to 600 requests per second.
|
||||
// (30 was picked by hand, to not hit the limit during tests).
|
||||
const limiter = rateLimit({
|
||||
windowMs: 1000,
|
||||
limit: 30,
|
||||
legacyHeaders: true,
|
||||
});
|
||||
|
||||
// Support both POST and GET requests.
|
||||
this._app.use("/api/version", expressWrap(async (req, res) => {
|
||||
this._app.use("/api/version", limiter, expressWrap(async (req, res) => {
|
||||
// Get some telemetry from the body request.
|
||||
const payload = (name: string) => req.body?.[name] ?? req.query[name];
|
||||
|
||||
@@ -132,29 +144,6 @@ export class UpdateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON returned to the client (exported for tests).
|
||||
*/
|
||||
export interface LatestVersion {
|
||||
/**
|
||||
* Latest version of core component of the client.
|
||||
*/
|
||||
latestVersion: string;
|
||||
/**
|
||||
* If there were any critical updates after client's version. Undefined if
|
||||
* we don't know client version or couldn't figure this out for some other reason.
|
||||
*/
|
||||
isCritical?: boolean;
|
||||
/**
|
||||
* Url where the client can download the latest version (if applicable)
|
||||
*/
|
||||
updateURL?: string;
|
||||
|
||||
/**
|
||||
* When the latest version was updated (in ISO format).
|
||||
*/
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
type VersionChecker = (signal: AbortSignal) => Promise<LatestVersion>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user