diff --git a/.dockerignore b/.dockerignore
index e413ac5ea..14b1fb97f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,18 +1,3 @@
-# excluded because we'll likely need to rebuild this.
node_modules
-
-# scripts are used during development and testing, not
-# production.
-scripts
-
-# documentation should not be visable in production.
-docs
-
-# static assets are rebuild in the docker container.
-dist
-
-# tests are not run in the docker container.
test
-
-# we won't use the .git folder in production.
.git
diff --git a/.editorconfig b/.editorconfig
index 4a7ea3036..986314661 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,11 +2,14 @@ root = true
[*]
indent_style = space
-indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+[{package.json,.*rc,*.yml}]
+indent_style = space
+indent_size = 2
+
[*.md]
trim_trailing_whitespace = false
diff --git a/.eslintignore b/.eslintignore
index 6ec4c0c88..83564d3ff 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,3 @@
dist
client/lib
**/*.html
-plugins/
-plugins/**/node_modules
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5edaabbd1..9ce1637d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,20 @@
node_modules
npm-debug.log*
dist
+!dist/coral-admin
+dist/coral-admin/bundle.js
test/e2e/reports
+.DS_Store
+*.iml
+*.swp
dump.rdb
.env
*.cfg
.idea/
coverage/
-*.swp
-plugins.json
-plugins
-!plugins/coral-plugin-respect
+.tags
+.tags1
+
+# remove plugin folders
+# plugins
+# plugins.json
diff --git a/Dockerfile b/Dockerfile
index 5bc38013a..f8cb8a45d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,21 +5,16 @@ RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Setup the environment
+ENV NODE_ENV production
ENV PATH /usr/src/app/bin:$PATH
ENV TALK_PORT 5000
EXPOSE 5000
+# Install app dependencies
+COPY package.json yarn.lock /usr/src/app/
+RUN yarn install --production
+
# Bundle app source
COPY . /usr/src/app
-# Install app dependencies and build static assets.
-RUN yarn install --frozen-lockfile && \
- cli plugins reconcile && \
- yarn build && \
- yarn install --production && \
- yarn cache clean
-
-# Ensure the runtime of the container is in production mode.
-ENV NODE_ENV production
-
CMD ["yarn", "start"]
diff --git a/Dockerfile.onbuild b/Dockerfile.onbuild
deleted file mode 100644
index 34c321448..000000000
--- a/Dockerfile.onbuild
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM coralproject/talk:latest
-
-# Bundle app source
-ONBUILD COPY . /usr/src/app
-
-# At this stage, we need to install the development dependancies again because
-# we need to have webpack available. We then build the new dependancies and
-# clear out the development dependancies again. After this we of course need to
-# clear out the yarn cache, this saves quite a lot of size.
-ONBUILD RUN NODE_ENV=development yarn install --frozen-lockfile && \
- NODE_ENV=production cli plugins reconcile && \
- NODE_ENV=production yarn build && \
- NODE_ENV=production yarn install --production && \
- yarn cache clean
\ No newline at end of file
diff --git a/PLUGINS.md b/PLUGINS.md
index e5cf8d760..a77b0b8b2 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -9,7 +9,7 @@ All plugins must be registered in the root file `plugins.json`.
The format for this file is thus:
-```json
+```js
{
"server": [
"people"
@@ -22,42 +22,6 @@ name in the `plugins/` folder. For example, the above `plugins.json` would
require a plugin from `plugins/people`, which must provide a `index.js` file
that returns an object that matches the Plugin Specification.
-If the package is external (available on NPM) you can specify the string for
-the version by using an object instead, for example:
-
-```json
-{
- "server": [
- {"people": "^1.2.0"}
- ]
-}
-```
-
-External plugins can be resolved by running:
-
-```bash
-./bin/cli plugins reconcile
-```
-
-This will also traverse into local plugin folders and install their
-dependancies. _Note that if the plugin is already installed and available in the
-node_modules folder, it will not be fetched again unless there is a version
-mismatch._
-
-## Plugin Dependencies
-
-From your plugins you may import any component of server code relative to the
-project root. An example could be:
-
-```js
-const cache = require('services/cache');
-```
-
-You may also include additional external depenancies in your local packages by
-specifying a `package.json` at your plugin root which will result in a
-`node_modules` folder being generated at the plugin root with your specific
-dependencies.
-
## Server Plugins
### Specification
@@ -206,88 +170,6 @@ If your post function accepts four parameters, then it can modify the field
result. It is *required* that the function resolves a promise (or returns) with
the modified value or simply the original if you didn't modify it.
-#### Field: `router`
-
-```js
-(router) => {
- router.get('/api/v1/people', (req, res) => {
- res.json({people: [{name: 'Bob'}]});
- });
-}
-```
-
-The Router hook allows you to create a function that accepts the base express
-router where you can mount any amount of middleware/routes to do any form of
-action needed by external applications. We also provide the authorization
-middleware via:
-
-```js
-const authorization = require('middleware/authorization');
-
-module.exports = {
- router(router) {
- router.get('/api/v1/people', authorization.needed('ADMIN'), (req, res) => {
- res.json({people: [{name: 'SECRET PEOPLE'}]});
- });
- }
-}
-```
-
-#### Field: `passport`
-
-```js
-const FacebookStrategy = require('passport-facebook').Strategy;
-const UsersService = require('services/users');
-const {ValidateUserLogin, HandleAuthPopupCallback} = require('services/passport');
-
-module.exports = {
- passport(passport) {
- passport.use(new FacebookStrategy({
- clientID: process.env.TALK_FACEBOOK_APP_ID,
- clientSecret: process.env.TALK_FACEBOOK_APP_SECRET,
- callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`,
- passReqToCallback: true,
- profileFields: ['id', 'displayName', 'picture.type(large)']
- }, async (req, accessToken, refreshToken, profile, done) => {
-
- let user;
- try {
- user = await UsersService.findOrCreateExternalUser(profile);
- } catch (err) {
- return done(err);
- }
-
- return ValidateUserLogin(profile, user, done);
- }));
- },
- router(router) {
-
- // Note that we have to import the passport instance here, it is
- // instantiated after all the strategies have been mounted.
- const {passport} = require('services/passport');
-
- /**
- * Facebook auth endpoint, this will redirect the user immediatly to facebook
- * for authorization.
- */
- router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']}));
-
- /**
- * Facebook callback endpoint, this will send the user a html page designed to
- * send back the user credentials upon sucesfull login.
- */
- router.get('/facebook/callback', (req, res, next) => {
-
- // Perform the facebook login flow and pass the data back through the opener.
- passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next);
- });
- }
-};
-```
-
-This is a full example including the routes hook to add the required components
-to the application router to support a different auth strategy.
-
### Full Example
Contents of `plugins.json`:
diff --git a/README.md b/README.md
index 3111446ea..7e303f70e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Talk [](https://circleci.com/gh/coralproject/talk)
-Talk is currently in Beta! [Read more about Talk here.](https://coralproject.net/products/talk.html)
+A commenting platform from [The Coral Project](https://coralproject.net). Talk enters a closed beta in March 2017, but you can download the code for our alpha here. [Read more about Talk here.](https://coralproject.net/products/talk.html)
Third party licenses are available via the `/client/3rdpartylicenses.txt`
endpoint when the server is running with built assets.
diff --git a/app.js b/app.js
index 3ae03ec72..5b704bea0 100644
--- a/app.js
+++ b/app.js
@@ -3,7 +3,7 @@ const bodyParser = require('body-parser');
const morgan = require('morgan');
const path = require('path');
const helmet = require('helmet');
-const {passport} = require('./services/passport');
+const passport = require('./services/passport');
const session = require('express-session');
const enabled = require('debug').enabled;
const RedisStore = require('connect-redis')(session);
diff --git a/bin/cli b/bin/cli
index 7e27fa85b..c0690e455 100755
--- a/bin/cli
+++ b/bin/cli
@@ -13,7 +13,6 @@ program
.command('setup', 'setup the application')
.command('jobs', 'work with the job queues')
.command('users', 'work with the application auth')
- .command('plugins', 'provides utilities for interacting with the plugin system')
.parse(process.argv);
/**
diff --git a/bin/cli-plugins b/bin/cli-plugins
deleted file mode 100755
index 94ce74904..000000000
--- a/bin/cli-plugins
+++ /dev/null
@@ -1,297 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Module dependencies.
- */
-
-// Interface heavily inspired by the yarn package manager:
-// https://yarnpkg.com/
-
-const program = require('./commander');
-
-// Make things colorful!
-require('colors');
-const emoji = require('node-emoji');
-
-const dir = process.cwd();
-const fs = require('fs');
-const path = require('path');
-const spawn = require('cross-spawn');
-const semver = require('semver');
-const resolve = require('resolve');
-const {plugins, itteratePlugins, isInternal} = require('../plugins');
-
-function existsInNodeModules(name) {
- try {
- resolve.sync(name, {basedir: dir});
-
- return true;
- } catch (e) {
- return false;
- }
-}
-
-function versionMatch(name, version) {
- try {
- let matched = false;
-
- resolve.sync(name, {
- basedir: dir,
- packageFilter: (pkg) => {
- if (pkg && pkg.version && semver.satisfies(pkg.version, version)) {
- matched = true;
- }
-
- return pkg;
- }
- });
-
- return matched;
- } catch (e) {
- return false;
- }
-}
-
-const EXTERNAL = /^\w[a-z\-0-9\.]+$/; // Match "react", "path", "fs", "lodash.random", etc.
-
-function reconcilePackages({quiet = false, upgradeRemote = false}) {
- const fetchable = [];
- const local = [];
- const upgradable = [];
-
- if (!quiet) {
- console.log();
- console.log(' +local (l) packages in your project');
- console.log(' +external (e) packages are external');
- console.log(' +outofdate (oe) packages are external but are out of date');
- console.log(' +missing (m) packages are not found');
- console.log();
- }
-
- for (let i in plugins) {
- let section = itteratePlugins(plugins[i]);
-
- for (let j in section) {
- let {name, version} = section[j];
-
- let namespaced = name.charAt(0) === '@';
- let dep = name.split('/')
- .slice(0, namespaced ? 2 : 1)
- .join('/');
-
- // Ignore relative modules, which aren't installed by NPM
- if (!dep.match(EXTERNAL) && !namespaced) {
- return;
- }
-
- if (isInternal(dep)) {
- if (!quiet) {
- console.log(` l ${name}`);
- }
-
- local.push({name, version});
- continue;
- }
-
- if (!existsInNodeModules(dep)) {
- if (!quiet) {
- console.log(` m ${name}`);
- }
- fetchable.push({name, version});
- } else if (!versionMatch(dep, version)) {
-
- // A plugin was found, yet the current version does not match the
- // current version installed. We should warn if upgradeRemote is
- // not enabled that it is currently not supported.
- if (!upgradeRemote) {
- if (!quiet) {
- console.warn(` oe ${name} (package upgrade may be required)`.bgRed);
- }
-
- continue;
- }
-
- console.log(` oe ${name} (package upgrade may be required)`);
-
- upgradable.push({name, version});
- } else {
- if (!quiet) {
- console.log(` e ${name}`);
- }
-
- if (upgradeRemote) {
- upgradable.push({name, version});
- }
- }
- }
- }
-
- if (!quiet) {
- console.log();
- }
-
- return {local, fetchable, upgradable};
-}
-
-async function reconcileRemotePlugins({skipLocal, dryRun, upgradeRemote}) {
- console.log(`\n[${skipLocal ? '1/2' : '2/3'}] ${emoji.get('mag')} Reconciling plugins...`.yellow);
- const {fetchable, upgradable} = reconcilePackages({upgradeRemote});
-
- console.log(`[${skipLocal ? '2/2' : '3/3'}] ${emoji.get('truck')} Fetching plugins...\n`.yellow);
-
- if (fetchable.length > 0) {
-
- console.log(`$ yarn add ${fetchable.map(({name, version}) => `${name}@${version}`.cyan)}`);
-
- if (!dryRun) {
-
- let args = [
- 'add',
- ...fetchable.map(({name, version}) => `${name}@${version}`)
- ];
-
- let output = spawn.sync('yarn', args, {
- stdio: ['ignore', 'pipe', 'inherit']
- });
-
- if (output.status) {
- throw new Error('Could not install external plugins, errors occured during install');
- }
-
- console.log(output.stdout.toString());
- }
- }
-
- if (upgradable.length > 0) {
- console.log(`$ yarn upgrade ${upgradable.map(({name, version}) => `${name}@${version}`.cyan)}`);
-
- if (!dryRun) {
-
- let args = [
- 'upgrade',
- ...upgradable.map(({name, version}) => `${name}@${version}`)
- ];
-
- let output = spawn.sync('yarn', args, {
- stdio: ['ignore', 'pipe', 'inherit']
- });
-
- if (output.status) {
- throw new Error('Could not install external plugins, errors occured during install');
- }
-
- console.log(output.stdout.toString());
- }
- }
-
- return {upgradable, fetchable};
-}
-
-async function reconcileLocalPlugins({skipRemote, dryRun}) {
- console.log(`\n[${skipRemote ? '1/1' : '1/3'}] ${emoji.get('pick')} Installing local plugin dependencies...\n`.yellow);
- const {local} = reconcilePackages({quiet: true});
-
- for (let i in local) {
- let {name} = local[i];
-
- if (!fs.existsSync(path.join(dir, 'plugins', name, 'package.json'))) {
- continue;
- }
-
- let wd = path.join(dir, 'plugins', name);
-
- console.log(`$ cd ${wd.cyan} && yarn`);
-
- if (!dryRun) {
- let args = [];
-
- let output = spawn.sync('yarn', args, {
- stdio: ['ignore', 'pipe', 'inherit'],
- cwd: wd
- });
-
- if (output.status) {
- throw new Error('Could not install local plugin dependencies, errors occured during install');
- }
-
- console.log(output.stdout.toString());
- }
- }
-}
-
-// This traverses the local plugins and installs any dependencies listed there,
-// this only is really needed for plugins that are installed via docker because
-// core plugins will have their dependencies already included in core.
-async function reconcilePluginDeps({skipLocal, skipRemote, dryRun, upgradeRemote}) {
- let startTime = new Date();
-
- // We don't need to do anything if we skip everything....
- if (skipLocal && skipRemote) {
- return;
- }
-
- // Traverse local plugins and install dependencies if enabled.
- if (!skipLocal) {
- await reconcileLocalPlugins({skipRemote, dryRun});
- }
-
- // Locate any external plugins and install them.
- if (!skipRemote) {
- let results = [];
- try {
- results = await reconcileRemotePlugins({skipLocal, skipRemote, dryRun, upgradeRemote});
- } catch (e) {
- throw e;
- }
-
- let status;
- if (dryRun) {
- status = '[dry-run] success'.green;
- } else {
- status = 'success'.green;
- }
-
- let message;
- if (results.upgradable.length === 0 && results.fetchable.length === 0) {
- message = 'Already up-to-date.';
- } else if (results.upgradable.length === 0) {
- message = `Fetched ${results.fetchable.length} new plugins.`;
- } else if (results.fetchable.length === 0) {
- message = `Upgraded ${results.upgradable.length} new plugins.`;
- } else {
- message = `Fetched ${results.fetchable.length} new plugins, upgraded ${results.upgradable.length} plugins.`;
- }
-
- console.log(`\n${status} ${message}`);
- }
-
- let endTime = new Date();
-
- let totalTime = ((endTime.getTime() - startTime.getTime()) / 1000).toFixed(2);
- console.log(`✨ Done in ${totalTime}s.`);
-}
-
-//==============================================================================
-// Setting up the program command line arguments.
-//==============================================================================
-
-program
- .command('list')
- .description('')
- .action(reconcilePackages);
-
-program
- .command('reconcile')
- .description('reconciles local plugin dependencies and downloads external plugins')
- .option('-u, --upgrade-remote', 'upgrades remote dependencies')
- .option('-d, --dry-run', 'does not actually change anything on the filesystem acts only as a simulation')
- .option('--skip-local', 'skips the local dependancy reconciliation')
- .option('--skip-remote', 'skips the remote plugin reconciliation')
- .action(reconcilePluginDeps);
-
-program.parse(process.argv);
-
-// If there is no command listed, output help.
-if (!process.argv.slice(2).length) {
- program.outputHelp();
-}
diff --git a/circle.yml b/circle.yml
index 013ef28b8..2357ecf4c 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,6 +1,6 @@
machine:
node:
- version: 7
+ version: 7.6
services:
- docker
- redis
@@ -20,7 +20,6 @@ dependencies:
# - sudo service mongod restart
# Install node dependencies.
- - yarn --version
- yarn
cache_directories:
- ~/.cache/yarn
@@ -48,9 +47,9 @@ deployment:
release:
tag: /v[0-9]+(\.[0-9]+)*/
commands:
- - bash ./scripts/docker.sh deploy
+ - bash ./scripts/deploy.sh
latest:
branch: master
commands:
- - bash ./scripts/docker.sh deploy
+ - bash ./scripts/deploy.sh
diff --git a/client/coral-admin/README.md b/client/coral-admin/README.md
new file mode 100644
index 000000000..7bc9de36e
--- /dev/null
+++ b/client/coral-admin/README.md
@@ -0,0 +1,23 @@
+
+# Coral Admin
+
+This app handles moderation for Talk (and maybe more later on)
+
+## Installation
+
+ $ yarn install
+ $ cp config.sample.json config.json
+
+Then change `config.json` to adjust it to your project
+
+## Building for production
+
+ $ yarn build
+
+The public folder has everything you need for deployment. You can just copy that folder to your favorite static web server
+
+## Development
+
+ $ yarn start
+
+A development server will be running at http://localhost:4132
diff --git a/client/coral-embed-stream/src/Comment.css b/client/coral-embed-stream/src/Comment.css
index 4383cbc5b..01022ae66 100644
--- a/client/coral-embed-stream/src/Comment.css
+++ b/client/coral-embed-stream/src/Comment.css
@@ -5,7 +5,6 @@
.Comment {
margin-bottom: 15px;
- position: relative;
}
.pendingComment {
diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js
index f5781a430..39c7d908f 100644
--- a/client/coral-embed-stream/src/Comment.js
+++ b/client/coral-embed-stream/src/Comment.js
@@ -148,7 +148,8 @@ class Comment extends React.Component {
id={`c_${comment.id}`}
style={{marginLeft: depth * 30}}>