feat: allow explicitly disabling client routes (#2311)

This commit is contained in:
Kiwi
2019-05-15 01:27:34 +02:00
committed by GitHub
parent 1794086683
commit 898bc0de06
7 changed files with 164 additions and 113 deletions
+8 -1
View File
@@ -110,6 +110,13 @@ const config: Config = {
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor("npm run --silent start:development"),
},
runServerWithoutClientRoutes: {
paths: ["locales/**/*.ftl"],
ignore: ["core/client/**/*"],
executor: new LongRunningExecutor(
"DISABLE_CLIENT_ROUTES=true npm run --silent start:development"
),
},
runServerLint: {
paths: ["core/**/*.ts"],
ignore: ["core/client/**/*"],
@@ -140,7 +147,7 @@ const config: Config = {
"runServerSyntaxCheck",
],
client: [
"runServer",
"runServerWithoutClientRoutes",
"runServerLint",
"runServerSyntaxCheck",
"runWebpackDevServer",
+8 -1
View File
@@ -36,6 +36,7 @@ export interface AppOptions {
signingConfig: JWTSigningConfig;
tenantCache: TenantCache;
metrics: boolean;
disableClientRoutes: boolean;
}
/**
@@ -58,7 +59,13 @@ export async function createApp(options: AppOptions): Promise<Express> {
const passport = createPassport(options);
// Mount the router.
parent.use("/", createRouter(options, { passport }));
parent.use(
"/",
createRouter(options, {
passport,
disableClientRoutes: options.disableClientRoutes,
})
);
// Enable CORS headers for media assets, font's require them.
parent.use("/assets/media", cors());
+123 -3
View File
@@ -1,8 +1,15 @@
import express from "express";
import express, { Router } from "express";
import { minify } from "html-minifier";
import path from "path";
import { cacheHeadersMiddleware } from "talk-server/app/middleware/cacheHeaders";
import { Entrypoint } from "../helpers/entrypoints";
import { cspTenantMiddleware } from "talk-server/app/middleware/csp/tenant";
import { installedMiddleware } from "talk-server/app/middleware/installed";
import { tenantMiddleware } from "talk-server/app/middleware/tenant";
import logger from "talk-server/logger";
import TenantCache from "talk-server/services/tenant/cache";
import Entrypoints, { Entrypoint } from "../helpers/entrypoints";
export interface ClientTargetHandlerOptions {
/**
@@ -28,7 +35,7 @@ export interface ClientTargetHandlerOptions {
staticURI: string;
}
export function createClientTargetRouter({
function createClientTargetRouter({
staticURI,
entrypoint,
enableCustomCSS = false,
@@ -63,3 +70,116 @@ export function createClientTargetRouter({
return router;
}
interface MountClientRouteOptions {
tenantCache: TenantCache;
staticURI: string;
}
export function mountClientRoutes(
router: Router,
{ staticURI, tenantCache }: MountClientRouteOptions
) {
// TODO: (wyattjoh) figure out a better way of referencing paths.
// Load the entrypoint manifest.
const manifest = path.join(
__dirname,
"..",
"..",
"..",
"..",
"..",
"dist",
"static",
"asset-manifest.json"
);
const entrypoints = Entrypoints.fromFile(manifest);
if (!entrypoints) {
logger.error(
{ manifest },
"could not load the generated manifest, client routes will remain un-mounted"
);
return;
}
// Tenant identification middleware.
router.use(
tenantMiddleware({
cache: tenantCache,
passNoTenant: true,
})
);
// Add CSP headers to the request, which only apply when serving HTML content.
router.use(cspTenantMiddleware);
// Add the embed targets.
router.use(
"/embed/stream",
createClientTargetRouter({
staticURI,
enableCustomCSS: true,
entrypoint: entrypoints.get("stream"),
})
);
router.use(
"/embed/auth",
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("auth"),
})
);
router.use(
"/embed/auth/callback",
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("authCallback"),
})
);
// Add the standalone targets.
router.use(
"/account",
// If we aren't already installed, redirect the user to the install page.
installedMiddleware(),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("account"),
})
);
// Add the standalone targets.
router.use(
"/admin",
// If we aren't already installed, redirect the user to the install page.
installedMiddleware(),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("admin"),
})
);
router.use(
"/install",
// If we're already installed, redirect the user to the admin page.
installedMiddleware({
redirectIfInstalled: true,
redirectURL: "/admin",
}),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("install"),
})
);
// Handle the root path.
router.get(
"/",
// Redirect the user to the install page if they are not, otherwise redirect
// them to the admin.
installedMiddleware(),
(req, res, next) => res.redirect("/admin")
);
}
+7 -108
View File
@@ -1,20 +1,15 @@
import express, { Router } from "express";
import path from "path";
import { register } from "prom-client";
import { AppOptions } from "talk-server/app";
import { noCacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
import { cspTenantMiddleware } from "talk-server/app/middleware/csp/tenant";
import { installedMiddleware } from "talk-server/app/middleware/installed";
import playground from "talk-server/app/middleware/playground";
import { tenantMiddleware } from "talk-server/app/middleware/tenant";
import { RouterOptions } from "talk-server/app/router/types";
import logger from "talk-server/logger";
import Entrypoints from "../helpers/entrypoints";
import { basicAuth } from "../middleware/basicAuth";
import { createAPIRouter } from "./api";
import { createClientTargetRouter } from "./client";
import { mountClientRoutes } from "./client";
export function createRouter(app: AppOptions, options: RouterOptions) {
// Create a router.
@@ -28,109 +23,13 @@ export function createRouter(app: AppOptions, options: RouterOptions) {
attachGraphiQL(router, app);
}
// TODO: (wyattjoh) figure out a better way of referencing paths.
// Load the entrypoint manifest.
const manifest = path.join(
__dirname,
"..",
"..",
"..",
"..",
"..",
"dist",
"static",
"asset-manifest.json"
);
const entrypoints = Entrypoints.fromFile(manifest);
if (entrypoints) {
// Tenant identification middleware.
router.use(
tenantMiddleware({
cache: app.tenantCache,
passNoTenant: true,
})
);
// Add CSP headers to the request, which only apply when serving HTML content.
router.use(cspTenantMiddleware);
const staticURI = app.config.get("static_uri");
// Add the embed targets.
router.use(
"/embed/stream",
createClientTargetRouter({
staticURI,
enableCustomCSS: true,
entrypoint: entrypoints.get("stream"),
})
);
router.use(
"/embed/auth",
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("auth"),
})
);
router.use(
"/embed/auth/callback",
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("authCallback"),
})
);
// Add the standalone targets.
router.use(
"/account",
// If we aren't already installed, redirect the user to the install page.
installedMiddleware(),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("account"),
})
);
// Add the standalone targets.
router.use(
"/admin",
// If we aren't already installed, redirect the user to the install page.
installedMiddleware(),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("admin"),
})
);
router.use(
"/install",
// If we're already installed, redirect the user to the admin page.
installedMiddleware({
redirectIfInstalled: true,
redirectURL: "/admin",
}),
createClientTargetRouter({
staticURI,
cacheDuration: false,
entrypoint: entrypoints.get("install"),
})
);
// Handle the root path.
router.get(
"/",
// Redirect the user to the install page if they are not, otherwise redirect
// them to the admin.
installedMiddleware(),
(req, res, next) => res.redirect("/admin")
);
if (!options.disableClientRoutes) {
mountClientRoutes(router, {
staticURI: app.config.get("static_uri"),
tenantCache: app.tenantCache,
});
} else {
logger.warn(
{ manifest },
"could not load the generated manifest, client routes will remain un-mounted"
);
logger.warn("client routes are disabled");
}
if (app.metrics) {
+5
View File
@@ -6,4 +6,9 @@ export interface RouterOptions {
* and mount new authentication middleware.
*/
passport: passport.Authenticator;
/**
* disableClientRoutes will not mount the routes to the client bundles.
*/
disableClientRoutes: boolean;
}
+8
View File
@@ -181,6 +181,14 @@ const config = convict({
env: "DISABLE_MONGODB_AUTOINDEXING",
arg: "disableMongoDBAutoindexing",
},
disable_client_routes: {
doc:
"Disables mounting of client routes for developing with Webpack Dev Server",
format: Boolean,
default: false,
env: "DISABLE_CLIENT_ROUTES",
arg: "disableClientRoutes",
},
});
export type Config = typeof config;
+5
View File
@@ -241,6 +241,10 @@ class Server {
// Only enable the metrics server if concurrency is set to 1.
const metrics = this.config.get("concurrency") === 1;
// Disables the client routes to serve bundles etc. Useful for devleoping with
// Webpack Dev Server.
const disableClientRoutes = this.config.get("disable_client_routes");
// Create the Talk App, branching off from the parent app.
const app: Express = await createApp({
parent,
@@ -254,6 +258,7 @@ class Server {
mailerQueue: this.tasks.mailer,
scraperQueue: this.tasks.scraper,
metrics,
disableClientRoutes,
});
// Start the application and store the resulting http.Server. The server