mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-20 09:04:08 +00:00
5ef889addd
Summary: This moves enough server material into core to run a home server. The data engine is not yet incorporated (though in manual testing it works when ported). Test Plan: existing tests pass Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2552
75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import {tbind} from 'app/common/tbind';
|
|
import {NextFunction, Request, RequestHandler, Response} from 'express';
|
|
|
|
export type RequestWithTag = Request & {tag: string|null};
|
|
|
|
/**
|
|
*
|
|
* Middleware to handle a /v/TAG/ prefix on urls.
|
|
*
|
|
*/
|
|
export class TagChecker {
|
|
|
|
// Use app.use(tagChecker.inspectTag) to strip /v/TAG/ from urls (if it is present).
|
|
// If the tag is present and matches what is expected, then `tag` is set on the request.
|
|
// If the tag is present but does not match what is expected, a 400 response is returned.
|
|
// If the tag is absent, `tag` is not set on the request.
|
|
public readonly inspectTag: RequestHandler = tbind(this._inspectTag, this);
|
|
|
|
// Use app.get('/path', tagChecker.requireTag, ...) to serve something only if the tag was
|
|
// present in the url. If the tag was not present, the route will not match and express will
|
|
// look further.
|
|
public readonly requireTag: RequestHandler = tbind(this._requireTag, this);
|
|
|
|
// pass in the tag to expect.
|
|
public constructor(public tag: string) {
|
|
}
|
|
|
|
// Like requireTag but for use wrapping other handlers in app.use().
|
|
// Whatever it wraps will be skipped if that tag was not set.
|
|
// See https://github.com/expressjs/express/issues/2591
|
|
public withTag(handler: RequestHandler) {
|
|
return function fn(req: Request, resp: Response, next: NextFunction) {
|
|
if (!(req as RequestWithTag).tag) { return next(); }
|
|
return handler(req, resp, next);
|
|
};
|
|
}
|
|
|
|
// Removes tag from url if present.
|
|
// Returns [remainder, tagInUrl, isMatch]
|
|
private _removeTag(url: string): [string, string|null, boolean] {
|
|
if (url.startsWith('/v/')) {
|
|
const taggedUrl = url.match(/^\/v\/([a-zA-Z0-9.\-_]+)(\/.*)/);
|
|
if (taggedUrl) {
|
|
const tag = taggedUrl[1];
|
|
// Turn off tag matching as we transition to serving
|
|
// static resources from CDN. We don't have version-sensitive
|
|
// routing, so under ordinary operation landing page html served
|
|
// by one home server could have its assets served by another home server.
|
|
// Once the CDN is active, those asset requests won't reach the home
|
|
// servers. TODO: turn tag matching back on when tag mismatches
|
|
// imply a bug.
|
|
return [taggedUrl[2], tag, true /* tag === this.tag */];
|
|
}
|
|
}
|
|
return [url, null, true];
|
|
}
|
|
|
|
private async _inspectTag(req: Request, resp: Response, next: NextFunction) {
|
|
const [newUrl, urlTag, isOk] = this._removeTag(req.url);
|
|
if (!isOk) {
|
|
return resp.status(400).send({error: "Tag mismatch",
|
|
expected: this.tag,
|
|
received: urlTag});
|
|
}
|
|
req.url = newUrl;
|
|
(req as RequestWithTag).tag = urlTag;
|
|
return next();
|
|
}
|
|
|
|
private async _requireTag(req: Request, resp: Response, next: NextFunction) {
|
|
if ((req as RequestWithTag).tag) { return next(); }
|
|
return next('route');
|
|
}
|
|
}
|