Merge branch 'next-profile' of https://github.com/coralproject/talk into next-profile

This commit is contained in:
Chi Vinh Le
2018-10-03 15:51:52 +02:00
36 changed files with 697 additions and 185 deletions
+18 -3
View File
@@ -19,9 +19,6 @@ const config: Config = {
"core/client/stream/**/*.ts",
"core/client/stream/**/*.tsx",
"core/client/stream/**/*.graphql",
"core/client/auth/**/*.ts",
"core/client/auth/**/*.tsx",
"core/client/auth/**/*.graphql",
"core/server/**/*.graphql",
],
ignore: [
@@ -34,6 +31,23 @@ const config: Config = {
runOnInit: true,
}),
},
compileRelayAdmin: {
paths: [
"core/client/admin/**/*.ts",
"core/client/admin/**/*.tsx",
"core/client/admin/**/*.graphql",
"core/server/**/*.graphql",
],
ignore: [
"core/**/*.d.ts",
"core/**/*.graphql.ts",
"**/test/**/*",
"core/**/*.spec.*",
],
executor: new CommandExecutor("npm run compile:relay-admin", {
runOnInit: true,
}),
},
compileRelayAuth: {
paths: [
"core/client/auth/**/*.ts",
@@ -80,6 +94,7 @@ const config: Config = {
"compileCSSTypes",
"compileRelayStream",
"compileRelayAuth",
"compileRelayAdmin",
"compileSchema",
],
docz: ["runDocz", "compileCSSTypes"],
+1 -2
View File
@@ -59,9 +59,8 @@ export default function({
},
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
rewrites: [{ from: /^\/admin/, to: "/admin.html" }],
},
public: allowedHost,
index: "embed.html",
+160 -18
View File
@@ -5160,7 +5160,6 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@@ -8969,6 +8968,11 @@
}
}
},
"dom-helpers": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz",
"integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg=="
},
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
@@ -9880,6 +9884,39 @@
"time-stamp": "^1.0.0"
}
},
"farce": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/farce/-/farce-0.2.6.tgz",
"integrity": "sha1-PTB/rp6sRqFI/VU13uLfvPZTuoM=",
"requires": {
"dom-helpers": "^3.3.1",
"invariant": "^2.2.4",
"is-promise": "^2.1.0",
"query-string": "^5.1.1",
"redux": "^4.0.0",
"warning": "^3.0.0"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
},
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
@@ -10341,6 +10378,70 @@
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"found": {
"version": "0.3.14",
"resolved": "https://registry.npmjs.org/found/-/found-0.3.14.tgz",
"integrity": "sha512-Z4Ag0L8whIJwVf0MyNTkx9q3ekmbqK5RkCvlcVU7Dn3ruKfKSxbPReUiQi7mWGMTAut2JnkwiobSD/FQRhk9Vw==",
"requires": {
"babel-runtime": "^6.26.0",
"farce": "^0.2.6",
"is-promise": "^2.1.0",
"lodash": "^4.17.5",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.1",
"prop-types-extra": "^1.0.1",
"react-redux": "^5.0.7",
"react-static-container": "^1.0.2",
"redux": "^4.0.0",
"warning": "^3.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"requires": {
"isarray": "0.0.1"
}
},
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"found-relay": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/found-relay/-/found-relay-0.3.1.tgz",
"integrity": "sha512-N9lADMedKGkl+PNY/L/qf0rDy+3HRbO9IX8Gcvjpy2tgRmPy6Z2QM/zVOufCvvD8rYGP84CuqtmTWJvJrLPFYw==",
"requires": {
"babel-runtime": "^6.26.0",
"is-promise": "^2.1.0",
"lodash": "^4.17.10",
"prop-types": "^15.6.2",
"prop-types-extra": "^1.1.0",
"warning": "^4.0.2"
},
"dependencies": {
"warning": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz",
"integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -12080,8 +12181,7 @@
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
"dev": true
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
},
"home-or-tmp": {
"version": "2.0.0",
@@ -12685,7 +12785,6 @@
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dev": true,
"requires": {
"loose-envify": "^1.0.0"
}
@@ -13107,8 +13206,7 @@
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
},
"is-redirect": {
"version": "1.0.0",
@@ -15214,8 +15312,7 @@
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
"dev": true
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"js-yaml": {
"version": "3.12.0",
@@ -15902,6 +15999,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash-es": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -16186,7 +16288,6 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"dev": true,
"requires": {
"js-tokens": "^3.0.0"
}
@@ -17397,8 +17498,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": {
"version": "0.1.0",
@@ -20509,12 +20609,30 @@
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"dev": true,
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"prop-types-extra": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
"requires": {
"react-is": "^16.3.2",
"warning": "^3.0.0"
},
"dependencies": {
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"proxy-addr": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
@@ -21147,8 +21265,7 @@
"react-is": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.2.tgz",
"integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg==",
"dev": true
"integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
@@ -21220,6 +21337,19 @@
"prop-types": "^15.6.0"
}
},
"react-redux": {
"version": "5.0.7",
"resolved": "http://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
"integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==",
"requires": {
"hoist-non-react-statics": "^2.5.0",
"invariant": "^2.0.0",
"lodash": "^4.17.5",
"lodash-es": "^4.17.5",
"loose-envify": "^1.1.0",
"prop-types": "^15.6.0"
}
},
"react-relay": {
"version": "1.7.0-rc.1",
"resolved": "https://registry.npmjs.org/react-relay/-/react-relay-1.7.0-rc.1.tgz",
@@ -21298,6 +21428,11 @@
"prop-types": "^15.6.0"
}
},
"react-static-container": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-static-container/-/react-static-container-1.0.2.tgz",
"integrity": "sha512-rxlZtZk5t6Y3gqqpaZ1lxY3RqlQcBU5uGsSoZj/hbF3ZweDqPbFHDkczT4emAxeaw37OD96RAAoayFGFQZCdWg=="
},
"react-test-renderer": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.2.tgz",
@@ -21510,6 +21645,15 @@
}
}
},
"redux": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz",
"integrity": "sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA==",
"requires": {
"loose-envify": "^1.1.0",
"symbol-observable": "^1.2.0"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@@ -21528,8 +21672,7 @@
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"regenerator-transform": {
"version": "0.12.4",
@@ -23252,8 +23395,7 @@
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string-length": {
"version": "2.0.0",
+4
View File
@@ -10,6 +10,7 @@
"compile:css-types": "tcm src/core/client/",
"compile:relay-stream": "ts-node ./scripts/compileRelay --src ./src/core/client/stream --schema tenant",
"compile:relay-auth": "ts-node ./scripts/compileRelay --src ./src/core/client/auth --schema tenant",
"compile:relay-admin": "ts-node ./scripts/compileRelay --src ./src/core/client/admin --schema tenant",
"compile:schema": "node ./scripts/generateSchemaTypes.js",
"docz": "docz",
"start": "node dist/index.js",
@@ -45,6 +46,9 @@
"dotenv-expand": "^4.2.0",
"express": "^4.16.3",
"express-static-gzip": "^0.3.2",
"farce": "^0.2.6",
"found": "^0.3.14",
"found-relay": "^0.3.1",
"fs-extra": "^6.0.1",
"graphql": "^0.13.2",
"graphql-config": "^2.0.1",
+30
View File
@@ -239,6 +239,22 @@ export default function createWebpackConfig({
},
],
},
{
test: paths.appAdminLocalesTemplate,
use: [
// This is the locales loader that loads available locales
// from a particular target.
{
loader: "locales-loader",
options: {
...localesOptions,
// Target specifies the prefix for fluent files to be loaded.
// ${target}-xyz.ftl and ${†arget}.ftl are loaded into the locales.
target: "admin",
},
},
],
},
// Loader for our fluent files.
{
test: /\.ftl$/,
@@ -416,6 +432,12 @@ export default function createWebpackConfig({
paths.appAuthIndex,
// Remove deactivated entries.
],
admin: [
// We ship polyfills by default
paths.appPolyfill,
...devServerEntries,
paths.appAdminIndex,
],
},
plugins: [
...baseConfig.plugins!,
@@ -435,6 +457,14 @@ export default function createWebpackConfig({
inject: "body",
...htmlWebpackConfig,
}),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "admin.html",
template: paths.appAdminHTML,
chunks: ["admin"],
inject: "body",
...htmlWebpackConfig,
}),
// 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">
+4
View File
@@ -29,6 +29,10 @@ export default {
appAuthLocalesTemplate: resolveSrc("core/client/auth/locales.ts"),
appAuthIndex: resolveSrc("core/client/auth/index.tsx"),
appAdminHTML: resolveSrc("core/client/admin/index.html"),
appAdminLocalesTemplate: resolveSrc("core/client/admin/locales.ts"),
appAdminIndex: resolveSrc("core/client/admin/index.tsx"),
appEmbedIndex: resolveSrc("core/client/embed/index.ts"),
appEmbedHTML: resolveSrc("core/client/embed/index.html"),
appEmbedArticleHTML: resolveSrc("core/client/embed/article.html"),
+10
View File
@@ -0,0 +1,10 @@
const path = require("path");
module.exports = {
extends: "../.babelrc.js",
plugins: [
[
"babel-plugin-relay",
{ artifactDirectory: path.resolve(__dirname, "./__generated__") },
],
],
};
+4
View File
@@ -0,0 +1,4 @@
.root {
width: 1080px;
margin: 0 auto;
}
+13
View File
@@ -0,0 +1,13 @@
import React, { StatelessComponent } from "react";
import { HorizontalGutter } from "talk-ui/components";
import styles from "./App.css";
import Navigation from "./Navigation";
const App: StatelessComponent = ({ children }) => (
<HorizontalGutter className={styles.root}>
<Navigation />
{children}
</HorizontalGutter>
);
export default App;
@@ -0,0 +1,10 @@
import React, { StatelessComponent } from "react";
import { HorizontalGutter, Typography } from "talk-ui/components";
const Community: StatelessComponent = ({ children }) => (
<HorizontalGutter>
<Typography variant="heading3">Community</Typography>
</HorizontalGutter>
);
export default Community;
@@ -0,0 +1,10 @@
import React, { StatelessComponent } from "react";
import { HorizontalGutter, Typography } from "talk-ui/components";
const Configure: StatelessComponent = ({ children }) => (
<HorizontalGutter>
<Typography variant="heading3">Configure</Typography>
</HorizontalGutter>
);
export default Configure;
@@ -0,0 +1,10 @@
import React, { StatelessComponent } from "react";
import { HorizontalGutter, Typography } from "talk-ui/components";
const Moderate: StatelessComponent = ({ children }) => (
<HorizontalGutter>
<Typography variant="heading3">Moderate</Typography>
</HorizontalGutter>
);
export default Moderate;
@@ -0,0 +1,7 @@
.link {
text-decoration: none;
}
.active {
border-bottom: 3px solid var(--palette-primary-main);
}
@@ -0,0 +1,40 @@
import { Link } from "found";
import React, { StatelessComponent } from "react";
import { Button, Flex, Typography } from "talk-ui/components";
import styles from "./Navigation.css";
const Navigation: StatelessComponent = () => (
<Flex itemGutter="double">
<Typography variant="heading1">Talk</Typography>
<Link
to="/admin/moderate"
className={styles.link}
activeClassName={styles.active}
>
<Button>Moderate</Button>
</Link>
<Link
to="/admin/community"
className={styles.link}
activeClassName={styles.active}
>
<Button>Community</Button>
</Link>
<Link
to="/admin/stories"
className={styles.link}
activeClassName={styles.active}
>
<Button>Stories</Button>
</Link>
<Link
to="/admin/configure"
className={styles.link}
activeClassName={styles.active}
>
<Button>Configure</Button>
</Link>
</Flex>
);
export default Navigation;
@@ -0,0 +1,10 @@
import React, { StatelessComponent } from "react";
import { HorizontalGutter, Typography } from "talk-ui/components";
const Stories: StatelessComponent = ({ children }) => (
<HorizontalGutter>
<Typography variant="heading3">Stories</Typography>
</HorizontalGutter>
);
export default Stories;
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Talk - Admin</title>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no">
</head>
<body>
<div id="app"></div>
</body>
</html>
+40
View File
@@ -0,0 +1,40 @@
import { BrowserProtocol, queryMiddleware } from "farce";
import { createFarceRouter, createRender } from "found";
import { Resolver } from "found-relay";
import React, { StatelessComponent } from "react";
import ReactDOM from "react-dom";
import { createManaged } from "talk-framework/lib/bootstrap";
import { TalkContextConsumer } from "talk-framework/lib/bootstrap/TalkContext";
import { initLocalState } from "./local";
import localesData from "./locales";
import routeConfig from "./routeConfig";
async function main() {
const ManagedTalkContextProvider = await createManaged({
initLocalState,
localesData,
userLocales: navigator.languages,
});
const Router = createFarceRouter({
historyProtocol: new BrowserProtocol(),
historyMiddlewares: [queryMiddleware],
routeConfig,
render: createRender({}),
});
const Index: StatelessComponent = () => (
<ManagedTalkContextProvider>
<TalkContextConsumer>
{({ relayEnvironment }) => (
<Router resolver={new Resolver(relayEnvironment)} />
)}
</TalkContextConsumer>
</ManagedTalkContextProvider>
);
ReactDOM.render(<Index />, document.getElementById("app"));
}
main();
+1
View File
@@ -0,0 +1 @@
export { default as initLocalState } from "./initLocalState";
@@ -0,0 +1,21 @@
import { commitLocalUpdate, Environment } from "relay-runtime";
import {
createAndRetain,
LOCAL_ID,
LOCAL_TYPE,
} from "talk-framework/lib/relay";
/**
* Initializes the local state, before we start the App.
*/
export default async function initLocalState(environment: Environment) {
commitLocalUpdate(environment, s => {
const root = s.getRoot();
// Create the Local Record which is the Root for the client states.
const localRecord = createAndRetain(environment, s, LOCAL_ID, LOCAL_TYPE);
root.setLinkedRecord(localRecord, "local");
});
}
@@ -0,0 +1,5 @@
type Local {}
extend type Query {
local: Local!
}
+9
View File
@@ -0,0 +1,9 @@
/**
* The actual content of this file is being generated by our `locales-loader`.
* Please check `./src/loaders` and the webpack config for more information.
*
* This file only represents the types that gets exported.
*/
import { LocalesData } from "talk-framework/lib/i18n";
export default {} as LocalesData;
+17
View File
@@ -0,0 +1,17 @@
import { makeRouteConfig, Route } from "found";
import React from "react";
import App from "./components/App";
import Community from "./components/Community";
import Configure from "./components/Configure";
import Moderate from "./components/Moderate";
import Stories from "./components/Stories";
export default makeRouteConfig(
<Route path="admin" Component={App}>
<Route path="moderate" Component={Moderate} />
<Route path="community" Component={Community} />
<Route path="stories" Component={Stories} />
<Route path="configure" Component={Configure} />
</Route>
);
+7
View File
@@ -34,6 +34,13 @@ const config = convict({
default: "development",
env: "NODE_ENV",
},
enable_graphiql: {
doc: "When true, this will enable the GraphiQL routes",
format: Boolean,
default: false,
env: "ENABLE_GRAPHIQL",
arg: "enableGraphiQL",
},
port: {
doc: "The port to bind.",
format: "port",
@@ -0,0 +1,5 @@
import { RequestHandler } from "express";
export const adminHandler: RequestHandler = (req, res) => {
res.render("admin");
};
@@ -1,5 +0,0 @@
import { RequestHandler } from "express";
export const streamHandler: RequestHandler = (req, res) => {
res.render("stream");
};
@@ -9,7 +9,7 @@ export const nocacheMiddleware: RequestHandler = (req, res, next) => {
next();
};
export const cacheHeadersMiddleware = (duration: string): RequestHandler => {
export const cacheHeadersMiddleware = (duration?: string): RequestHandler => {
const maxAge = duration ? Math.floor(ms(duration) / 1000) : false;
if (!maxAge) {
return nocacheMiddleware;
-156
View File
@@ -1,156 +0,0 @@
import express from "express";
import passport from "passport";
import {
logoutHandler,
signupHandler,
} from "talk-server/app/handlers/auth/local";
import { streamHandler } from "talk-server/app/handlers/embed/stream";
import { apiErrorHandler } from "talk-server/app/middleware/error";
import { errorLogger } from "talk-server/app/middleware/logging";
import { wrapAuthn } from "talk-server/app/middleware/passport";
import tenantMiddleware from "talk-server/app/middleware/tenant";
import managementGraphMiddleware from "talk-server/graph/management/middleware";
import tenantGraphMiddleware from "talk-server/graph/tenant/middleware";
import {
cacheHeadersMiddleware,
nocacheMiddleware,
} from "talk-server/app/middleware/cacheHeaders";
import { AppOptions } from "./index";
import playground from "./middleware/playground";
async function createManagementRouter(app: AppOptions, options: RouterOptions) {
const router = express.Router();
// Management API
router.use(
"/graphql",
express.json(),
await managementGraphMiddleware(
app.schemas.management,
app.config,
app.mongo
)
);
return router;
}
async function createTenantRouter(app: AppOptions, options: RouterOptions) {
const router = express.Router();
// Tenant identification middleware.
router.use(tenantMiddleware({ cache: app.tenantCache }));
// Setup Passport middleware.
router.use(options.passport.initialize());
// Setup auth routes.
router.use("/auth", createNewAuthRouter(app, options));
// Tenant API
router.use(
"/graphql",
express.json(),
// Any users may submit their GraphQL requests with authentication, this
// middleware will unpack their user into the request.
options.passport.authenticate("jwt", { session: false }),
await tenantGraphMiddleware({
schema: app.schemas.tenant,
config: app.config,
mongo: app.mongo,
redis: app.redis,
queue: app.queue,
})
);
return router;
}
function createNewAuthRouter(app: AppOptions, options: RouterOptions) {
const router = express.Router();
// Mount the passport routes.
router.delete(
"/",
options.passport.authenticate("jwt", { session: false }),
logoutHandler({ redis: app.redis })
);
router.post(
"/local",
express.json(),
wrapAuthn(options.passport, app.signingConfig, "local")
);
router.post(
"/local/signup",
express.json(),
signupHandler({ db: app.mongo, signingConfig: app.signingConfig })
);
router.get("/oidc", wrapAuthn(options.passport, app.signingConfig, "oidc"));
router.get(
"/oidc/callback",
wrapAuthn(options.passport, app.signingConfig, "oidc")
);
return router;
}
async function createAPIRouter(app: AppOptions, options: RouterOptions) {
// Create a router.
const router = express.Router();
// Configure the tenant routes.
router.use("/tenant", await createTenantRouter(app, options));
// Configure the management routes.
router.use("/management", await createManagementRouter(app, options));
// General API error handler.
router.use(errorLogger);
router.use(apiErrorHandler);
return router;
}
export interface RouterOptions {
/**
* passport is the instance of the Authenticator that can be used to create
* and mount new authentication middleware.
*/
passport: passport.Authenticator;
}
export async function createRouter(app: AppOptions, options: RouterOptions) {
// Create a router.
const router = express.Router();
router.use("/api", nocacheMiddleware, await createAPIRouter(app, options));
if (app.config.get("env") === "development") {
// Tenant GraphiQL
router.get(
"/tenant/graphiql",
playground({
endpoint: "/api/tenant/graphql",
subscriptionEndpoint: "/api/tenant/live",
})
);
// Management GraphiQL
router.get(
"/management/graphiql",
playground({
endpoint: "/api/management/graphql",
subscriptionEndpoint: "/api/management/live",
})
);
}
// Handle the stream handler.
router.get("/embed/stream", cacheHeadersMiddleware("1h"), streamHandler);
return router;
}
+39
View File
@@ -0,0 +1,39 @@
import express from "express";
import { AppOptions } from "talk-server/app";
import {
logoutHandler,
signupHandler,
} from "talk-server/app/handlers/auth/local";
import { wrapAuthn } from "talk-server/app/middleware/passport";
import { RouterOptions } from "talk-server/app/router/types";
export function createNewAuthRouter(app: AppOptions, options: RouterOptions) {
const router = express.Router();
// Mount the passport routes.
router.delete(
"/",
options.passport.authenticate("jwt", { session: false }),
logoutHandler({ redis: app.redis })
);
router.post(
"/local",
express.json(),
wrapAuthn(options.passport, app.signingConfig, "local")
);
router.post(
"/local/signup",
express.json(),
signupHandler({ db: app.mongo, signingConfig: app.signingConfig })
);
router.get("/oidc", wrapAuthn(options.passport, app.signingConfig, "oidc"));
router.get(
"/oidc/callback",
wrapAuthn(options.passport, app.signingConfig, "oidc")
);
return router;
}
+34
View File
@@ -0,0 +1,34 @@
import express from "express";
import passport from "passport";
import { AppOptions } from "talk-server/app";
import { apiErrorHandler } from "talk-server/app/middleware/error";
import { errorLogger } from "talk-server/app/middleware/logging";
import { createManagementRouter } from "./management";
import { createTenantRouter } from "./tenant";
export interface RouterOptions {
/**
* passport is the instance of the Authenticator that can be used to create
* and mount new authentication middleware.
*/
passport: passport.Authenticator;
}
export async function createAPIRouter(app: AppOptions, options: RouterOptions) {
// Create a router.
const router = express.Router();
// Configure the tenant routes.
router.use("/tenant", await createTenantRouter(app, options));
// Configure the management routes.
router.use("/management", await createManagementRouter(app));
// General API error handler.
router.use(errorLogger);
router.use(apiErrorHandler);
return router;
}
@@ -0,0 +1,21 @@
import express from "express";
import { AppOptions } from "talk-server/app";
import managementGraphMiddleware from "talk-server/graph/management/middleware";
export async function createManagementRouter(app: AppOptions) {
const router = express.Router();
// Management API
router.use(
"/graphql",
express.json(),
await managementGraphMiddleware(
app.schemas.management,
app.config,
app.mongo
)
);
return router;
}
+42
View File
@@ -0,0 +1,42 @@
import express from "express";
import { AppOptions } from "talk-server/app";
import tenantMiddleware from "talk-server/app/middleware/tenant";
import { RouterOptions } from "talk-server/app/router/types";
import tenantGraphMiddleware from "talk-server/graph/tenant/middleware";
import { createNewAuthRouter } from "./auth";
export async function createTenantRouter(
app: AppOptions,
options: RouterOptions
) {
const router = express.Router();
// Tenant identification middleware.
router.use(tenantMiddleware({ cache: app.tenantCache }));
// Setup Passport middleware.
router.use(options.passport.initialize());
// Setup auth routes.
router.use("/auth", createNewAuthRouter(app, options));
// Tenant API
router.use(
"/graphql",
express.json(),
// Any users may submit their GraphQL requests with authentication, this
// middleware will unpack their user into the request.
options.passport.authenticate("jwt", { session: false }),
await tenantGraphMiddleware({
schema: app.schemas.tenant,
config: app.config,
mongo: app.mongo,
redis: app.redis,
queue: app.queue,
})
);
return router;
}
+29
View File
@@ -0,0 +1,29 @@
import express from "express";
import { cacheHeadersMiddleware } from "talk-server/app/middleware/cacheHeaders";
export interface ClientTargetHandlerOptions {
/**
* view is the name of the template to render.
*/
view: string;
/**
* cacheDuration is the cache duration that a given request should be cached for.
*/
cacheDuration?: string;
}
export function createClientTargetRouter({
view,
cacheDuration = "1h",
}: ClientTargetHandlerOptions) {
// Create a router.
const router = express.Router();
router.get("/", cacheHeadersMiddleware(cacheDuration), (req, res) =>
res.render(view)
);
return router;
}
+60
View File
@@ -0,0 +1,60 @@
import express, { Router } from "express";
import { AppOptions } from "talk-server/app";
import { nocacheMiddleware } from "talk-server/app/middleware/cacheHeaders";
import playground from "talk-server/app/middleware/playground";
import { RouterOptions } from "talk-server/app/router/types";
import logger from "talk-server/logger";
import { createAPIRouter } from "./api";
import { createClientTargetRouter } from "./client";
export async function createRouter(app: AppOptions, options: RouterOptions) {
// Create a router.
const router = express.Router();
router.use("/api", nocacheMiddleware, await createAPIRouter(app, options));
// Attach the GraphiQL if enabled.
if (app.config.get("enable_graphiql")) {
attachGraphiQL(router, app);
}
// Add the client targets.
router.get("/embed/stream", createClientTargetRouter({ view: "stream" }));
router.get("/admin", createClientTargetRouter({ view: "admin" }));
return router;
}
/**
* attachGraphiQL will attach the GraphiQL routes to the router.
*
* @param router the router to attach the GraphiQL routes to
* @param app the application to read the configuration from
*/
function attachGraphiQL(router: Router, app: AppOptions) {
if (app.config.get("env") === "production") {
logger.warn(
"enable_graphiql is enabled, but we're in production mode, this is not recommended"
);
}
// Tenant GraphiQL
router.get(
"/tenant/graphiql",
playground({
endpoint: "/api/tenant/graphql",
subscriptionEndpoint: "/api/tenant/live",
})
);
// Management GraphiQL
router.get(
"/management/graphiql",
playground({
endpoint: "/api/management/graphql",
subscriptionEndpoint: "/api/management/live",
})
);
}
+9
View File
@@ -0,0 +1,9 @@
import passport from "passport";
export interface RouterOptions {
/**
* passport is the instance of the Authenticator that can be used to create
* and mount new authentication middleware.
*/
passport: passport.Authenticator;
}
+5
View File
@@ -0,0 +1,5 @@
declare module "farce" {
// TODO: provide better typings.
export class BrowserProtocol {}
export class queryMiddleware {}
}
+6
View File
@@ -0,0 +1,6 @@
declare module "found-relay" {
import { Environment } from "relay-runtime";
export class Resolver {
constructor(relayEnvironment: Environment);
}
}