mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 09:55:59 +08:00
Merge branch 'next-ui-select' of https://github.com/coralproject/talk into next-ui-select
This commit is contained in:
Generated
+18
@@ -1772,6 +1772,15 @@
|
||||
"integrity": "sha512-p+gNRe4RPjpl1lTBUomFJ42P8ymArH/P93DFJ0iY873BJ4ZmogcKc6TbHgZQmtQMsy3jxcAo0HcTjidXwo8uKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/cors": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.4.tgz",
|
||||
"integrity": "sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/cross-spawn": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.0.tgz",
|
||||
@@ -6861,6 +6870,15 @@
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"cors": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
|
||||
"integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
|
||||
"requires": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
}
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-3.1.0.tgz",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"cheerio": "^1.0.0-rc.2",
|
||||
"consolidate": "0.14.0",
|
||||
"convict": "^4.3.1",
|
||||
"cors": "^2.8.4",
|
||||
"dataloader": "^1.4.0",
|
||||
"dotenv": "^6.0.0",
|
||||
"dotenv-expand": "^4.2.0",
|
||||
@@ -119,6 +120,7 @@
|
||||
"@types/compression-webpack-plugin": "^0.4.2",
|
||||
"@types/consolidate": "0.0.34",
|
||||
"@types/convict": "^4.2.0",
|
||||
"@types/cors": "^2.8.4",
|
||||
"@types/cross-spawn": "^6.0.0",
|
||||
"@types/dompurify": "0.0.31",
|
||||
"@types/dotenv": "^4.0.3",
|
||||
|
||||
@@ -9,6 +9,7 @@ import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
|
||||
import UglifyJsPlugin from "uglifyjs-webpack-plugin";
|
||||
import webpack, { Configuration } from "webpack";
|
||||
import ManifestPlugin from "webpack-manifest-plugin";
|
||||
import PublicURIWebpackPlugin from "./plugins/PublicURIWebpackPlugin";
|
||||
|
||||
import paths from "./paths";
|
||||
|
||||
@@ -39,6 +40,13 @@ export default function createWebpackConfig({
|
||||
|
||||
const isProduction = env.NODE_ENV === "production";
|
||||
|
||||
/**
|
||||
* ifProduction will only include the nodes if we're in production mode.
|
||||
*/
|
||||
const ifProduction = isProduction
|
||||
? <T extends {}>(...nodes: T[]) => nodes
|
||||
: <T extends {}>(...nodes: T[]) => [];
|
||||
|
||||
const htmlWebpackConfig: Options = {
|
||||
minify: isProduction && {
|
||||
removeComments: true,
|
||||
@@ -438,12 +446,14 @@ export default function createWebpackConfig({
|
||||
stream: [
|
||||
// We ship polyfills by default
|
||||
paths.appPolyfill,
|
||||
...ifProduction(paths.appPublicPath),
|
||||
...devServerEntries,
|
||||
paths.appStreamIndex,
|
||||
],
|
||||
auth: [
|
||||
// We ship polyfills by default
|
||||
paths.appPolyfill,
|
||||
...ifProduction(paths.appPublicPath),
|
||||
...devServerEntries,
|
||||
paths.appAuthIndex,
|
||||
// Remove deactivated entries.
|
||||
@@ -451,12 +461,14 @@ export default function createWebpackConfig({
|
||||
install: [
|
||||
// We ship polyfills by default
|
||||
paths.appPolyfill,
|
||||
...ifProduction(paths.appPublicPath),
|
||||
...devServerEntries,
|
||||
paths.appInstallIndex,
|
||||
],
|
||||
admin: [
|
||||
// We ship polyfills by default
|
||||
paths.appPolyfill,
|
||||
...ifProduction(paths.appPublicPath),
|
||||
...devServerEntries,
|
||||
paths.appAdminIndex,
|
||||
],
|
||||
@@ -495,6 +507,15 @@ export default function createWebpackConfig({
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
...ifProduction(
|
||||
// Inject the pieces we need here to resolve all the now relative url's
|
||||
// against the CDN if it's provided. It will inject the following into
|
||||
// the configuration blob on the page.
|
||||
new PublicURIWebpackPlugin(
|
||||
"{{ staticURI | dump | safe }}",
|
||||
"{{ staticURI }}"
|
||||
)
|
||||
),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
|
||||
@@ -17,6 +17,7 @@ export default {
|
||||
appSrc: resolveSrc("."),
|
||||
appTsconfig: resolveSrc("core/client/tsconfig.json"),
|
||||
appPolyfill: resolveSrc("core/build/polyfills.js"),
|
||||
appPublicPath: resolveSrc("core/build/publicPath.js"),
|
||||
appLocales: resolveSrc("locales"),
|
||||
appThemeVariables: resolveSrc("core/client/ui/theme/variables.ts"),
|
||||
appThemeVariablesCSS: resolveSrc("core/client/ui/theme/variables.css"),
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Hooks } from "html-webpack-plugin";
|
||||
import { Compiler, Plugin } from "webpack";
|
||||
|
||||
export default class PublicURIWebpackPlugin implements Plugin {
|
||||
private configTemplate: string;
|
||||
private prefixTemplate: string;
|
||||
|
||||
constructor(configTemplate: string, prefixTemplate: string) {
|
||||
this.configTemplate = configTemplate;
|
||||
this.prefixTemplate = prefixTemplate;
|
||||
}
|
||||
|
||||
private prefixAttribute(attr: string | boolean) {
|
||||
if (!attr || typeof attr !== "string" || !attr.startsWith("/")) {
|
||||
return attr;
|
||||
}
|
||||
|
||||
return this.prefixTemplate + attr;
|
||||
}
|
||||
|
||||
private prefixTag = (tag: {
|
||||
tagName: string;
|
||||
attributes: Record<string, string | boolean>;
|
||||
}) => {
|
||||
switch (tag.tagName) {
|
||||
case "link":
|
||||
tag.attributes.href = this.prefixAttribute(tag.attributes.href);
|
||||
break;
|
||||
case "script":
|
||||
tag.attributes.src = this.prefixAttribute(tag.attributes.src);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
public apply = (compiler: Compiler) => {
|
||||
compiler.hooks.compilation.tap("CDNWebpackPlugin", compilation => {
|
||||
(compilation.hooks as Hooks).htmlWebpackPluginAlterAssetTags.tapAsync(
|
||||
"CDNWebpackPlugin",
|
||||
(htmlPluginData, cb) => {
|
||||
// Prefix all the asset's url's with the template.
|
||||
htmlPluginData.head.forEach(this.prefixTag);
|
||||
htmlPluginData.body.forEach(this.prefixTag);
|
||||
|
||||
// Insert the public path reference.
|
||||
htmlPluginData.body.unshift({
|
||||
tagName: "script",
|
||||
attributes: {
|
||||
type: "application/json",
|
||||
id: "config",
|
||||
},
|
||||
innerHTML: this.configTemplate,
|
||||
voidTag: false,
|
||||
});
|
||||
|
||||
return cb(null, htmlPluginData);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
__webpack_public_path__ = JSON.parse(
|
||||
document.getElementById("config").innerText
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
.buttonReset {
|
||||
|
||||
/* reset button */
|
||||
user-select: none;
|
||||
font-family: inherit;
|
||||
@@ -28,7 +27,6 @@
|
||||
}
|
||||
|
||||
&:-moz-focusring {
|
||||
color: transparent;
|
||||
textshadow: 0 0 0 #000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,20 @@ convict.addFormat({
|
||||
},
|
||||
});
|
||||
|
||||
// Add a custom format for the optional-url.
|
||||
convict.addFormat({
|
||||
name: "optional-url",
|
||||
validate: (url: string) => {
|
||||
if (url) {
|
||||
Joi.assert(url, Joi.string().uri());
|
||||
}
|
||||
},
|
||||
// Ensure that there is no ending slash.
|
||||
coerce: (url: string) => {
|
||||
return url.replace(/\/$/, "");
|
||||
},
|
||||
});
|
||||
|
||||
const config = convict({
|
||||
env: {
|
||||
doc: "The application environment.",
|
||||
@@ -54,6 +68,7 @@ const config = convict({
|
||||
default: "mongodb://127.0.0.1:27017/talk",
|
||||
env: "MONGODB_URI",
|
||||
arg: "mongodb",
|
||||
sensitive: true,
|
||||
},
|
||||
redis: {
|
||||
doc: "The Redis database to connect to.",
|
||||
@@ -61,6 +76,7 @@ const config = convict({
|
||||
default: "redis://127.0.0.1:6379",
|
||||
env: "REDIS_URI",
|
||||
arg: "redis",
|
||||
sensitive: true,
|
||||
},
|
||||
signing_secret: {
|
||||
doc: "",
|
||||
@@ -68,6 +84,7 @@ const config = convict({
|
||||
default: "keyboard cat", // TODO: (wyattjoh) evaluate best solution
|
||||
env: "SIGNING_SECRET",
|
||||
arg: "signingSecret",
|
||||
sensitive: true,
|
||||
},
|
||||
signing_algorithm: {
|
||||
doc: "",
|
||||
@@ -93,6 +110,13 @@ const config = convict({
|
||||
env: "LOGGING_LEVEL",
|
||||
arg: "logging",
|
||||
},
|
||||
static_uri: {
|
||||
doc: "The URL that static assets will be hosted from",
|
||||
format: "optional-url",
|
||||
default: "",
|
||||
env: "STATIC_URI",
|
||||
arg: "staticUri",
|
||||
},
|
||||
disable_tenant_caching: {
|
||||
doc:
|
||||
"Disables the tenant caching, all tenants will be loaded from MongoDB each time it's needed",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import cons from "consolidate";
|
||||
import cors from "cors";
|
||||
import { Express } from "express";
|
||||
import http from "http";
|
||||
import { Redis } from "ioredis";
|
||||
@@ -56,6 +57,9 @@ export async function createApp(options: AppOptions): Promise<Express> {
|
||||
})
|
||||
);
|
||||
|
||||
// Enable CORS headers for media assets, font's require them.
|
||||
parent.use("/assets/media", cors());
|
||||
|
||||
// Static Files
|
||||
parent.use("/assets", cacheHeadersMiddleware("1w"), serveStatic);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RequestHandler } from "express";
|
||||
import ms from "ms";
|
||||
|
||||
export const nocacheMiddleware: RequestHandler = (req, res, next) => {
|
||||
export const noCacheMiddleware: RequestHandler = (req, res, next) => {
|
||||
// Set cache control headers to prevent browsers/cdn's from caching these
|
||||
// requests.
|
||||
res.set({ "Cache-Control": "no-cache, no-store, must-revalidate" });
|
||||
@@ -9,10 +9,12 @@ export const nocacheMiddleware: RequestHandler = (req, res, next) => {
|
||||
next();
|
||||
};
|
||||
|
||||
export const cacheHeadersMiddleware = (duration?: string): RequestHandler => {
|
||||
export const cacheHeadersMiddleware = (
|
||||
duration?: string | false
|
||||
): RequestHandler => {
|
||||
const maxAge = duration ? Math.floor(ms(duration) / 1000) : false;
|
||||
if (!maxAge) {
|
||||
return nocacheMiddleware;
|
||||
return noCacheMiddleware;
|
||||
}
|
||||
|
||||
return (req, res, next) => {
|
||||
|
||||
@@ -11,10 +11,17 @@ export interface ClientTargetHandlerOptions {
|
||||
/**
|
||||
* cacheDuration is the cache duration that a given request should be cached for.
|
||||
*/
|
||||
cacheDuration?: string;
|
||||
cacheDuration?: string | false;
|
||||
|
||||
/**
|
||||
* staticURI is prepended to the static url's that are included on the static
|
||||
* pages.
|
||||
*/
|
||||
staticURI: string;
|
||||
}
|
||||
|
||||
export function createClientTargetRouter({
|
||||
staticURI,
|
||||
view,
|
||||
cacheDuration = "1h",
|
||||
}: ClientTargetHandlerOptions) {
|
||||
@@ -22,7 +29,7 @@ export function createClientTargetRouter({
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/", cacheHeadersMiddleware(cacheDuration), (req, res) =>
|
||||
res.render(view)
|
||||
res.render(view, { staticURI })
|
||||
);
|
||||
|
||||
return router;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express, { Router } from "express";
|
||||
|
||||
import { AppOptions } from "talk-server/app";
|
||||
import { nocacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
|
||||
import { noCacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
|
||||
import { installedMiddleware } from "talk-server/app/middleware/installed";
|
||||
import playground from "talk-server/app/middleware/playground";
|
||||
import { RouterOptions } from "talk-server/app/router/types";
|
||||
@@ -14,42 +14,56 @@ export async function createRouter(app: AppOptions, options: RouterOptions) {
|
||||
// Create a router.
|
||||
const router = express.Router();
|
||||
|
||||
router.use("/api", nocacheMiddleware, await createAPIRouter(app, options));
|
||||
router.use("/api", noCacheMiddleware, await createAPIRouter(app, options));
|
||||
|
||||
// Attach the GraphiQL if enabled.
|
||||
if (app.config.get("enable_graphiql")) {
|
||||
attachGraphiQL(router, app);
|
||||
}
|
||||
|
||||
const staticURI = app.config.get("static_uri");
|
||||
|
||||
// Add the embed targets.
|
||||
router.use("/embed/stream", createClientTargetRouter({ view: "stream" }));
|
||||
router.use("/embed/auth", createClientTargetRouter({ view: "auth" }));
|
||||
router.use(
|
||||
"/embed/stream",
|
||||
createClientTargetRouter({ staticURI, view: "stream" })
|
||||
);
|
||||
router.use(
|
||||
"/embed/auth",
|
||||
createClientTargetRouter({ staticURI, view: "auth", cacheDuration: false })
|
||||
);
|
||||
|
||||
// Add the standalone targets.
|
||||
router.use(
|
||||
"/admin",
|
||||
// If we aren't already installed, redirect the user to the install page.
|
||||
installedMiddleware({
|
||||
tenantCache: app.tenantCache,
|
||||
}),
|
||||
createClientTargetRouter({ view: "admin" })
|
||||
createClientTargetRouter({ staticURI, view: "admin", cacheDuration: false })
|
||||
);
|
||||
router.use(
|
||||
"/install",
|
||||
// If we're already installed, redirect the user to the admin page.
|
||||
installedMiddleware({
|
||||
tenantCache: app.tenantCache,
|
||||
redirectIfInstalled: true,
|
||||
redirectURL: "/admin",
|
||||
}),
|
||||
createClientTargetRouter({ view: "install", cacheDuration: "" })
|
||||
createClientTargetRouter({
|
||||
staticURI,
|
||||
view: "install",
|
||||
cacheDuration: false,
|
||||
})
|
||||
);
|
||||
|
||||
// Handle the root path.
|
||||
router.get(
|
||||
"/",
|
||||
// Redirect the user to the install page if they are not, otherwise redirect
|
||||
// them to the admin.
|
||||
installedMiddleware({ tenantCache: app.tenantCache }),
|
||||
(req, res, next) => {
|
||||
res.redirect("/admin");
|
||||
}
|
||||
(req, res, next) => res.redirect("/admin")
|
||||
);
|
||||
|
||||
return router;
|
||||
|
||||
@@ -38,9 +38,12 @@ class Server {
|
||||
|
||||
constructor(options: ServerOptions) {
|
||||
this.parentApp = express();
|
||||
|
||||
// Load the configuration.
|
||||
this.config = config
|
||||
.load(options.config || {})
|
||||
.validate({ allowed: "strict" });
|
||||
logger.debug({ config: this.config.toString() }, "loaded configuration");
|
||||
|
||||
// Load the graph schemas.
|
||||
this.schemas = {
|
||||
|
||||
Reference in New Issue
Block a user