Updated docs

This commit is contained in:
Wyatt Johnson
2017-09-28 23:38:01 -06:00
parent 44ce71ef14
commit 2e7ee6bb8b
234 changed files with 2050 additions and 22791 deletions
-117
View File
@@ -1,117 +0,0 @@
---
title: Frequently Asked Questions
permalink: /docs/faq/
---
{% include toc %}
### My site doesn't use HSTS headers, how do I stop Talk from sending them too?
You can specify the configuration option `TALK_HELMET_CONFIGURATION` and set it
to:
```
TALK_HELMET_CONFIGURATION={"hsts": false}
```
Which will disable the HSTS module. See the
[helmet](https://github.com/helmetjs/helmet) repository for more information on
how to configure other security middleware used by default.
### How are new stories/assets added to Talk? Is there an API?
There are three ways that new assets can make their way into Talk:
- Just in time
- Active
- Manual
#### Just in Time asset creation
Talk ships with a _just in time_ mechanism that works out of the box without
integration with any CMS or manual work needed.
The _just in time_ flow looks like this:
* Request comes in for a stream on an asset that doesn't yet exist.
* Talk screens the domain against the domain whitelist, fails if doesn't pass.
* Then, concurrently
* Talk creates a new asset record and returns the stream data (which will be
empty)
* Schedules a job to scrape the new page and fill in asset information.
The scraping mechanism utilizes
[metascraper](https://www.npmjs.com/package/metascraper) and is queued using the
[Que](https://www.npmjs.com/package/kue). If your Talk deployments is configured
to run separate job worker cluster, scraping will be performed by them.
#### Active (or push based) asset creation
If tighter CMS integration is required to push custom data into assets and/or
keep data in sync as changes are made in a CMS an _active_ push based workflow
must be implemented.
This is an ideal candidate for a plugin. If you are interested in working on it,
please [contact us](https://coralproject.net/contact)!
#### Manual asset creation
Sometimes you want to load a lot of assets into the database. The most common
use case for this is populating the database during an initial installation. We
recommend writing a script that transforms the data from it's source and inserts
it into the _assets_ collection.
For current schema information, please see the
[asset model](https://github.com/coralproject/talk/blob/master/models/asset.js).
### Where are your http API docs?
Coral relies on GraphQL for the vast majority of it's client and server
communication. All core queries, mutations and subscriptions are defined along
with types and comments in our central
[TypeDef](https://github.com/coralproject/talk/blob/master/graph/typeDefs.graphql).
For plugin graph api typedefs, see each plugin's `/server/` directory.
In addition, Talk Server ships with
[GraphiQL](https://github.com/graphql/graphiql). GraphiQL provides a full data
layer IDE including interactive documentation. The autocomplete and
documentation are populated from introspection meaning that Core _and plugin_
apis can be explored.
To access GraphiQL:
* [Install Talk](install-source).
* Open http://localhost:3000/api/v1/graph/iql in your browser. (Note, your server an port may differ.)
### Where is documentation for a specific component?
We strive for clear inline documentation across our codebase, but have gaps.
Contributions to documentation would be greatly appreciated and is a great way
to start contributing to the project!
If you are considering changing a core component (aka, one that is not in a
plugin), you are entering the realm of a core developer. We strongly ask that
you reach out the coral team before forking and changing core code. We are glad
to help talk through your product need and come up with a strategy for
implementing as a plugin, or working with you to extend the plugin API for your
use case.
### How do I contribute to these docs?
Contributions to the docs are much appreciated and a great way to get involved
in the project.
Fork the Talk repo, clone it locally (no need to go through the install from
source process), then:
```
cd docs
docker run --rm --volume=$PWD:/srv/jekyll -p 127.0.0.1:4000:4000 -it jekyll/jekyll:pages bash -c "bundle install && jekyll serve"
```
You can edit the files in docs with any editor and view the live updates in a
browser by hitting From the docs directory. Then visit:
[http://127.0.0.1:4000/talk/](http://127.0.0.1:4000/talk/).
Once you've made the changes, file a PR back to the `coralproject/talk`
repository.
-21
View File
@@ -1,21 +0,0 @@
---
title: Installation Overview
permalink: /docs/install/
---
## Requirements
Talk requires MongoDB and Redis.
- MongoDB ^3.2 - [Docs](https://docs.mongodb.com/manual/installation/)
- Redis ^3.2.5 - [Docs](https://redis.io/topics/quickstart)
## Installation method
While Talk can be installed in many ways, we support two install paths:
* [Install from Source](source) (development)
* [Install via Docker](docker) (deployment)
If you have success installing Talk in another way, please consider
[contributing to this documentation]({{ "/docs/faq/" | absolute_url }}#how-do-i-contribute-to-these-docs)!
-150
View File
@@ -1,150 +0,0 @@
---
title: Installation From Docker
permalink: /docs/install/docker/
---
We currently support packaging the Talk application via Docker, which automates
the dependency install and asset build process. This is the recommended way to
deploy the application when used in production.
Available as [coralproject/talk](https://hub.docker.com/r/coralproject/talk/) on Docker Hub.
Images are tagged using the following notation:
- `x` (where `x` is the major version number): any minor or patch updates will
be included in this. If you're ok getting new features occasionally and all
the bug fixes, this is the tag for you.
- `x.y` (where `y` is the minor version number): any patch updates will be
included with this tag. If you like getting fixes and having features change
only when you want, this is the tag for you. **(recommended)**
- `x.y.z` (where `z` is the patch version): this tag never gets updated, and
essentially freezes your version, this should only be used when you are either
extending Talk or are sure of a specific version you want to freeze.
We provide tags with `*-onbuild` that can be used for easy plugin integration and
acts as a customization endpoint. Instructions are provided in the `PLUGINS.md`
document as to how to use it.
### Requirements
There are some runtime requirements for running Talk for Docker:
- [Docker](https://www.docker.com/) v1.13.0 or later
- [Docker Compose](https://docs.docker.com/compose/) v1.10.0 or later
_Please be sure to check the versions of these requirements. Incorrect versions
of these may lead to unexpected errors!_
### Installing
An example docker-compose.yml:
```yaml
version: '2'
services:
talk:
image: coralproject/talk:latest
restart: always
ports:
- "5000:5000"
depends_on:
- mongo
- redis
environment:
- TALK_MONGO_URL=mongodb://mongo/talk
- TALK_REDIS_URL=redis://redis
- TALK_ROOT_URL=http://localhost:5000
- TALK_JWT_SECRET=password
- TALK_FACEBOOK_APP_ID=12345
- TALK_FACEBOOK_APP_SECRET=123abc
mongo:
image: mongo:3.2
restart: always
volumes:
- mongo:/data/db
redis:
image: redis:3.2
restart: always
volumes:
- redis:/data
volumes:
mongo:
external: false
redis:
external: false
```
At this stage, you should refer to the [configuration](https://coralproject.github.io/talk/docs/running/configuration/)
for configuration variables that are specific to your installation. Some
pre-defined fields have been filled in the above example which are consistent
with Docker Compose naming conventions for [Docker Links](https://docs.docker.com/compose/networking/#links).
### Scaling
If you are interested in splitting apart services, you can simply adjust the
command being executed in the container to optimize for your use case. An
example would be if you wanted to run the API server and the job processor
on different machines. You can achieve this easily with docker compose:
```yaml
version: '2'
services:
talk-api:
image: coralproject/talk:latest
command: cli serve
restart: always
ports:
- "5000:5000"
depends_on:
- mongo
- redis
environment:
- TALK_MONGO_URL=mongodb://mongo/talk
- TALK_REDIS_URL=redis://redis
talk-jobs:
image: coralproject/talk:latest
command: cli jobs process
restart: always
ports:
- "5001:5000"
depends_on:
- mongo
- redis
environment:
- TALK_MONGO_URL=mongodb://mongo/talk
- TALK_REDIS_URL=redis://redis
mongo:
image: mongo:3.2
restart: always
volumes:
- mongo:/data/db
redis:
image: redis:3.2
restart: always
volumes:
- redis:/data
volumes:
mongo:
external: false
redis:
external: false
```
Note that the only difference is in the `command` key. From this, you are able
to discretely control which modules are running in order to have the maximum
flexibility when managing your application.
### Running
If you're using docker compose:
```bash
# Start the services using compose
docker-compose up -d
```
If you're using plain docker:
```bash
docker run -d -P coralproject/talk:latest
```
-77
View File
@@ -1,77 +0,0 @@
---
title: Installation From Source
permalink: /docs/install/source/
---
This provides information on how to setup the application from source. Note that
this is not recommended for production deploys, but will work for development
and testing purposes.
## Requirements
There are some runtime requirements for running Talk from source:
- [Node](https://nodejs.org/) ~7.8
- [Yarn](https://yarnpkg.com/) ^0.22.0
_Please be sure to check the versions of these requirements. Incorrect versions
of these may lead to unexpected errors!_
## Installing
### Download
It is highly recommended that you download a released version as the code
available in `master` may not be stable. You can download the latest release
from the [releases page](https://github.com/coralproject/talk/releases).
You can also clone the git repository via:
```bash
git clone https://github.com/coralproject/talk.git
```
### Building
We now have to install the dependencies and build the static assets.
```bash
# Install package dependencies
yarn
# Build static files
yarn build
```
After you create/modify the `plugins.json` (refer to `PLUGINS.md` for plugin
docs) file, you can re-run the following to install their dependencies:
```bash
# Reconcile plugins
./bin/cli plugins reconcile
# Build static files
yarn build
```
## Running
Refer to the [configuration](https://coralproject.github.io/talk/docs/running/configuration/) page for required configuration variables to add to the
environment.
You can start the server after configuring the server using the command:
```bash
yarn start
```
This will setup the server to serve everything on a single node.js process and
is designed to be used in production.
You can see other scripts we've made available by consulting the `package.json`
file under the `scripts` key including:
- `yarn test` run unit tests
- `yarn build-watch` watch for changes to client files and build static assets
- `yarn dev-start` watch for changes to server files and reload the server while
also sourcing a `.env` file in your local directory for configuration
-36
View File
@@ -1,36 +0,0 @@
---
title: First Setup
permalink: /docs/install/setup/
---
Once you've installed Talk (either via Docker or source), you still need to
setup the application. If you are unfamiliar with any terminology used in the
setup process, refer to the `TERMINOLOGY.md` document.
## Via Web
If you want to perform your setup via the web, you can navigate to your
installation of Talk at the path `/admin/install`. There you will be asked a
series of questions for your installation.
## Via CLI
If you want to perform your setup through the terminal, you can simply run:
```bash
cli setup
```
And follow the instructions to perform initial setup and create your first user
account.
# Usage
After setup is complete, you can then refer to the `/admin/configure` path to
get the embed code that you can copy/paste onto your blog or website in order to
start using Talk.
_In order for the embed to work correctly, you will need to whitelist the domain
that is allowed to embed your site on the `/admin/configure` page, failure to do
so will result in the comment stream not loading._
-95
View File
@@ -1,95 +0,0 @@
---
title: Microservice Deployments
permalink: /docs/install/microservices/
---
In Talk, we seek to deliver the simplicity of a monolith with the advantages of
a microservice based infrastructure for those who want them.
To accomplish this, Talk has the ability to run with subsets of its overall
functionality and contains architecture that allows them to operate logically as
microservices when running in a single environment.
## Talk Server Functionalities
The Talk server serves several logically/architecturally distinct functions:
* A web server that
* serves "public" assets (aka, the comment embed)
* the GraphQL endpoints
* A web socket server that handles subscriptions.
* A jobs processor that handles queued operations.
In the documentation so far, we've discussed how to deploy all of these
functionalities bundled into a single monolith application. This is convenient
as there is minimal configuration and horizontal scaling is as easy as upping
the number of servers behind a single load balancer.
## Separating Talk into Microservices
Talk can be run in two or more separate clusters of servers by
enabling/disabling different bits of functionality: webserver, socket server and
jobs server.
Each microservice would deploy with the same codebase and configuration.
Note that the `cli serve` command, which is responsible for starting the server,
contains flags that control whether `jobs` and `websockets` are enabled.
```
talk :) ]$ bin/cli serve --help
Options:
...
-j, --jobs enable job processing on this thread
-w, --websockets enable the websocket (subscriptions) handler on this thread
...
```
Each Talk Microservice cluster can be deployed in an identical manner described
in the other docs in this section with the omission of the `-j` and/or `-w`
flags.
With routing logic in front of the webserver cluster, separation between public
and protected assets can be achieved.
## When should I consider separating?
Consider a microservice deployment if:
* you are running plugins that require intensive job processing
* you do not want to simplicity of single cluster horizontal scaling and want to
tune the economy and performance of your install.
* You run into scaling issues serving websockets
At scale, combining separate concerns in a single process makes it very
difficult to understand what is taking up resources. With microservices, each
server could be configured to sit behind it's own load balance and scale
independently. Each variety of process can always have just enough resources.
An install that heavily utilizes the jobs queue could see delays in http service
because of heavy jobs processes and/or delays in the execution of jobs processes
due to increased server load as a result of Node's single thread infrastructure.
## Deployment Methodologies
Note that there is no flag to separate the http routes on the webserver.
Separating the http server functionalities can be accomplished by the routing of
various routes to the correct http server via an external upstream proxy like
Google Cloud's Load Balancer, AWS's ELB, or a websever like NGINX/Apache. This
can ensure that sensitive areas, such as the `/admin/` route are not available
outside the firewall.
Talk's job processors are synchronized by Redis, so as long as all Talk
instances access the same Redis cluster no additional configuration is needed
when launching an independent jobs cluster.
If there are any features of Talk that you believe should be disable-able via
server flags, please let us know and consider contributing it to the project!
## Deployment Flows/Scripts
We do not currently support any microservice based deployment flows. If you
develop one yourself that is completely based on open source tooling, please
consider contributing it to the project!
@@ -1,19 +0,0 @@
---
title: Installation Troubleshooting
permalink: /docs/install/troubleshooting/
---
This page tracks common issues that arise when installing Talk and provides resolutions.
## The Talk server seems to be working but the stream isn't showing up on my page.
Talk employs a _domain whitelist_ that controls which sites can contain comment threads. This prevents malicious folks from using your server to embed streams on unwanted pages.
If your comment thread isn't showing:
1. Log into your admin panel
1. Go to the Configure tab
1. Select the Tech Settings submenu
1. Ensure that your Domain is the Permitted Domains list
Note: if your site has multiple subdomains, listing the domain itself (ie: `mydomain.com`) will enable Talk on all subdomains. If you would like to restrict Talk to certain subdomains, you must list all of them here (ie: `thisone.mydomain.com thatone.mydomain.com`).
-229
View File
@@ -1,229 +0,0 @@
---
title: Configuration
permalink: /docs/running/configuration/
---
{% include toc %}
## Overview
{:.no_toc}
Talk, like many web applications, requires manual configuration via environment
variables to configure the server for your specific needs. This is following the
standard [12 Factor App Manifesto](https://12factor.net/) which requires that
said configuration lives as environment variables.
During development, we can utilize a `.env` file, which takes the form of
`NAME=VALUE`. For example:
```bash
TALK_MONGO_URL=mongodb://some-awesome-mongo-instance
TALK_REDIS_URL=redis://some-awesome-redis-instance
TALK_ROOT_URL=https://my-awesome-talk.com
# ... and so on
```
When you place a file titled `.env` at the root of the project, and start the
application with `yarn dev-start`, it will read in the contents of the `.env`
file as if they were environment variables. This is done via the
[dotenv](https://github.com/motdotla/dotenv) package. In production, using this
method is discouraged, as it promotes bad practices such as storing config in a
file.
## Variables
The Talk application looks for the following configuration values either as
environment variables. Refer to the
[config.js](https://github.com/coralproject/talk/blob/master/config.js) file to
see how the configuration is parsed.
### Webpack
These are only used during the webpack build.
- `TALK_THREADING_LEVEL` (_optional_) - specify the maximum depth of the comment
thread. (Default `3`)
- `TALK_DEFAULT_STREAM_TAB` (_optional_) - specify the default stream tab in the
admin. (Default `all`)
- `TALK_DISABLE_EMBED_POLYFILL` (_optional_) - when set to `TRUE`, the build process will not include the [babel-polyfill](https://babeljs.io/docs/usage/polyfill/) in the embed.js target. (Default `FALSE`)
### Database
- `TALK_MONGO_URL` (*required*) - the database connection string for the MongoDB database.
- `TALK_REDIS_URL` (*required*) - the database connection string for the Redis database.
#### Advanced
{:.no_toc}
- `TALK_REDIS_CLIENT_CONFIG` (_optional_) - configuration overrides for the
redis client configuration in a JSON encoded string. Configuration is
overridden as the second parameter to the redis client constructor, and is
merged with default configuration. (Default `{}`)
- `TALK_REDIS_RECONNECTION_BACKOFF_FACTOR` (_optional_) - the time factor that
will be multiplied against the current attempt count inbetween attempts to
connect to redis. (Default `500 ms`)
- `TALK_REDIS_RECONNECTION_BACKOFF_MINIMUM_TIME` (_optional_) - the minimum time
used to delay before attempting to reconnect to redis. (Default `1 sec`)
- `TALK_REDIS_CLUSTER_MODE` (_optional_) - the cluster mode of the redis client.
Can be either `NONE` or `CLUSTER`. (Default `NONE`)
- `TALK_REDIS_CLUSTER_CONFIGURATION` (_optional_) - the json serialized form of
the cluster nodes. Only required when `TALK_REDIS_CLUSTER_MODE=CLUSTER`. See
https://github.com/luin/ioredis#cluster for configuration details.
(Default `[]`)
### Server
- `TALK_ROOT_URL` (*required*) - root url of the installed application externally
available in the format: `<scheme>://<host>:<port?>/<pathname>`.
- `TALK_KEEP_ALIVE` (_optional_) - The keepalive timeout that should be used to
send keep alive messages through the websocket to keep the socket alive. (Default `30s`)
- `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - When `TRUE`, disables the dynamic setup endpoint. (Default `FALSE`)
#### Advanced
{:.no_toc}
- `TALK_ROOT_URL_MOUNT_PATH` (_optional_) - when set to `TRUE`, the routes will
be mounted onto the `<pathname>` component of the `TALK_ROOT_URL`. You would
use this when your upstream proxy cannot strip the prefix from the url.
(Default `FALSE`)
- `TALK_WEBSOCKET_LIVE_URI` (_optional_) - used to override the location to
connect to the websocket endpoint to potentially another host. This should
be used when you need to route websocket requests out of your CDN in order to
serve traffic more efficiently for websockets. **Warning: if used without
managing the auth state manually, auth cannot be persisted, for further
information refer to the [Persistence Documentation]({{ "/docs/running/persistence/" | absolute_url }})**
(Default `${ssl ? 'ws' : 'wss'}://${location.host}${TALK_ROOT_URL_MOUNT_PATH}api/v1/live`)
- `TALK_STATIC_URI` (_optional_) - Used to set the uri where the static assets
should be served from. This is used when you want to upload the static assets
through your build process to a service like Google Cloud Storage or Amazon S3
and you would then specify the CDN/Storage url. (Default `process.env.TALK_ROOT_URL`)
- `TALK_DISABLE_STATIC_SERVER` (_optional_) - When `TRUE`, it will not mount the
static asset serving routes on the router. (Default `FALSE`)
- `TALK_HELMET_CONFIGURATION` (_optional_) - A JSON string representing the
configuration passed to the
[helmet](https://github.com/helmetjs/helmet) middleware. It can be used to
disable features like [HSTS](https://helmetjs.github.io/docs/hsts/) and others
by simply providing the configuration as detailed on the
[helmet README](https://github.com/helmetjs/helmet). (Default `{}`)
### Word Filter
- `TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS` (_optional_) When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`)
### JWT
The following are configuration shared with every type of secret used.
- `TALK_JWT_ALG` (_optional_) - the algorithm used to sign/verify JWT's used for
session management. Read up about alternative algorithms on the
[jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken#algorithms-supported) package. (Default `HS256`)
- `TALK_JWT_EXPIRY` (_optional_) - the expiry duration (`exp`) for the tokens
issued for logged in sessions. (Default `1 day`)
- `TALK_JWT_ISSUER` (_optional_) - the issuer (`iss`) claim for login JWT
tokens. (Default `process.env.TALK_ROOT_URL`)
- `TALK_JWT_AUDIENCE` (_optional_) - the audience (`aud`) claim for login JWT
tokens. (Default `talk`)
**You must also specify secrets as either the `TALK_JWT_SECRET` or the `TALK_JWT_SECRETS`
variable. Refer to the [Secrets Documentation]({{ "/docs/running/secrets/" | absolute_url }})
on the contents of those variables.**
#### Advanced
{:.no_toc}
These are advanced settings for fine tuning the auth integration, and
is not needed in most situations.
- `TALK_JWT_COOKIE_NAME` (_optional_) - the default cookie name to check for a
valid JWT token to use for verifying a user. (Default `authorization`)
- `TALK_JWT_SIGNING_COOKIE_NAME` (_optional_) - the default cookie name that is
use to set a cookie containing a JWT that was issued by Talk.
(Default `process.env.TALK_JWT_COOKIE_NAME`)
- `TALK_JWT_COOKIE_NAMES` (_optional_) - the different cookie names to check for
a JWT token in, separated by `,`. By default, we always use the
`process.env.TALK_JWT_COOKIE_NAME` and `process.env.TALK_JWT_SIGNING_COOKIE_NAME`
for this value. Any additional cookie names specified here will be appended to
the list of cookie names to inspect.
- `TALK_JWT_CLEAR_COOKIE_LOGOUT` (_optional_) - when `FALSE`, Talk will not
clear the cookie with name `TALK_JWT_COOKIE_NAME` when logging out (Default
`TRUE`)
- `TALK_JWT_DISABLE_AUDIENCE` (_optional_) - when `TRUE`, Talk will not verify or sign JWT's
with an audience (`aud`) claim, even if the `TALK_JWT_AUDIENCE` config is set. (Default `FALSE`)
- `TALK_JWT_DISABLE_ISSUER` (_optional_) - when `TRUE`, Talk will not verify or sign JWT's
with an issuer (`iss`) claim, even if the `TALK_JWT_ISSUER` config is set. (Default `FALSE`)
- `TALK_JWT_USER_ID_CLAIM` (_optional_) - specify the claim using dot notation for where the
user id should be stored/read to/from. Example `user.id` would store it like: `{user: {id}}`
on the claims object. (Default `sub`)
When integrating with an external authentication system, the following JWT claims
will be used:
```js
{
"jti": "<the unique token identifier>", // *required* unique id used for blacklisting
"aud": TALK_JWT_AUDIENCE, // *optional* if TALK_JWT_DISABLE_AUDIENCE === 'TRUE', *required* otherwise
"iss": TALK_JWT_ISSUER, // *optional* if TALK_JWT_DISABLE_ISSUER === 'TRUE', *required* otherwise
[TALK_JWT_USER_ID_CLAIM]: "<the user id>", // *required* the id of the user
// Note, if TALK_JWT_USER_ID_CLAIM contains '.', it will be used to delineate an object, for example
// `user.id` would store it like: `{user: {id}}`
}
```
When our passport middleware checks for JWT tokens, it searches in the following
order:
1. Custom cookies named from the list in `TALK_JWT_COOKIE_NAMES`.
2. Default cookies named `TALK_JWT_COOKIE_NAME` then `TALK_JWT_SIGNING_COOKIE_NAME`.
3. Query parameter `?access_token={TOKEN}`.
4. Header: `Authorization: Bearer {TOKEN}`.
### Email
- `TALK_SMTP_EMAIL` (*required for email*) - the address to send emails from
using the SMTP provider.
- `TALK_SMTP_USERNAME` (*required for email*) - username of the SMTP provider
you are using.
- `TALK_SMTP_PASSWORD` (*required for email*) - password for the SMTP provider
you are using.
- `TALK_SMTP_HOST` (*required for email*) - SMTP host url with format
`smtp.domain.com`, note the lack of protocol on the domain.
- `TALK_SMTP_PORT` (*required for email*) - SMTP port.
### Recaptcha
- `TALK_RECAPTCHA_SECRET` (*required for reCAPTCHA support*) - server secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
- `TALK_RECAPTCHA_PUBLIC` (*required for reCAPTCHA support*) - client secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
### Trust
Trust can auto-moderate comments based on user history. By specifying this
option, the behavior can be changed to offer different results.
- `TRUST_THRESHOLDS` (_optional_) - configure the reliability thresholds for
flagging and commenting. (Default `comment:2,-1;flag:2,-1`)
The form of the environment variable:
```
<name>:<RELIABLE>,<UNRELIABLE>;<name>:<RELIABLE>,<UNRELIABLE>;...
```
The default could be read as:
- When a commenter has one comment rejected, their next comment must be
pre-moderated once in order to post freely again. If they instead get rejected
again, then they must have two of their comments approved in order to get
added back to the queue.
- At the moment of writing, behavior is not attached to the flagging
reliability, but it is recorded.
### Cache
- `TALK_CACHE_EXPIRY_COMMENT_COUNT` (_optional_) - configure the duration for which
comment counts are cached for. (Default `1hr`)
### Plugins
Plugins configuration can be found on the [Plugins]({{ "/docs/running/plugins/" | absolute_url }}) page.
-111
View File
@@ -1,111 +0,0 @@
---
title: Secrets
permalink: /docs/running/secrets/
---
{% include toc %}
## Secret Types
We support two types of secrets.
- Shared secrets
- Asymmetric Secrets
### Shared Secret
You would use a shared secret when you have no need to share the tokens with
other applications in your organization.
Supported signing algorithms:
- HS256
- HS384
- HS512
These must be provided in the form:
```json
{
"secret": "<my secret key>"
}
```
### Asymmetric Secret
You would use a asymmetric secret when you want to share the token in your
organization, and would like to pass an existing auth token to Talk in order to
authenticate your users. (Documentation on how to do this is pending!).
Supported signing algorithms:
- RS256
- RS384
- RS512
- ES256
- ES384
- ES512
These must be provided in the form:
```json
{
"public": "<the PEM encoded public key>",
"private": "<the PEM encoded private key>"
}
```
Note that when using the asymmetric keys as discussed above, the certificates
must have their newlines replaced with `\\n`, this is to ensure that the
newlines are preserved after JSON decoding. Not doing so will result in parsing
errors.
To assist with this process, we have developed a tool that can generate new
certificates that match our required format: [coralcert](https://github.com/coralproject/coralcert).
This tool can generate RSA and ECDSA certificates, check it's [README](https://github.com/coralproject/coralcert)
for more details.
## Authentication Types
Talk also supports two methods of providing authentication details.
- Single key: this is used when your secrets do not need to be rotated.
- Multiple keys: this is used when you expect to rotate your secrets.
### Single Key
When using a single key, you can utilize the following configuration style:
- `TALK_JWT_SECRET` (*required*) - The shared secret or certificate (depending
on your choice of `TALK_JWT_ALG`) used for jwt tokens.
An example of this:
```bash
# for a shared secret
TALK_JWT_SECRET={"secret": "<my secret string>"}
# for a asymmetric secret
TALK_JWT_SECRET={"private": "<my private key>", "public": "<my public key>"}
```
### Multiple Key
When using a multiple keys, you can utilize the following configuration style:
- `TALK_JWT_SECRETS` (_optional_) - used when specifying multiple secrets used
for key rotations. This is a JSON encoded array, where each element matches
the JWT Secret pattern.
All secrets also get a `kid` field which uniquely identifies a given key and
will sign all tokens with that `kid` for later identification.
An example of this:
```bash
# for a shared secret
TALK_JWT_SECRETS=[{"kid" "1", "secret": "<my secret string>"}, {"kid" "2", "secret": "<my other secret string>"}]
# for a asymmetric secret
TALK_JWT_SECRETS=[{"kid": "1", "private": "<my private key>", "public": "<my public key>"}, {"kid": "2", "private": "<my other private key>", "public": "<my other public key>"}]
```
-25
View File
@@ -1,25 +0,0 @@
---
title: Plugins
permalink: /docs/running/plugins/
---
Configuration for the available plugins are stored in a JSON encoded string. The format
of this document are available with the [Plugins Overview]({{ "/docs/plugins/" | absolute_url }}).
You can override the plugin config by specifying the content in the `TALK_PLUGIN_JSON`
environment variable.
## Bundled Plugin Configuration
{:.no_toc}
Some of the core plugins that are bundled with Talk require specific configuration to be
available.
{% include toc %}
### Facebook Authentication
- `TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook
Login enabled app.
- `TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your
Facebook Login enabled app.
-15
View File
@@ -1,15 +0,0 @@
---
title: Database Migrations
permalink: /docs/running/migrations/
---
On major version changes, database migrations are usually required. The
application will refuse to start until all pending migrations are ran. This also
applies to empty databases.
## Running Migrations
We have a migration tool that can be run using `bin/cli migration run`. This
will detect new migrations available and prompt you to backup your database
before proceeding with the migration. Migrations are required with major version
releases.
-50
View File
@@ -1,50 +0,0 @@
---
title: User Persistence
permalink: /docs/running/persistence/
---
One of the biggest problems on the internet today is the proliferation of
sophisticated tracking systems and the outcome of invading user's privacy. This
has had quite a big impact on our design of Talk, as we've had to work around
the safeguards that are there to keep your data safe while still allowing our
application to run smoothly on the page it's embedded on.
---
**Problem**: Safari has inconsistent behavior around localStorage when used
within an iFrame.
**Solution**: We set a cookie instead when Safari is detected to store the auth
state.
---
**Problem**: Safari's default privacy settings block cookies from domains that
do not match the current domain.
**Solution**: When using Talk's built in auth, we will open a pop-out when
setting the cookie, so that the domain of the setting domain matches the issuer.
---
**Problem**: When using a different domain for websockets, and using the built
in auth solution, cookies are not set on that domain for use with Safari.
**No Solution Exists**: It is our expectation that for users that must deploy
Talk in environments that must run the websockets out of a separate domain will
use and integrate their own auth solution. During the login process in Talk,
users submit their user credentials to an auth endpoint, and receive a token
back, or for Safari, a cookie. Aggressive defaults in Safari make it not
possible to have one domain set cookies for another domain during this process.
This results in a situation where we have no way to persist the auth credentials
for this specific situation for the time being.
---
If you are using a custom auth solution, (Which involves providing the user's
jwt token via the `auth_token` parameter on the call to
`Talk.render(... {auth_token})` and creating the
[tokenUserNotFound]({{ "/docs/plugins/server/" | absolute_url }}) hook to lookup
that user) then concerns related to cookies/localStorage are moot! As it isn't
necessary for Talk to maintain any state beyond what is passed to it via the
pym bridge.
-77
View File
@@ -1,77 +0,0 @@
---
title: Architecture Overview
permalink: /docs/architecture/
---
## Talk's Architecture
Talk consists of four distinct layers of code:
* Plugins
* Plugin API
* Core
* cli
### Plugins
Talk plugins deliver the features and functionality that can be changed or removed. Much of the default functionality is delivered by core plugins allowing developers to have control over any non-essential functionality.
### Plugin API
Talk plugins interact exclusively with the Plugin API. Maintaining this layer of separation between plugins and core allows us to consciously design the api that we want it publish to plugin authors. We can then expose just the elements of core that make sense and maintain that contract as core changes.
### Core
Talk core consists of architecture and functionality that deliver stability, security, scalability, extendability, etc... In addition, the Core contains features and functionality that is essential to the operation of Talk as a product.
Our goal is to continually extend our plugin infrastructure making the Core as pluggable as possible. Ultimately, a day may come where the Core of Talk is simply a framework for delivering a certain flavor of web applications.
### cli
Talk ships with [a cli tool]({{ "/docs/architecture/cli" | absolute_url }}) that exposes functionality to the command line. We seek to provide cli functionality for all features that could need to be accomplished programmatically or prior to the server's startup.
## Thinking about Plugins, the Plugin API and Core?
The following is a template for a thought process that may help clarify your ideas against the backdrop of Talk's architecture.
Think of a feature or capability. It could be something that's already in Talk or not. It could be something you want to build, or something you'd think would be a terrible idea. The important part here is to have something to interrogate.
```
wait(60000);
```
Now, ask these questions:
### Is it a Plugin?
Most work for Talk happens in the Plugin space. If the answers to any of these questions is Yes, then you're thinking of a Plugin.
* Does Talk's existing Plugin APIs support the thing you want to build?
* Is this something that only some users will want/need?
* Is this something that we want devs to iterate on widely?
You should [build it as a plugin]({{ "/docs/plugins/quickstart" | absolute_url }}). Feel free to explore here on your own or reach out to us. We love to advise on plugins, so please feel free to [let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code) and we will start a conversation. We will help you conceptualize, architect and promote your plugin if it is in line with our values.
### Does it need updates to the Plugin API?
If you answered yes above:
* Do I need to extend the Plugin API to support my plugin?
Often times all the functionality a plugin needs is in the Core, but the Plugin API doesn't expose it. In these cases, we seek to iteratively extend the Talk Plugin API. All Plugin API contributions from the community must begin by [let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
Note: we are stabilizing the process by which new Plugin API bindings are created, agreed upon and ultimately made part of our Plugins Contract. If you are interested in this process, please reach out to us.
### Does it require updates to the Plugin API _and_ Core?
What, if any, changes need to be made to Core so that the API can be extended?
Quite often the only things missing from Core are things like _events_, _slots_, _CSS classes_, etc... Adding these is a great way to become a Core Contributor and break new ground as a Plugin Developer.
We seek to keep Core as lean as possible.
### Is my idea really just Core?
Amazing! We are always looking to extend the capabilities of Talk. We look forward to discussing what you've got to bring!
Please see our [contributing guide]({{ "/docs/development/contributing" | absolute_url }}) for more information.
-68
View File
@@ -1,68 +0,0 @@
---
title: Tags
permalink: /docs/architecture/tags/
---
Tags are essentially strings that can be added to models. Currently, tags can be added to [Users, Comments and Assets](https://github.com/coralproject/talk/blob/ced449a1489d47c25d604020fa2e0b3b7a741353/graph/typeDefs.graphql#L144). If you would like to add tags to other models, you can extend this schema using [GraphQL hooks]({{ "/docs/plugins/server" | absolute_url }}#graphql-hooks).
## Tag Definitions
When handling tags, the Talk Server references a set of definitions that describe how tags are handled. These definitions are keyed off the tag `name`, the simple string that is stored on items.
The schema for Tag definitions [can be found here](https://github.com/coralproject/talk/blob/3545bf01cd91044fdb738d337a0ac94d9f71fbc3/models/schema/tag.js).
Note that along with the `name`, tag definitions contains:
* `permissions` information about who can see and set the tag,
* `models` which `ITEM_TYPES` this tag can be applied to, and
Whenever a tag is 'handled' by the server, it references this definition to determine that tag's behavior.
See [Plugin API Documentation]({{ "/docs/plugins/server" | absolute_url }}#field-tags) for more information.
### Creating a Tag Definition
Tag Definitions must be created in order for the system to determine what tags are permitted on the server side.
Tag Definitions do not contain any logic themselves but provide information that other parts of the system can use to specify which models a tag can be applied to (models) and perform authorization logic (permissions).
Take the tag created by `talk-plugin-offtopic` as an example.
```
// talk-plugin-offtopic/index.js
module.exports = {
tags: [
{
name: 'OFF_TOPIC',
permissions: {
public: true,
self: true,
roles: []
},
models: ['COMMENTS'],
created_at: new Date()
}
]
};
```
This plugin allows users to self-report that their comment is "off topic" at the time of creation, then display a badge on those comments.
To accomplish this, the plugin creates the tag `OFF_TOPIC` with:
* `permissions.public: true` - will be sent over the wire to the client side
* `permissions.self: true` - can be added by the active user to themselves or assets they own
* `permissions.roles: []` - cannot be added by anyone based on their roles
* `models: ['COMMENTS']` - can only be added to COMMENTS (not to users/assets/etc...)
And [viola](https://youtu.be/Q0O9gFf-tiI?t=23s)! This tag is something that can only be created by the logged in user on their own comments and is sent over the wire to the client so it can display the badge.
## Tag Links
When tags are stored on objects in the database, they are represented by [TagLinks](https://github.com/coralproject/talk/blob/master/models/schema/tag_link.js).
A TagLinks says that `tag` was `assigned_by` a specific user at a specific time (`created_at`).
Note that the `tag` field in the TagLinkSchema is the full TagSchema itself. This allows for another level of flexibility. Server code may generate Tags on the fly, complete with programmatically generated permissions and item behaviors.
If a Tag definitions exists in the global/asset context then that definition will be used regardless of what is stored here. This allows high level controls on the behavior of tags, ensuring that plugins cannot produce unexpected definitions for already defined tags.
-89
View File
@@ -1,89 +0,0 @@
---
title: Metadata
permalink: /docs/architecture/metadata/
---
_Metadata_ allows you to add fields to models that are not represented in the core schema.
## Goals
The metadata api is designed to satisfy two product goals:
* Give developers flexibility in extending datatypes.
* Protect core fields that are essential to Talk's operation.
## Design
Metadata is represented by an [subdocument in our Schemas](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/models/comment.js#L74). This takes advantage of Mongo's flexibility allowing for any data to be stored therein.
### Setting Metadata
Talk provides [a service layer](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/services/metadata.js) allowing developers to `set` and `unset` metadata on objects in a way similar to key-value stores.
Let's say that I want to add a custom field called `potency` to a comment.
```
const MetadataService = require('services/metadata');
const CommentModel = require('models/comment');
// Sets the property `potency` on the comment with `id=1`.
MetadataService.set(CommentModel, '1', 'potency', 42);
```
Note that the model passed here is the Model itself and not an individual comment object. This allows us to update the value on that document [in an atomic manner](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/services/metadata.js#L60) for efficiency and to prevent race conditions.
### Accessing Metadata
The metadata api does not contain a `get` method. The metadata object is retrieved via database queries along with the rest of the data.
## Metadata and the Graph
One of the first principles of GraphQL is that the shape of the graph does not need to be the same as the shape of the data in the database. In fact, it probably shouldn't be.
This enables us to treat metadata fields in any way that makes sense as we design our Graph. The fact that a value is stored in the metadata object is an implementation detail invisible to the front end.
Take for example, the `reason` field in the `FlagAction` type. This stores the user provided reason why they flagged a comment. As far as the front end knows, it's [just another field](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/graph/typeDefs.graphql#L453) alongside the core fields:
```
# graph/typeDefs.graphql
type FlagAction implements Action {
...
# The reason for which the Flag Action was created.
reason: String
...
}
```
If, however, we [look at the resolver](https://github.com/coralproject/talk/blob/a47e2378e96f34f25447782f3e7ce59fa48ec791/graph/resolvers/dont_agree_action.js) for that field, we see that `reason` is [destructured](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) from the metadata object and returned.
```
// graph/resolvers/dont_agree_action.js
const DontAgreeAction = {
// Stored in the metadata, extract and return.
reason({metadata: {reason}}) {
return reason;
}
};
module.exports = DontAgreeAction;
```
This is an extremely powerful pattern as it allows us absolute freedom in designing our graph and complete isolation of the added fields in the database.
## Some things to keep in mind
### Namespace your metadata fields
Since metadata can be added by the core and multiple plugins, collisions may occur. As you create your plugins, please be careful to pick unique names for metadata fields. We recommend namespacing all your fields in a subdocument named after your plugin.
```
[model].metadata.[your_plugin_name].[the_field]
```
### Querying by metadata fields
We currently do not have a clean way to index metadata fields. As a result queries that match against metadata fields will not scale. If you have a need to match, sort, etc... by a metadata field, [please let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
-136
View File
@@ -1,136 +0,0 @@
---
title: Command Line Interface (cli)
permalink: /docs/architecture/cli/
---
Talk ships with a cli tool that allows access to a wide variety of functionality.
It is designed to provide a convenient way for engineers to perform key tasks without the need to muck about in the UI. It also opens the door for scripts to perform operations programmatically.
Note: the cli tool requires [the Talk environment to be configured]({{ "/docs/running/configuration" | absolute_url }}) either via env vars or by using a `.cli` file via `bin/cli -c .env [command] ....`
## Using the cli
In a terminal, try:
```
/path/to/talk/bin cli [options] [commands] [arguments]
```
Commonly, you'll be in the `talk/` folder, in which case you can:
```
bin/cli [options] [commands] [arguments]
```
If you're a heavy cli user, consider adding the `bin` folder to your PATH so you can run `cli` from anywhere!
If you are using [our Docker environment]({{ "/docs/install/docker" | absolute_url }}), the bin folder will already be in the PATH.
## What can I do with the cli?
The Talk cli ships with 'unix style' help. To access the docs, simply run the cli with insufficient arguments.
Let's say I wanted to figure out how to change a user's password. I'd start be seeing what the cli has for me.
(Note: the following output may change, please reference at the `--help` output for your version as you use the cli.)
```
talk :) ]$ bin/cli --help
Usage: cli [options] [command]
Commands:
serve serve the application
settings interact with the application settings
assets interact with assets
setup setup the application
jobs work with the job queues
token work with the access tokens
users work with the application auth
migration provides utilities for migrating the database
plugins provides utilities for interacting with the plugin system
help [cmd] display help for [cmd]
Options:
-h, --help output usage information
-V, --version output the version number
-c, --config [path] Specify the configuration file to load
--pid [path] Specify a path to output the current PID to
```
Most commands contain sub-commands. Running with cli with such a command generates the docs for the sub-commands and options therein.
Change user password is likely to be in the `users` command group.
```
talk :) ]$ bin/cli users
Usage: cli-users [options] [command]
Commands:
create [options] create a new user
delete <userID> delete a user
passwd <userID> change a password for a user
update [options] <userID> update a user
list list all the users in the database
merge <dstUserID> <srcUserID> merge srcUser into the dstUser
addrole <userID> <role> adds a role to a given user
removerole <userID> <role> removes a role from a given user
ban <userID> ban a given user
uban <userID> unban a given user
disable <userID> disable a given user from logging in
enable <userID> enable a given user from logging in
Options:
-h, --help output usage information
-V, --version output the version number
-c, --config [path] Specify the configuration file to load
--pid [path] Specify a path to output the current PID to
```
I now see that I can change a password like so:
```
bin/cli users passwd [userID]
```
You can also read these help prompts by [exploring the source code](https://github.com/coralproject/talk/blob/master/bin/cli).
### Usage Notes
If you haven't used a cli enabled system before, think of it like this: generally, you'd make a rest call, rpc, etc... to perform an action. The cli's api is designed in the same way, just for the audience of command line wielding engineers and scripts.
The best way to understand what the cli does is to explore the help commands. Uses of cli are also scattered through this documentation in context of their topics.
For some real world uses of the cli, see the scripts in the [package.json file](https://github.com/coralproject/talk/blob/d688f70c19d8dee48371784009fd07322dae4eb5/package.json#L8).
## What's really going on when I run the cli?
The cli tool is a standalone application. Running it starts up the internals of a talk process, executes the given command, then shuts it down. No server functionality is enabled by cli commands unless specifically noted.
The cli tool _does not require a talk server to be running._ This means that you can execute commands, for example, during and installation process before starting the server. The also means that you can execute commands using varying configurations (via the `-c [.env file]` flag).
### Accessing existing Talk installs with the cli
You may use the cli tool to 'remotely' control existing talk installs.
This is accomplished by running the cli tool on any box using the mongo/redis/etc... credentials for the environment that you would like to act on. For example, if you want something to happen periodically on your production Talk install, you could set up a utility box with a cron job that triggers the cli with the same db/cache credentials. If you want to do something quick on a staging server, you could run the cli locally with staging credentials.
The cli tool will connect directly with the install's db and redis instance(s) so ensure that your box can reach those servers on the appropriate ports.
Also, _please ensure your cli and the server(s) in an environment are using the same version of Talk._
Please secure your environments and credentials or the cli tool becomes a convenient way for someone to own your system.
## Extending the cli
The Talk cli is based on the excellent [commander](https://github.com/tj/commander.js/) library.
At the time of writing, there are no plugin hooks for the cli tool. If you would like to change this, whether by writing code yourself or recommending a need, please [write and issue]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
-117
View File
@@ -1,117 +0,0 @@
---
title: Client Architecture
permalink: /docs/architecture/client
---
## The Stack
- [React](#react)
- [Redux](#redux)
## The Architecture
Our frontend lives within [talk/client](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client) folder. Every folder contains a plugin. In [coral-framework](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework) you will find the core architecture of Talk.
Here is where our Redux Application, translations, components, and helpers live.
## Presentational and Container Components
We use a common simple pattern called
__Presentational and Container Components__
It basically consist in having two types of components:
- Presentational
- Containers
### Presentational Components
- __How our UI looks like__
- Are stateless components
- Render props
- Allow containment of children via `this.props.children`
- They have DOM Markup
### Container Components
* __How things work__
* They dont have markup nor styles
* They provide data and behavior to Presentational or Container Components
* They connect via `react-redux`s `connect()` to the state.
* They `mapStateToProps` the state to the Presentational Container.
* They `mapDispatchToProps` to send actions to the Presentational Container.
* Name Convention `<Name>Container.js`
How a container looks like:
```js
/*
* mapStateToProps
* We map the part of the state that we want to use
*/
const mapStateToProps = state => ({
auth: state.auth.toJS()
});
/*
* mapDispatchToProps
* We map the actions that we want to use
*/
const mapDispatchToProps = dispatch => ({
checkLogin: () => dispatch(checkLogin())
});
/*
* connect
* We wrap our container in a connect() function
*/
export default connect(
mapStateToProps,
mapDispatchToProps
)(SignInContainer);
````
How our SignInContainer works: [talk/SignInContainer.js · GitHub](https://github.com/coralproject/talk/blob/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-sign-in/containers/SignInContainer.js)
Within our plugins we create two folders `containers` and `components` so we can differentiate them:
```
coral-sign-in/
├── containers/
│ └── SignInContainer.js
└── components/
├── SignInContent.js
└── SignUpContent.js
```
More about this architecture:
[Container Components Learn React with chantastic Medium](https://medium.com/@learnreact/container-components-c0e67432e005#.w8mzgndcg)
[Presentational and Container Components Dan Abramov Medium](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.ai4ih55v3)
## GraphQL
We use [Apollo](http://www.apollodata.com/) to handle graph requests and handle state.
## Redux
We use [Redux](http://redux.js.org/) to handle the auth state.
## Test
[How we do testing at Coral with Talk]({{ "/docs/development/tools" | absolute_url }})
## Lint
For linting in Talk we use `eslint:recommended`
You can find more info about the rules and best practices here:
http://eslint.org/docs/rules/#best-practices
## Lint the code
```js
yarn lint
```
## The Future of the Frontend
- [Preact](https://github.com/developit/preact)
- [Reselect](https://github.com/reactjs/reselect)
-241
View File
@@ -1,241 +0,0 @@
---
title: Plugins Quickstart
permalink: /docs/plugins/quickstart/
---
This tutorial walks through the mechanics of creating and publishing a Talk plugin. Along the way I call out some particular habits and techniques that I employ. If you have other practices that you find valuable, please don't hesitate to [contribute!]({{ "/docs/faq" | absolute_url }}#how-do-i-contribute-to-these-docs)
We will create a plugin that exposes a route that allows assets to be created or updated.
## Setup the environment
Before I begin working on the plugin, I've installed [Talk from source]({{ "/docs/development/tools" | absolute_url }}).
### Watch the Server
In a terminal, I run `yarn dev-start`. This:
* starts my server, showing plugin and configuration information
* restarts it when I save files
* shows my temporary `console.log()` statements
* shows real time access logs
* shows verbose debug output if enabled (more on this later)
### Watch the Client build process
In a separate terminal I run `yarn build-watch`. This:
* builds the client side javascript bundles
* watches relevant files and rebuilds the bundle on change
* displays _compile time_ errors, including (the many) syntax errors I cause
If you need to run `yarn install`, you will see missing module error messages here.
### Watch from the Browser
I open up `http://localhost:3000` in a web browser and see the default comment stream. I then open the dev tools console, which:
* shows any _run time_ errors/warnings generated on the front end.
* shows any temporary `console.log()` statements I add during development.
I also often toggle to the Network Tab to see:
* which files are being loaded
* requests sent from my front end code, including headers, the payload/queries sent and the data returned
I strongly recommend taking the time to fully explore all the features of your browser's dev tools!
## Create a home for my new plugin
My goals for this tutorial are to:
* build this plugin locally
* use source control and publish for collaboration
* publish the plugin as an npm library
### Create a repo
I create a new repo called `talk-plugin-asset-manager`. (I use github, but this you could store this anywhere, bitbucket, svn, etc...)
_make sure to respect the naming convention `talk-plugin-*`. This will allow for easy identification of the repo and, eventually, easy searching on npm._
### Set up a local file structure
In a blatant rip off from the Golang community, I create an environment var to hold the path to the root of my Coral directory. This allows absolute pathing.
```
export CORALPATH=/path/to/my/coral/root/dir
```
I like to put my plugins in a directory next to talk, but you could put this anywhere.
```
cd $CORALPATH
git clone https://github.com/jde/talk-plugin-asset-manager.git
```
### Register your plugin
Add the plugin to the plugins.json file:
```json
{
"server": [
...
"talk-plugin-asset-manager"
],
"client": [
...
// no client side components so I won't add it here
]
}
```
But wait! Talk looks in `talk/plugins/[plugin-name]` for plugin code. Why couldn't we just add that plugin there?
We could have.
This would make it _a little_ easier to register, but _a lot_ harder to cleanly manage in version control. In order to avoid it being sucked into your Talk repo, you would have to manually `.gitignore` it or use sub modules or something similar.
As a user of a Linux_y_ os, I prefer to create a symbolic link.
```bash
cd $CORALPATH/talk/plugins
ln -s $CORALPATH/talk-plugin-asset-manager
```
Now, as far as Talk knows, our plugin is right there in the folder. Git is wise, however, and will not include it in the Talk repo. Best of all, our `yarn dev-start` based watch statement follows symbolic links and will restart our sever each time a file is saved.
### Create the initial index file
All plugins contain server and/or client index files, which export all plugin functionality.
```js
// talk-plugin-asset-manager/index.js
module.exports = {};
```
## Build the feature!
Now that the plugin is set up I can get down to writing the feature. My goal is to allow my CMS to push new assets as they are created into Talk. To accomplish this, I will create a POST endpoint using Talk's [route api]({{ "/docs/plugins/server" | absolute_url }}#field-router).
### Create a route
When designing my api, I want to be careful to avoid conflicts with not only the existing Talk api, but other plugins in the open source ecosystem that may be creating routes. To do this, I'll follow the golden rule of creating universals with plugins:
_Always namespace all universals with your plugin's unique name._
To ensure everything is hooked up, I'll log the request body (POST payload in this case) to the console and echo it as the response:
```js
// talk-plugin-asset-manager/index.js
module.exports = {
router(router) {
router.post('/api/v1/asset-manager', (req, res) => {
console.log(req.body);
res.json(req.body);
});
}
}
```
When I save this file, I reflexively check my console to be sure that the server restarts.
To test that this works, I can:
```bash
$ curl -H "Content-Type: application/json" -X POST -d '{"url":"http://localhost:3000/my-article","title":"My Article"}' http://localhost:3000/api/v1/asset-manager
{"url":"http://localhost:3000/my-article","title":"My Article"}
```
After hitting the endpoint, I can also look at the terminal running `yarn dev-start` and see my `console.log()` and the access log:
```
{ url: 'http://localhost:3000/my-article',
title: 'My Article' }
POST /api/v1/asset-manager 200 1.379 ms - 68
```
### Save the asset
When I save this asset, I will use Talk's [asset model](https://github.com/coralproject/talk/blob/master/models/asset.js).
Mongo has a handy method [findOneAndUpdate](https://docs.mongodb.com/v3.2/reference/method/db.collection.findOneAndUpdate/) that will take care determining whether or not this asset exists, then either updating or inserting it. Whenever possible, we recommend using these atomic patterns that prevent multiple queries to the db and the efficiency problems and race conditions that they cause.
```js
// talk-plugin-asset-manager/index.js
const AssetModel = require('models/asset');
module.exports = {
router(router) {
router.post('/api/v1/asset-manager', (req, res) => {
const asset = req.body;
const update = {$setOnInsert: {url: asset.url}};
AssetModel.findOneAndUpdate(asset, update, {
new: true,
upsert: true,
setDefaultsOnInsert: true
})
.then((asset) => res.json(asset));
});
}
}
```
I can now run the `curl` command as before and see that the response contains a complete asset document!
In addition, I can change the title in the json payload and verify that the id is the same, indicating that the record was updated!
Lastly, I can see the asset in the admin panel at `http://localhost:3000/admin` as well as in my database.
We have an alpha version of our plugin!
### More work to be done
The purpose of this tutorial is to follow the full lifecycle of a plugin, from conception through publication into deployment. With that in mind we'll move forward with this alpha version.
Some things to make this production ready:
* refactoring to separate concerns
* commenting
* adding tests
* validating data
* [adding security](https://github.com/coralproject/talk/blob/b805451d376d2892c81c58d8822a85563e469b88/routes/api/users/index.js#L14)
* incorporating [domain whitelisting](https://github.com/coralproject/talk/blob/b805451d376d2892c81c58d8822a85563e469b88/services/assets.js#L60).
It is important to realize that when you're writing a Talk plugin you are writing a program that may be touched by other devs and could grow in size and complexity. Bring your best engineering sensibilities to bear.
## Publishing the plugin
### Publish to npm
In order to [register]({{ "/docs/plugins" | absolute_url }}#plugin-registration) your _published_ plugin, you will need to [publish it to npm](https://docs.npmjs.com/getting-started/publishing-npm-packages).
Once the package is published, update `plugins.json` to use the published plugin:
```json
{
"server": [
// ...
{"talk-plugin-asset-manager": "^0.1"}
],
// ...
}
```
Finally, run the `reconcile` script to install the plugin from npm.
```bash
$ bin/cli plugins reconcile
```
Once you've taken this step, anyone can register your plugin into their Talk server! Thank you for contributing to the open source community!
### Publish to version control
This plugin is open source, so I'm also going to [publish it to github](https://github.com/jde/talk-plugin-asset-manager/commit/66b626caa85cb8030b3ddaa7c1a4821bf01e350a) and [cut a release](https://github.com/jde/talk-plugin-asset-manager/releases/tag/v0.1) that mirrors the npm release.
## Done!
-331
View File
@@ -1,331 +0,0 @@
---
title: Client Plugin API
permalink: /docs/plugins/client/
---
{% include toc title="API" %}
## Overview
We can build plugins to extend the client side functionality of Talk.
The ultimate goal of our client side plugin architecture is to allow developers
to build on existing concepts without needing to understand core code while
providing complete power and flexibility of javascript, css and html.
* [Plugin Architecture](#plugin-architecture)
* Using our building block components
* [Reactions](#reactions)
* [Styling](#styling-plugins)
Advanced users will quickly realize that our plugins have complete access to core code. If you would like to write advanced plugins that reach outside of our published API as described in this document, please see [our notes on experimental plugins]({{ "/docs/plugins/experimental/" | absolute_url }}).
Under the hood our plugins are powered by *React*, *Redux* and *GraphQL*. We can also build them with simple vanilla javascript.
## Plugin Architecture
The plugins live in the `/plugins` folder. Each plugin must have an `index.js` file and two folders `client` and `server`.
### The Client Folder
The frontend of our plugin lives inside the `client` folder. The `client` folder must have an `index.js` file that exports the configuration of our plugin.
```
my-plugin/
├── client/
│ └── index.js <-- index for client side functionality
├── server/
└── index.js <-- base plugin index
```
For now our base plugin `index.js` file should look like this:
```js
export default {
// We will add more here later.
};
```
### Creating a Component
We can add our components (or any other javascript code) within the `client` folder.
```
my-plugin/
├── client/
│ ├── MyComponent.js
│ └── index.js
├── server/
└── index.js
```
Our component could look like this:
```js
import React, {Component} from 'react';
class MyButton extends Component {
render() {
return <button>My Button</button>;
}
}
export default MyButton;
```
Here we create a component that renders a `button`. Now that we created our component we need to specify where it should get injected within Talk!
To tell Talk where that Component should get injected we need to specify which *Slots* to insert it into.
```js
import React from 'react';
export default = () => <button>My Button</button>;
```
### Slots
In Talk we have defined specific *Slots* where we can inject components.
Here is how we specify our slots config in `my-plugin/index.js`
```js
import MyButton from './MyButton';
export default {
slots: {
commentDetail: [MyButton]
}
};
```
Here Im specifying that the MyComponent Component will take place within the `commentDetail` in Talk.
`commentDetail` its a specific slot in the CommentStream. It means that it will be embedded inside de comment detail.
Slots properties take an`Array` so we can add as many components as we want.
## Building Blocks (TBD)
`Note: the concepts in this section are still to be implemented. Code samples are for discussion and may change.`
In order to allow you to build more complex plugins, we have wrapped some of our functionality in higher order components that expose a simple api.
## Reactions
Reactions provide users the ability to 'like', 'respect', etc... comments.
Note: some server side work will need to accompany this client side component. See the like and respect plugins as examples.
### Building Reactions
#### Our `client/index.js` :
```js
import LoveButton from './LoveButton';
export default {
slots: {
commentReactions: [LoveButton]
}
};
```
In this example we add our reaction component to the `commentReaction` Slot
#### Our Reaction component:
```js
import React from 'react';
import {withReaction} from 'talk-plugin-api';
class LoveButton extends React.Component {
handleClick = () => {
const {
postReaction,
deleteReaction,
alreadyReacted
} = this.props;
if (alreadyReacted()) {
deleteReaction();
} else {
postReaction();
}
};
render() {
const {count} = this.props;
return (
<button onClick={this.handleClick}>
<span>Love</span>
<span>{count > 0 && count}</span>
</button>
);
}
}
export default withReaction('love')(LoveButton);
```
This feature introduces `withReaction` HOC. `withReaction` takes, as argument, a reaction string and it allows our component to receive specific props for handling reactions.
* `postReaction` - Posts the reaction
* `deleteReaction` - Removes the reaction
* `alreadyReacted` - A function that returns a boolean.
* `count` - The reaction count
For full reference: Please, check `talk-plugin-love`: [LoveButton.js](https://github.com/coralproject/talk/blob/master/plugins/talk-plugin-love/client/LoveButton.js)
### Comment Stream
Comment streams may be created with filtering and ordering in place:
* filter by user
* filter by tag
* sort by date ascending / descending
### Comment Commit hooks
// docs for the pre/post comment submit commit hooks
### Mod Queues
Moderation queues can be added via configuration objects passed in through plugins.
Basic mod queues will resemble the current moderation queues but can be generated from different lists of comments.
* filter by user tag
* filter by comment tag
* filter by comment status
* Custom queries (paired with back end plugins that provide queries to get the data)
#### Advanced mod queues
Advanced mod queues can be created giving plugin authors the power to create the cards that appear in the queue, create actions and custom buttons, etc...
### Custom Configuration
Plugins may rely on configuration options that admins/moderators can set in the Configuration section.
Basic settings can be added via json configuration in a plugin.
* Setting headline
* Setting description
* Setting input type
* Default value
* Variable name
#### Advanced Custom Configuration (low priority)
Users can inject configuration interfaces that they create into the configuration allowing for more advanced configuration.
## Styling Plugins
Talk uses CSS Modules. This basically means that you can also add your CSS Module to your plugin without colliding with the rest of Talk!
##### My Component
```js
import styles from './style.css';
class MyCoralButton extends Component {
render() {
return <button className={styles.button}>My Button</button>;
}
}
````
Our `style.css` should could look like this.
```css
.button {
background: coral;
border-radius: 3px;
}
```
## Plugin Hooks
The plugins injected in the CommentBox such as `commentInputDetailArea` will inherit through props tools for handling hooks.
### Available hook types:
`preSubmit` : To perform actions before submitting the comment.
`postSubmit` : To perform actions after submitting the comment.
### Register Hooks
`registerHook` is a function that takes: the hook type, a hook function and returns the hook data.
#### Usage:
```js
this.addCommentTagHook = this.props.registerHook('postSubmit', (data) => {
const {comment} = data.createComment;
this.props.addCommentTag({
id: comment.id,
tag: 'OFF_TOPIC'
});
});
```
### Unregister Hooks
`unregisterHook` will remove the hook.
```js
this.props.unregisterHook(this.addCommentTagHook);
```
### The server folder and the index file
Read more about the `/server` and how to extend Talk here.
[Server API]({{ "/docs/plugins/server/" | absolute_url }})
## ESlint and Babel
In talk we use `eslint:recommended` and Babel with the latest ECMAScript Features. But you can use your own!
While building your plugin you need to specify a `.eslintrc.json` file and a`.babelrc` file.
### `.eslintrc.json`
```json
{
"env": {
"browser": true,
"es6": true,
"mocha": true
},
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
}
},
"parser": "babel-eslint",
"plugins": [
"react"
],
"rules": {
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
}
```
### `.babelrc `
```json
{
"presets": [
"es2015"
],
"plugins": [
"add-module-exports",
"transform-class-properties",
"transform-decorators-legacy",
"transform-object-assign",
"transform-object-rest-spread",
"transform-async-to-generator",
"transform-react-jsx"
]
}
```
-494
View File
@@ -1,494 +0,0 @@
---
title: Server Plugin API
permalink: /docs/plugins/server/
---
{% include toc title="API" %}
## The Server Folder
The server functionality of our plugin lives inside the `server` folder. The `server`
folder must have an `index.js` file that exports the configuration of our plugin.
```
my-plugin/
├── client/
│ └── ... <-- client side plugin files
├── server/
│ └── index.js <-- index for server side functionality
│ └── ... <-- other server side plugin files
└── index.js <-- base plugin index
```
## Specification
Each plugin should export a single object with all hooks available on it.
_**Note: You will have access to the whole core and other plugin's typeDefs,
context, loaders, mutators, resolvers, hooks. This is intentional, as it
encourages composing plugins to merge functionality, like a Slack plugin which
provides a Slack notify context function as well as having the loader for
comments.**_
The following are the hooks available:
### GraphQL hooks
#### Field: `typeDefs`
```graphql
enum COLOUR {
RED
BLUE
}
type Person {
name: String!
colour: COLOUR!
}
type RootMutation {
createPerson(name: String!): Person
}
type RootQuery {
people: [Person!]
}
type Subscription {
leader: Person
}
```
Thanks to [gql-merge](https://www.npmjs.com/package/gql-merge) the contents of
`typeDefs` should be a string that will be _merged_ with the existing type
definitions. `enum`'s will be appended to, types will be appended, and new types
will be added.
#### Field: `context`
```js
{
Slack: (context) => ({
notify: (message) => {
// return a promise after we're done sending notifications.
}
})
}
```
Any property provided here will be added to the context parameter available
inside all resolvers, loaders, mutators, and of course, other context based
plugins.
The top level item must accept a context for the request which it should use to
configure the context plugin before it would be mounted at `context.plugins`.
This plugin above would mount at: `context.plugins.Slack`, or, if you're using
[object destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), `{plugins: {Slack}}`.
##### Field: `Sort`
A special context hook, `Sort` will allow plugin authors to provide new
methods to sort data. An example is as follows:
```js
{
Sort: () => ({
Comments: { // <-- (1)
likes: { // <-- (2)
startCursor(ctx, nodes, {cursor}) { // <-- (3)
return cursor != null ? cursor : 0;
},
endCursor(ctx, nodes, {cursor}) { // <-- (4)
return nodes.length ? (cursor != null ? cursor : 0) + nodes.length : null;
},
sort(ctx, query, {cursor, sort}) { // <-- (5)
if (cursor) {
query = query.skip(cursor);
}
return query.sort({
'action_counts.like': sort === 'DESC' ? -1 : 1,
created_at: sort === 'DESC' ? -1 : 1,
});
},
},
},
}),
}
```
This has a bunch of special features:
1. `Comments` is the name of the type being sorted, this is pluralized and
capitalized.
2. `likes` is the `sortBy` field in lowercase.
3. `startCursor` will retrieve the start cursor based on the current set of
nodes and the current cursor.
4. `endCursor` will retrieve the end cursor based on the current set of nodes
and the current cursor.
5. `sort` will mutate the `query` to apply the sort operations.
All the `startCursor`, `endCursor`, and `sort` functions must be provided in
order for the sorting to apply properly.
#### Field: `loaders`
```js
(context) => ({
People: {
load: () => db.people.find({user: context.user})
}
})
```
Loaders should be provided as a function which returns a map which is used in
the resolvers function. These must return a promise or a value.
#### Field: `mutators`
```js
(context) => ({
People: {
create: (name) => {
return db.people.insert({user: context.user, name});
}
}
})
```
Mutators should be provided as a function which returns a map which is used in
the resolvers function. These must return a promise or a value.
#### Field: `resolvers`
```js
{
Person: {
name(obj, args, context) {
return obj.name;
},
colour(obj, args, context) {
// Bill likes the colour red, everyone else likes blue.
return obj.name === 'bill' ? 'RED' : 'BLUE';
}
},
RootQuery: {
people(obj, args, {loaders: {People}}) {
return People.load();
}
},
RootMutation: {
createPerson(obj, {name}, {mutators: {People}}) {
return People.create(name);
}
}
}
```
Should return a resolver map as described in the
[Apollo Docs](http://dev.apollodata.com/tools/graphql-tools/resolvers#Resolver-map).
This will merge with the existing resolvers in core and from previous plugins.
#### Field: `hooks`
```js
{
RootMutation: {
createPerson: {
post: async (obj, args, {plugins: {Slack}}, info, person) {
if (!person) {
return person;
}
await Slack.notify(`A new person just was created with name ${person.name}`);
return person;
}
}
}
}
```
Hooks here are pretty special, for each resolver field, you can specify a
pre/post hook that will execute pre and post field resolution.
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: `setupFunctions`
```js
setupFunctions: {
leader: (options, args) => ({
leader: {
filter: (person) => person.place === 1
},
}),
}
```
Setup functions allow you to create filters that control which pubsub.publish() events
send data to the client. If the type in question contains args, clients may subscribe using those arguments to further filter their subscription.
For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions).
#### Field: `tokenUserNotFound`
```js
tokenUserNotFound: async ({jwt, token}) => {
let profile = await someExternalService(token);
if (!profile) {
return null;
}
let user = await UserModel.findOneAndUpdate({
id: profile.id
}, {
id: profile.id,
username: profile.username,
lowercaseUsername: profile.username.toLowerCase(),
roles: [],
profiles: []
}, {
setDefaultsOnInsert: true,
new: true,
upsert: true
});
return user;
}
```
The `tokenUserNotFound` hook allows auth integrations to hook into the event
when a valid token is provided but a user can't be found in the database that
matches the provided id.
The function is async, and should return the user object that was created in the
database, or null if the user wasn't found. The `jwt` parameter of the object
is the unpacked token, while `token` is the original jwt token string.
#### Field: `tags`
The tags hook allows a plugin to define tags that are code controlled (added
or enabled by code). Below is an example pulled from the core off topic plugin
on how to create a hook for the `OFF_TOPIC` name:
```js
[
{
name: 'OFF_TOPIC',
permissions: {
public: true,
self: true,
roles: []
},
models: ['COMMENTS'],
created_at: new Date()
}
]
```
You can refer to `models/schema/tag.js` for the available schema to match when
creating models to enable/disable specific features.
### Routes
#### 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.
### Authorization middleware
The following example creates the requisite callback route and passport
strategy needed to enable Facebook Authorization:
```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 immediately 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 successful 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);
});
}
};
```
## Full Example
Contents of `plugins.json`:
```json
{
"server": [
"people"
]
}
```
Located in `plugins/people/index.js`:
```js
module.exports = {
typeDefs: `
enum COLOUR {
RED
BLUE
}
type Person {
name: String!
colour: COLOUR!
}
type RootMutation {
createPerson(name: String!): Person
}
type RootQuery {
people: [Person!]
}
type Subscription {
leader: Person
}
`,
context: {
Slack: () => ({
notify: (message) => {
// return a promise after we're done sending notifications.
}
})
},
loaders: ({user}) => ({
People: {
load: () => db.people.find({user})
}
}),
mutators: ({user}) => ({
People: {
create: (name) => {
return db.people.insert({user, name});
}
}
}),
resolvers: {
Person: {
name(obj, args, context) {
return obj.name;
},
colour(obj, args, context) {
// Bill likes the colour red, everyone else likes blue.
return obj.name === 'bill' ? 'RED' : 'BLUE';
}
},
RootQuery: {
people(obj, args, {loaders: {People}}) {
return People.load();
}
},
RootMutation: {
createPerson(obj, {name}, {mutators: {People}}) {
return People.create(name);
}
}
},
hooks: {
RootMutation: {
createPerson: {
post: async (obj, args, {plugins: {Slack}}, info, person) => {
if (!person) {
return person;
}
await Slack.notify(`A new person just was created with name ${person.name}`);
return person;
}
}
}
},
setupFunctions: {
leader: (options, args) => ({
leader: {
filter: (person) => person.place === 1
}
}
}
};
```
## API
You can access any API available inside the talk directory in a plugin by simply
importing the file relative to the talk project root. An example would be if you
wanted to import the `MetadataService`, you would simply write:
```javascript
const MetadataService = require('services/metadata');
```
-60
View File
@@ -1,60 +0,0 @@
---
title: Experimental Plugin API
permalink: /docs/plugins/experimental/
---
Talk plugins are, in essence, small programs that hook into the core application in a variety of ways. Ultimately, this code can do anything that javascript is capable of. In addition, plugins can import any core code to hook into talk at any level.
If you want to write plugins that integrate with core code beyond the api described
in the client api or server api section, please keep the following things in mind:
* core code may change and break your plugin
* you may introduce inefficiencies with your plugin that could hurt performance/
crash Talk
* you may cause bugs in other areas of Talk
If you'd like to build a supported plugin but don't have the hooks you need,
please file an issue on this repo and we can discuss deepening the supported
plugin api!
With that said, if you are still undeterred, here are some of the prime
experimental integration points:
## Reducers and Actions : Redux
Talk is powered by Redux and our plugins can too! Our plugins can have their own reducers and actions.
```js
import MyButton from './MyButton';
import reducer from './reducer';
export default {
slots: {
commentDetail: [MyButton],
},
reducer
};
```
## Import Actions from Talk
We can easily trigger `Talk` actions in our plugin Components.
```js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {addTag, removeTag} from 'talk-plugin-commentbox/actions';
class MyButton extends Component {
render() {
return <button onClick={this.props.addTag('MY_TAG')}>My Button</button>;
}
}
const mapStateToProps = ({commentBox}) => ({commentBox});
const mapDispatchToProps = dispatch =>
bindActionCreators({addTag, removeTag}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox);
```
-33
View File
@@ -1,33 +0,0 @@
---
title: Development Tooling
permalink: /docs/development/tools/
---
## Debugging
How we debug errors at Coral
### React Debugging
For debugging React
#### React Developer Tools
Another amazing tool for debugging React Applications. You can see where the props are, and much more.
[React Developer Tools Extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)
### Redux Debugging
For debugging Redux
#### Redux Devtool Extension
Redux Devtool is an amazing debug tool. You can easily see what' happening with the state, the payloads, and more.
[Redux Devtool Chrome Extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en)
[Redux Devtool GitHub Repo](https://github.com/zalmoxisus/redux-devtools-extension)
### GraphQL
Talk ships with GraphiQL, a live data layer IDE. We strongly recommend using GraphiQL early and often as you work with all things queries, mutations and subscriptions!
* [Install Talk]({{ "/docs/install/source" | absolute_url }}).
* Open http://localhost:3000/api/v1/graph/iql in your browser. (Note, your server an port may differ.)
-87
View File
@@ -1,87 +0,0 @@
---
title: Contributor's Guide
permalink: /docs/development/contributing/
---
Welcome! We are very excited that you are interested in contributing to Talk.
This document is a companion to help you approach contributing. If it does not do so, please [let us know how we can improve it](https://github.com/coralproject/talk/issues)!
By contributing to this project you agree to the [Code of Conduct](https://coralproject.net/code-of-conduct).
## What should I Contribute?
There are at least three ways to contribute to Talk:
* Writing Code
* Writing Docs
* Providing Translations
## Writing Code
Conversation surrounding contributions begins in [issues](https://github.com/coralproject/talk/issues).
### When should I Create an Issue?
File an issue as soon as you have an idea of something you'd like to contribute. We would love to hear what you're thinking and help refine the idea to make it into the Talk ecosystem.
Please file issues if:
* you would like to contribute to Talk Core.
* you are building a Plugin that the current Plugin API doesn't support.
* you are building a Plugin and want advice.
### What should I include?
Coral has adopted an iterative, agile development philosophy. All contributions that make it into the Talk repo should start with a story or this form:
> As a [type of person] I'd like to be able to [do something] so that I can [get some result].
This exercise does two things:
* allows us to ground our technical choices in a clear, simple product need.
* expresses that product need in a way that doesn't imply a specific technical solution allowing for debate as to the best way to solve the problem.
Please feel free to provide as much detail as possible when filing the issue but please do keep the initial issue specific to one need and try to avoid including technical or design solutions.
If you have a specific technical or design solution in mind, please submit it as the first comment on the thread.
## Contributing Documentation
Clear docs are a prerequisite for a successful open source project. Contributing to the documentation is often more important than contributing to the code!
We are looking for _documentarians_ to:
* make clarity, grammar and completeness updates,
* create new / missing sections, and
* take the lead in making sections, or the over all structure better.
Information about how to update docs can be found in our [FAQ]({{ "/docs/faq" | absolute_url }}#how-do-i-contribute-to-these-docs).
If you'd like to discuss a contribution, please [file an issue](https://github.com/coralproject/talk/issues) describing the changes you would like to see.
## Contributing Translations
Talk's tranlations are stored in `.yml` files [here](https://github.com/coralproject/talk/tree/master/locales).
Translations can be submitted via pull request. If you do not use github, you can use 'en.yml' as a template and [email](https://coralproject.net/contact) the translations to us. We can import it into the repo.
## I want to contribute but I'm not sure what to do!
If you want to contribute but don't have a clear idea of exactly what that may be, here are some resources that may help:
### Product Roadmap
Please visit our product roadmap here: https://www.pivotaltracker.com/n/projects/1863625. If you'd like to take on any of our scheduled tasks we'd be forever grateful!
### Discussion Forum
If you'd like to discuss what we're up to, please visit or [community](https://community.coralproject.net/) or [contact us](https://coralproject.net/contact).
### Integrations
Have a favorite analytics engine? Data science service? CMS? Auth platform? Deployment platform or pipeline? Pet project? Consider building a plugin to integrate them!
### Favorite Features?
Do you have a favorite feature of an existing platform that's not yet been done in Talk? Sounds like Talk needs that feature.
-81
View File
@@ -1,81 +0,0 @@
---
title: Contributor Covenant Code of Conduct
permalink: /docs/development/code-of-conduct/
---
## Our Pledge
We expect everyone contributing to The Coral Project to follow this code of conduct. That means the team, contractors we employ, contributors, as well as anyone posting to our public or internal-facing channels.
We created it not because we anticipate any unacceptable behavior, but because we believe that articulating our values and obligations to one another reinforces the already exceptional level of respect among the team, and because having a code provides us with clear avenues to correct our culture should it ever stray from that course.
We commit to enforce and evolve this code over the duration of the project.
## Expected behavior
* Be supportive of each other.
* Be collaborative. Involve others in brainstorms, sketching sessions, code reviews, planning documents, and the like. Its not only okay to ask for help or feedback often, its unacceptable not to do so.
* Be generous and kind in both giving and accepting critique. Critique is a natural and important part of our culture. Good critiques are kind, respectful, clear, and constructive, focused on goals and requirements rather than personal preferences. You are expected to give and receive criticism with grace.
* Be humane. Be polite and friendly in all forms of communication, especially remote communication, where opportunities for misunderstanding are greater. Use sarcasm carefully. Tone is hard to decipher online; make judicious use of emoji to aid in communication.
* Be considerate.
* Be tolerant.
* Respect peoples boundaries.
* Do not make it personal.
* Use welcoming and inclusive language.
* Offer to help if you see someone struggling or otherwise in need of assistance (taking care not to be patronizing or disrespectful).
* If someone approaches you looking for help, be generous with your time; if youre under a deadline, direct them to someone else who may be of assistance.
* Go out of your way to include people in jokes or memes, recognizing that we want to build an environment free of cliques.
* Show empathy towards other community members
## Unacceptable behavior
We are committed to providing a welcoming and safe environment for people of all races, gender identities, gender expressions, sexual orientations, physical abilities, physical appearances, socioeconomic backgrounds, nationalities, ages, religions, and beliefs.
We expect that you will refrain from demeaning, discriminatory, or harassing behavior and speech.
Harassment includes, but is not limited to: deliberate intimidation; stalking; unwanted photography or recording; sustained or willful disruption of talks or other events; inappropriate physical contact; use of sexual or discriminatory imagery, comments, or jokes; and unwelcome sexual attention.
Furthermore, any behavior or language which is unwelcoming—whether or not it rises to the level of harassment—is also strongly discouraged. Much exclusionary behavior takes the form of microaggressions—subtle put-downs which may be unconsciously delivered. Regardless of intent, microaggressions can have a significant negative impact on victims and have no place on our team.
Other inappropriate behavior:
* Threats
* Slurs
* Pornography
* Spam
* Bullying
* Copyright infringement
* Impersonation of someone else
* Violating someones privacy
If you feel that someone has harassed you or otherwise treated you or someone else inappropriately, please alert the project lead at [andrewl@mozillafoundation.org](mailto:andrewl@mozillafoundation.org).
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
These guidelines are ambitious, and were not always going to succeed in meeting them. When something goes wrong—whether its a microaggression or an instance of harassment — there are a number of things you can do to address the situation. Depending on your comfort level and the severity of the situation, here are some suggestions:
* Address it directly. If youre comfortable bringing up the incident with the person who instigated it, pull them aside to discuss how it affected you. Be sure to approach these conversations in a forgiving spirit: an angry or tense conversation will not do either of you any good. If youre unsure how to go about that, try discussing with your manager or with the people and culture team first—they might have some advice about how to make this conversation happen.
If youre too frustrated to have a direct conversation, there are a number of alternate routes you can take.
* Talk to a peer or mentor. Your colleagues are likely to have personal and professional experience on which to draw that could be of use to you. If you have someone youre comfortable approaching, reach out and discuss the situation with them. They may be able to advise on how they would handle it, or direct you to someone who can. The flip side of this, of course, is that you should also be available when your colleagues reach out to you.
* Contact the project lead, Andrew Losowsky, [andrewl@mozillafoundation.org](mailto:andrewl@mozillafoundation.org), or the technical lead. We will work with you to help you figure out how to ensure that any conflict doesnt interfere with your work, in confidence if you would prefer.
* Talk to Chris Lawrence. Chris oversees the project. He can be contacted at [clawrence@mozillafoundation.org](mailto:clawrence@mozillafoundation.org).
If you feel you have been unfairly accused of violating this code of conduct, you should contact Chris with a concise description of your grievance.
## Conclusion
We welcome your feedback on this and every other aspect of what we do as The Coral Project, and we thank you for working with us to make it a safe, enjoyable, and friendly experience for everyone involved in the project and what we do.
Above text is licensed CC BY-SA 4.0, adapted from the SRCCON code of conduct, FreeBSDs code of conduct, Vox Medias product team code of conduct, Mediums code of conduct, as well as adapted from the Contributor Covenant.
-6
View File
@@ -1,6 +0,0 @@
---
title: REST API
permalink: /docs/development/rest/
---
Talk provides REST API documentation at [swagger.yaml]({{ "swagger.yaml" | absolute_url }}).
+122
View File
@@ -0,0 +1,122 @@
---
title: Installation from Docker
permalink: /installation-from-docker/
---
{% include badges.html %}
[Docker](https://www.docker.com/community-edition#/download){:target="_blank"} {{ site.versions.docker }} and
[Docker Compose](https://docs.docker.com/compose/install/){:target="_blank"} {{ site.versions.docker_compose }} are required
to perform installation via Docker. This is the recommended way to deploy the
application when used in production.
Available as [coralproject/talk](https://hub.docker.com/r/coralproject/talk/){:target="_blank"} on
Docker Hub. [(latest/Dockerfile)](https://github.com/coralproject/talk/blob/master/Dockerfile){:target="_blank"}
Images are tagged using the following notation:
- `x` (where `x` is the major version number): any minor or patch updates will
be included in this. If you're ok getting new features occasionally and all
the bug fixes, this is the tag for you. Any changes to this image tag will not
require a database migration.
- `x.y` (where `y` is the minor version number): any patch updates will be
included with this tag. If you like getting fixes and having features change
only when you want, this is the tag for you. **(recommended)**
- `x.y.z` (where `z` is the patch version): this tag never gets updated, and
essentially freezes your version, this should only be used when you are either
extending Talk or are sure of a specific version you want to freeze.
We provide tags with `*-onbuild`
[(onbuild/Dockerfile)](https://github.com/coralproject/talk/blob/master/Dockerfile.onbuild){:target="_blank"}
that can be used for easy plugin integration and acts as a customization
endpoint. To use this image tag, refer to the
[onbuild](#onbuild) section.
## Installing
To use Talk without major customization you can run the application using our
provided docker image. The following is a `docker-compose.yml` file that can
be used to setup Talk:
```yml
{% include files/docker-compose.yml %}
```
This is the bare minimum needed to start Talk, for more configuration
variables, check out the [Configuration]({{ "/configuration/" | relative_url }}) section.
{: .code-aside}
And you can then start it with:
```bash
docker-compose up -d
```
This process will take a minute or two, it has to download docker images for the
required databases and Talk as well as setup the environments.
Now that you've started the services started using compose, you should see
output that resembles the following:
```
Creating mongo_1 ...
Creating redis_1 ...
Creating mongo_1 ... done
Creating redis_1 ... done
Creating talk_1 ...
Creating talk_1 ... done
```
{: .no-copy }
And when you run `docker-compose ps`, you should see something like:
```
Name Command State Ports
-------------------------------------------------------------------------------
mongo_1 docker-entrypoint.sh mongod Up 27017/tcp
redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
talk_1 yarn start Up 0.0.0.0:3000->3000/tcp
```
{: .no-copy }
At this stage, you should refer to the [configuration]({{ "/configuration/" | relative_url }}) for
configuration variables that are specific to your installation.
## Onbuild
We provide `*-onbuild` images to assist and automate the customization of our
base installation with additional custom plugins. Images can be created with the
most basic of `Dockerfile`'s:
```docker
FROM coralproject/talk:latest-onbuild
```
And running the following to build the docker image:
```bash
docker build -t my-awesome-talk-image .
```
Don't forget to replace `my-awesome-talk-image` with your own image name.
{: .code-aside}
This accomplishes a lot:
1. Copies all the files alongside the `Dockerfile` into the application
directory in the Docker image.
2. Installs any new dependencies that were required by any new plugins.
3. Builds the new static bundles so that they are ready to serve when the image
is running.
This means that you can create a repository for your organization that simply
includes the above `Dockerfile`, a directory of `plugins`, and a `plugins.json`
file which specifies the activated plugins, and you can deploy Talk easily to
your containerized infrastructure. The versioning of our Docker tags as well
lets you do something like:
```docker
FROM coralproject/talk:3.5-onbuild
```
Which would pin your image to `3.5` release's.
+80
View File
@@ -0,0 +1,80 @@
---
title: Installation from Source
permalink: /installation-from-source/
---
{% include badges.html %}
To install Talk from Source, ensure that you have Node version
{{ site.versions.node }}. Installing via source is the recommended method when
developing as it give you the best tooling. We release versions using semantic
versioning, and do so to our
[Github Releases](https://github.com/coralproject/talk/releases){:target="_blank"}
page. There you can download archives of older versions or the latest release.
The examples following will download the latest code on our master branch.
## Installing
First we will download and extract the latest codebase of Talk:
```bash
curl -sLo talk.tar.gz https://github.com/coralproject/talk/archive/master.tar.gz
mkdir -p talk
tar xzf talk.tar.gz -C talk --strip-components 1
cd talk
```
From here we need to fetch the dependencies and build the static assets using
Yarn:
```bash
yarn
yarn build
```
You can either setup the required databases by visiting the docs for [MongoDB](https://docs.mongodb.com/manual/installation/){:target="_blank"} and
[Redis](https://redis.io/topics/quickstart){:target="_blank"}, or using the following commands which will leverage Docker:
```bash
docker run -p 127.0.0.1:6379:6379 -d redis
docker run -p 127.0.0.1:27017:27017 -d mongo
```
Didn't work? Sometimes you may already have a container running on these ports,
run `docker ps` to see what other containers you have running and running
`docker stop <id>` on those containers to stop them.
{: .code-aside}
_This documentation assumes that you will be running MongoDB on
`127.0.0.1:27017` and Redis on `127.0.0.1:6379`. The above Docker commands bind
MongoDB and Redis on these interfaces for you._
We should then specify the configuration variables that can be used to run the
application locally in a file named `.env`. This will be read by the application
when running in development mode:
```bash
NODE_ENV=development
TALK_MONGO_URL=mongodb://127.0.0.1:27017/talk
TALK_REDIS_URL=redis://127.0.0.1:6379
TALK_ROOT_URL=http://127.0.0.1:3000
TALK_PORT=3000
TALK_JWT_SECRET=password
TALK_FACEBOOK_APP_ID=A-Facebook-App-ID
TALK_FACEBOOK_APP_SECRET=A-Facebook-App-Secret
```
This is the bare minimum needed to start Talk, for more configuration
variables, check out the [Configuration]({{ "/configuration/" | relative_url }})
section. Facebook login above will definitely not work unless you change those
values as well.
{: .code-aside}
You can now start the application by running:
```bash
yarn dev-start
```
At this stage, you should refer to the [configuration]({{ "/configuration/" | relative_url }}) for
configuration variables that are specific to your installation.
+185
View File
@@ -0,0 +1,185 @@
---
title: Talk Quickstart
permalink: /
---
{% include badges.html %}
Online comments are broken. Our open-source Talk tool rethinks how moderation,
comment display, and conversation function, creating the opportunity for safer,
smarter discussions around your work. Read more about our product features and
goals [here](https://coralproject.net/products/talk.html){:target="_blank"}. The
documentation available here is pertaining to the technical details for
installing, configuring, and deploying Talk.
Talk is a [Node](https://nodejs.org/){:target="_blank"} application with
dependencies managed by
[Yarn](https://yarnpkg.com/en/docs/install){:target="_blank"} that connects to
[MongoDB](https://docs.mongodb.com/manual/installation/){:target="_blank"} and
[Redis](https://redis.io/topics/quickstart){:target="_blank"} databases in order
to persist data. The following versions are supported:
- Node {{ site.versions.node }}
- Yarn {{ site.versions.yarn }}
- MongoDB {{ site.versions.mongodb }}
- Redis {{ site.versions.redis }}
An optional dependency for Talk is
[Docker](https://www.docker.com/community-edition#/download){:target="_blank"}.
It is used during [development](#development) to setup the database and can be
used to [install via Docker](#installation-from-docker). We have tested Talk
and this documentation with versions {{ site.versions.docker }}.
Another optional dependency for Talk is
[Docker Compose](https://docs.docker.com/compose/install/){:target="_blank"}. It
can be used to setup your environment easily for testing. We have tested Talk
and this documentation with versions {{ site.versions.docker_compose }}.
## Installation
### Installation from Docker
To use Talk without major customization you can run the application using our
provided docker image. The following is a `docker-compose.yml` file that can
be used to setup Talk:
```yml
{% include files/docker-compose.yml %}
```
This is the bare minimum needed to run the demo, for more configuration
variables, check out the [Configuration]({{ "/configuration/" | relative_url }}) section.
{: .code-aside}
And you can then start it with:
```bash
docker-compose up -d
```
This process will take a minute or two, it has to download docker images for the
required databases and Talk as well as setup the environments.
Now that you've started the services started using compose, you should see
output that resembles the following:
```
Creating mongo_1 ...
Creating redis_1 ...
Creating mongo_1 ... done
Creating redis_1 ... done
Creating talk_1 ...
Creating talk_1 ... done
```
{: .no-copy }
And when you run `docker-compose ps`, you should see something like:
```
Name Command State Ports
-------------------------------------------------------------------------------
mongo_1 docker-entrypoint.sh mongod Up 27017/tcp
redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
talk_1 yarn start Up 0.0.0.0:3000->3000/tcp
```
{: .no-copy }
Continue onto the [Running](#running) section for details on how to complete the
installation and get started using Talk.
### Installation from Source
To install Talk from Source, ensure that you have the version of Node as
specified above. First we will download and extract the latest codebase of Talk:
```bash
curl -sLo talk.tar.gz https://github.com/coralproject/talk/archive/master.tar.gz
mkdir -p talk
tar xzf talk.tar.gz -C talk --strip-components 1
cd talk
```
From here we need to fetch the dependencies and build the static assets using
Yarn:
```bash
yarn
yarn build
```
You can either setup the required databases by visiting the docs for [MongoDB](https://docs.mongodb.com/manual/installation/){:target="_blank"} and
[Redis](https://redis.io/topics/quickstart){:target="_blank"}, or using the following commands which will leverage Docker:
```bash
docker run -p 127.0.0.1:6379:6379 -d redis
docker run -p 127.0.0.1:27017:27017 -d mongo
```
Didn't work? Sometimes you may already have a container running on these ports,
run `docker ps` to see what other containers you have running and running
`docker stop <id>` on those containers to stop them.
{: .code-aside}
_This documentation assumes that you will be running MongoDB on
`127.0.0.1:27017` and Redis on `127.0.0.1:6379`. The above Docker commands bind
MongoDB and Redis on these interfaces for you._
We should then specify the configuration variables that can be used to run the
application locally in a file named `.env`. This will be read by the application
when running in development mode:
```bash
NODE_ENV=development
TALK_MONGO_URL=mongodb://127.0.0.1:27017/talk
TALK_REDIS_URL=redis://127.0.0.1:6379
TALK_ROOT_URL=http://127.0.0.1:3000
TALK_PORT=3000
TALK_JWT_SECRET=password
TALK_FACEBOOK_APP_ID=A-Facebook-App-ID
TALK_FACEBOOK_APP_SECRET=A-Facebook-App-Secret
```
This is only the bare minimum needed to run the demo, for more configuration
variables, check out the [Configuration]({{ "/configuration/" | relative_url }}) section. Facebook login above
will definitely not work unless you change those values as well.
{: .code-aside}
You can now start the application by running:
```bash
yarn dev-start
```
Continue onto the [Running](#running) section for details on how to complete the
installation and get started using Talk.
## Running
You can now navigate to
[http://127.0.0.1:3000/admin/install](http://127.0.0.1:3000/admin/install){:target="_blank"}
and go through the admin installation. There you will be prompted to create your
first admin account, and specify the domain whitelist for domains that are
allowed to have the comment box on.
_During development, ensure you whitelist 127.0.0.1:3000 otherwise the
[http://127.0.0.1:3000/](http://127.0.0.1:3000/){:target="_blank"} page will not
load._
Once you've completed the installation, you can visit
[http://127.0.0.1:3000/](http://127.0.0.1:3000/){:target="_blank"} where you can
view our development area where we test out features in Talk where you can write
comments and see them in the admin interface where you can do moderation and
reconfigure the user experience.
## Demo
If you've followed the documentation above, you'll now have a running copy of
Talk. To demonstrate what your own self-hosted copy of Talk can do, below
you'll find a demo that can be used to test the copy that is running now on your
machine.
{% include demo.html %}
At this point you've successfully installed, configured, and ran your very own
instance of Talk! Continue through this documentation on this site to learn more
on how to configure, develop with, and contribute to Talk!
@@ -1,22 +1,30 @@
---
title: Plugins Overview
permalink: /docs/plugins/
permalink: /plugins/
---
Plugins are the integration point between the Talk core code and custom
functionality. We provide methods to inject behavior into the server side and
the client side application to affect different parts of the application
life cycle.
## Recipes
Recipes are plugin templates provided by the Coral Core team. Developers can use these recipes to build their own plugins. You can find all the Talk recipes here: [https://github.com/coralproject/talk-recipes/](https://github.com/coralproject/talk-recipes/).
Recipes are plugin templates provided by the Coral Core team. Developers can use
these recipes to build their own plugins. You can find all the Talk recipes
here: [github.com/coralproject/talk-recipes](https://github.com/coralproject/talk-recipes/){:target="_blank"}.
## Plugin Registration
In order for a plugin to be active in a Talk install, it must be _registered_. The parsing order for the plugin registration is as follows:
In order for a plugin to be active in a Talk install, it must be _registered_.
The parsing order for the plugin registration is as follows:
- `TALK_PLUGINS_JSON` environment variable
- `plugins.json` file
- `plugins.default.json` file
If you need to "disable all plugins", you can simply provide `{}` as the
contents of `process.env.TALK_PLUGINS_JSON` or the `plugins.json`.
contents of `TALK_PLUGINS_JSON` or the `plugins.json`.
The format for this is thus:
@@ -53,11 +61,12 @@ External plugins can be resolved by running:
This achieves two things:
1. It will traverse into local plugin folders and install their dependencies.
_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._ This will result in the
project `package.json` and `yarn.lock` files to be modified, this is normal as
this ensures that repeated deployments (with the same config) will have the
same config, these changes should not be committed to source control.
_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._ This will result in the project `package.json` and `yarn.lock`
files to be modified, this is normal as this ensures that repeated deployments
(with the same config) will have the same config, these changes should not be
committed to source control.
2. It will seek out dependencies that are listed in the object notation and try
to install them from npm.
@@ -79,7 +88,7 @@ local plugins into the `plugins/` directory. After including the config, you
need to reconcile the plugins and build the static assets:
```bash
# get plugin dependancies and remote plugins
# get plugin dependencies and remote plugins
./bin/cli plugins reconcile
# build static assets (including enabled client side plugins)
@@ -104,3 +113,7 @@ other local plugins that should be included.
Onbuild triggers will execute when the image is building with your custom
configuration and will ensure that the image is ready to use by building all
assets inside the image as well.
For more information on the onbuild image, refer to the
[Installation from Docker]({{ "/installation-from-docker/" | relative_url }})
documentation.
@@ -0,0 +1,442 @@
---
title: Advanced Configuration
permalink: /advanced-configuration/
class: configuration
---
Talk requires configuration in order to customize the installation. The default
behavior is to load it's configuration from the environment, following the
[12 Factor App Manifesto](https://12factor.net/){:target="_blank"}.
In development, you can specify configuration in a file named `.env` and it will
be loaded into the environment when you run `yarn dev-start`.
The following variables have defaults, and are _optional_ to start your
instance of Talk:
{% include toc.html %}
If this is your first time configuring Talk, ensure you've also added the
[Required Configuration]({{ "/configuration/" | relative_url }}) as well,
otherwise the application will fail to start.
## TALK_CACHE_EXPIRY_COMMENT_COUNT
Configure the duration for which comment counts are cached for, parsed by
[ms](https://www.npmjs.com/package/ms){:target="_blank"}. (Default `1hr`)
## TALK_DEFAULT_STREAM_TAB
Specify the default stream tab in the admin. (Default `all`)
## TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS
When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`)
## TALK_DISABLE_EMBED_POLYFILL
When set to `TRUE`, the build process will not include the
[babel-polyfill](https://babeljs.io/docs/usage/polyfill/){:target="_blank"}
in the embed.js target that is loaded on the page that loads the embed. (Default
`FALSE`)
## TALK_DISABLE_STATIC_SERVER
When `TRUE`, it will not mount the static asset serving routes on the router.
This is used primarily in conjunction with [TALK_STATIC_URI](#talk_static_uri){: .param}
when the static assets are being hosted on an external domain. (Default `FALSE`)
## TALK_HELMET_CONFIGURATION
A JSON string representing the configuration passed to the
[helmet](https://github.com/helmetjs/helmet){:target="_blank"} middleware. It
can be used to disable features like [HSTS](https://helmetjs.github.io/docs/hsts/){:target="_blank"}
and others by simply providing the configuration as detailed on the
[helmet docs](https://helmetjs.github.io/docs/){:target="_blank"}. (Default `{}`)
For sites that do not have SSL enabled on all their pages across their domain,
it is critical that you specify the following to disable the
[HSTS](https://helmetjs.github.io/docs/hsts/){:target="_blank"} headers from
being sent:
```plain
TALK_HELMET_CONFIGURATION={"hsts": false}
```
To disable these headers from being sent.
## TALK_INSTALL_LOCK
When `TRUE`, disables the dynamic setup endpoint `/admin/install` from even
loading. This prevents hits to the database with enabled. This should always be
set to `TRUE` after you've deployed Talk. (Default `FALSE`)
## TALK_JWT_ALG
The algorithm used to sign/verify JWTs used for session management. Read up
about alternative algorithms on the
[jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken#algorithms-supported){:target="_blank"}
package. (Default `HS256`)
### Shared Secret
{:.no_toc}
You would use a shared secret when you have no need to share the tokens with
other applications in your organization.
Supported signing algorithms:
- HS256
- HS384
- HS512
These must be provided in the form:
```json
{
"secret": "<my secret key>"
}
```
{: .no-copy}
### Asymmetric Secret
{:.no_toc}
You would use a asymmetric secret when you want to share the token in your
organization, and would like to pass an existing auth token to Talk in order to
authenticate your users.
Supported signing algorithms:
- RS256
- RS384
- RS512
- ES256
- ES384
- ES512
These must be provided in the form:
```json
{
"public": "<the PEM encoded public key>",
"private": "<the PEM encoded private key>"
}
```
{: .no-copy}
Note that when using the asymmetric keys as discussed above, the certificates
must have their newlines replaced with `\\n`, this is to ensure that the
newlines are preserved after JSON decoding. Not doing so will result in parsing
errors.
To assist with this process, we have developed a tool that can generate new
certificates that match our required format: [coralcert](https://github.com/coralproject/coralcert){:target="_blank"}.
This tool can generate RSA and ECDSA certificates, check it's [README](https://github.com/coralproject/coralcert){:target="_blank"}
for more details.
## TALK_JWT_AUDIENCE
The audience [aud](https://tools.ietf.org/html/rfc7519#section-4.1.3){:target="_blank"}
claim for login JWT tokens. (Default `talk`)
## TALK_JWT_CLEAR_COOKIE_LOGOUT
When `FALSE`, Talk will not clear the cookie with name
[TALK_JWT_SIGNING_COOKIE_NAME](#talk_jwt_signing_cookie_name){: .param} when logging out
but will still blacklist the token. (Default `TRUE`)
## TALK_JWT_COOKIE_NAME
The default cookie name to check for a valid JWT token to use for verifying a
user. (Default `authorization`)
## TALK_JWT_COOKIE_NAMES
The different cookie names to check for a JWT token in, separated by a `,`. By
default, we always use the value of [TALK_JWT_COOKIE_NAME](#talk_jwt_cookie_name){: .param}
and [TALK_JWT_SIGNING_COOKIE_NAME](#talk_jwt_signing_cookie_name){: .param} for this
value. Any additional cookie names specified here will be appended to the list
of cookie names to inspect.
For example, the value of:
```plain
TALK_JWT_COOKIE_NAME=talk
TALK_JWT_SIGNING_COOKIE_NAME=talk
TALK_JWT_COOKIE_NAMES=coralproject.talk,coralproject.auth
```
Would mean we would check the following cookies (in order) for a valid token:
1. `talk`
2. `coralproject.talk`
3. `coralproject.auth`
## TALK_JWT_DISABLE_AUDIENCE
When `TRUE`, Talk will not verify or sign JWTs with an audience
[aud](https://tools.ietf.org/html/rfc7519#section-4.1.3){:target="_blank"}
claim, even if [TALK_JWT_AUDIENCE](#talk_jwt_audience){: .param} is set. (Default `FALSE`)
## TALK_JWT_DISABLE_ISSUER
When `TRUE`, Talk will not verify or sign JWTs with an issuer
[iss](https://tools.ietf.org/html/rfc7519#section-4.1.1){:target="_blank"}
claim, even if [TALK_JWT_ISSUER](#talk_jwt_issuer){: .param} is set. (Default `FALSE`)
## TALK_JWT_EXPIRY
The expiry duration [exp](https://tools.ietf.org/html/rfc7519#section-4.1.4){:target="_blank"}
for the tokens issued for logged in sessions, parsed by
[ms](https://www.npmjs.com/package/ms){:target="_blank"}. (Default `1 day`)
If the user logs out, then an entry is created in the token blacklist of it's
[jti](https://tools.ietf.org/html/rfc7519#section-4.1.7){:target="_blank"} for
set to be automatically removed at it's expiry time. It is important for this
reason to create reasonable expiry lengths as to minimize the storage overhead.
## TALK_JWT_ISSUER
The issuer [iss](https://tools.ietf.org/html/rfc7519#section-4.1.1){:target="_blank"}
claim for login JWT tokens. (Defaults to value of [TALK_ROOT_URL]({{ "/configuration/#talk_root_url" | relative_url }}){: .param})
## TALK_JWT_SECRET
Used to specify the application signing secret. You can specify this using a
simple string, we recommend using a password generator and pasting it's output.
An example for `TALK_JWT_SECRET` could be:
```plain
TALK_JWT_SECRET=jX9y8G2ApcVLwyL{$6s3
```
{: .no-copy}
You can also express this secret in the JSON syntax:
```plain
TALK_JWT_SECRET={"secret": "jX9y8G2ApcVLwyL{$6s3"}
```
Refer to the documentation for [TALK_JWT_ALG](#talk_jwt_alg){: .param} for other signing
methods and other forms of the `TALK_JWT_SECRET`. If you are interested in using
multiple keys, then refer to [TALK_JWT_SECRETS](#talk_jwt_secrets){: .param}.
## TALK_JWT_SECRETS
Used when specifying multiple secrets used for key rotations. This is a JSON
encoded array, where each element matches the JWT Secret pattern. When this is
used, you do not need to specify a [TALK_JWT_SECRET](#talk_jwt_secret){: .param} as this
will take precedence. **The first secret in `TALK_JWT_SECRETS` will be used for
signing, and must contain a private key if used with an asymmetric algorithm.**
All secrets should specify a `kid` field which uniquely identifies a given key
and will sign all tokens with that `kid` for later identification.
When the value of [TALK_JWT_ALG](#talk_jwt_alg){: .param} is a `HS*` value, then the value
of the `TALK_JWT_SECRETS` should take the form:
```plain
TALK_JWT_SECRETS=[{"kid": "1", "secret": "my-super-secret"}, {"kid": "2", "secret": "my-other-super-secret"}]
```
{: .no-copy}
Note that the secret is stored in a JSON object, keyed by `secret`. This is only
needed when specifying in the multiple secrets for `TALK_JWT_SECRETS`, but may
be used to specify the single [TALK_JWT_SECRET](#talk_jwt_secret){: .param}.
{: .code-aside}
When the value of [TALK_JWT_ALG](#talk_jwt_alg){: .param} is **not** a `HS*` value, then
the value of the `TALK_JWT_SECRETS` should take the form:
```plain
TALK_JWT_SECRETS=[{"kid": "1", "private": "<my private key>", "public": "<my public key>"}, ...]
```
{: .no-copy}
Refer to the documentation on the [TALK_JWT_ALG](#talk_jwt_alg){: .param} for more
information on what to store in these parameters.
{: .code-aside}
## TALK_JWT_SIGNING_COOKIE_NAME
The default cookie name that is use to set a cookie containing a JWT that was
issued by Talk. (Defaults to value of [TALK_JWT_COOKIE_NAME](#talk_jwt_cookie_name){: .param})
## TALK_JWT_USER_ID_CLAIM
Specify the claim using dot notation for where the user id should be stored/read
to/from. (Default `sub`)
If for example, the JWT's claims looks something like this:
```json
{
"user": {
"id": "123123"
}
}
```
Then we would set `TALK_JWT_USER_ID_CLAIM` to:
```plain
TALK_JWT_USER_ID_CLAIM=user.id
```
## TALK_KEEP_ALIVE
The keepalive timeout that should be used to send keep alive messages through
the websocket to keep the socket alive, parsed by
[ms](https://www.npmjs.com/package/ms){:target="_blank"}. (Default `30s`)
## TALK_RECAPTCHA_PUBLIC
Client secret used for enabling reCAPTCHA powered logins. If
[TALK_RECAPTCHA_SECRET](#talk_recaptcha_secret){: .param} and
[TALK_RECAPTCHA_PUBLIC](#talk_recaptcha_public){: .param} are not provided it will instead
default to providing only a time based lockout. Refer to
[reCAPTCHA](https://www.google.com/recaptcha/intro/index.html) for information
on getting an account setup.
## TALK_RECAPTCHA_SECRET
Server secret used for enabling reCAPTCHA powered logins. If
[TALK_RECAPTCHA_SECRET](#talk_recaptcha_secret){: .param} and
[TALK_RECAPTCHA_PUBLIC](#talk_recaptcha_public){: .param} are not provided it will instead
default to providing only a time based lockout. Refer to
[reCAPTCHA](https://www.google.com/recaptcha/intro/index.html) for information
on getting an account setup.
## TALK_REDIS_CLIENT_CONFIG
Configuration overrides for the redis client configuration in a JSON encoded
string. Configuration is overridden as the second parameter to the redis client
constructor, and is merged with default configuration. Refer to the
[ioredis](https://github.com/luin/ioredis){:target="_blank"} docs on the
available options. (Default `{}`)
## TALK_REDIS_CLUSTER_CONFIGURATION
The JSON encoded form of the cluster nodes. Only required when
[TALK_REDIS_CLUSTER_MODE](#talk_redis_cluster_mode){: .param} is `CLUSTER`. See
[https://github.com/luin/ioredis#cluster](https://github.com/luin/ioredis#cluster){:target="_blank"}
for configuration details. (Default `[]`)
## TALK_REDIS_CLUSTER_MODE
The cluster mode of the redis client. Can be either `NONE` or `CLUSTER`.
(Default `NONE`)
## TALK_REDIS_RECONNECTION_BACKOFF_FACTOR
The time factor that will be multiplied against the current attempt count
between attempts to connect to redis, parsed by
[ms](https://www.npmjs.com/package/ms){:target="_blank"}. (Default `500 ms`)
## TALK_REDIS_RECONNECTION_BACKOFF_MINIMUM_TIME
The minimum time used to delay before attempting to reconnect to redis, parsed
by [ms](https://www.npmjs.com/package/ms){:target="_blank"}. (Default `1 sec`)
## TALK_ROOT_URL_MOUNT_PATH
When set to `TRUE`, the routes will be mounted onto the `<PATHNAME>` component
of the [TALK_ROOT_URL]({{ "/configuration/#talk_root_url" | relative_url }}){: .param}.
You would use this when your upstream proxy cannot strip the prefix from the
url. (Default `FALSE`)
If for example, you had the following configuration:
```plain
TALK_ROOT_URL=https://coralproject.net/talk/
TALK_ROOT_URL_MOUNT_PATH=TRUE
```
Then all the routes for the API will be expecting to be hit on `/talk/`, such as
`/talk/api/v1/graph/ql` instead of `/api/v1/graph/ql`. Most modern webservers
can perform the path stripping when serving an upstream proxy, but some CDN's
cannot. You would use this option in the latter situation.
## TALK_SMTP_EMAIL
The email address to send emails from using the SMTP provider in the format:
```plain
TALK_SMTP_EMAIL="The Coral Project" <support@coralproject.net>
```
Including the name and email address.
## TALK_SMTP_HOST
The domain for the SMTP provider that you are using.
## TALK_SMTP_PASSWORD
The password for the SMTP provider you are using.
## TALK_SMTP_PORT
The port for the SMTP provider that you are using.
## TALK_SMTP_USERNAME
The username of the SMTP provider you are using.
## TALK_STATIC_URI
Used to set the uri where the static assets should be served from. This is used
when you want to upload the static assets through your build process to a
service like Google Cloud Storage or Amazon S3 and you would then specify the
CDN/Storage url. (Defaults to value of
[TALK_ROOT_URL]({{ "/configuration/#talk_root_url" | relative_url }}){: .param})
## TALK_THREADING_LEVEL
Specify the maximum depth of the comment thread. (Default `3`)
**Note that a high value for `TALK_THREADING_LEVEL` will result in large
performance impacts.**
## TALK_WEBSOCKET_LIVE_URI
Used to override the location to connect to the websocket endpoint to
potentially another host. This should be used when you need to route websocket
requests out of your CDN in order to serve traffic more efficiently.
If the value of [TALK_ROOT_URL]({{ "/configuration/#talk_root_url" | relative_url }}){: .param}
is a https url, then this defaults to `wss://${location.host}${MOUNT_PATH}api/v1/live`.
Otherwise, it defaults to `ws://${location.host}${MOUNT_PATH}api/v1/live`.
Where `MOUNT_PATH` is either `/` if [TALK_ROOT_URL_MOUNT_PATH](#talk_root_url_mount_path){: .param}
is `FALSE`, or the path component of
[TALK_ROOT_URL]({{ "/configuration/#talk_root_url" | relative_url }}){: .param} if it's `TRUE`.
**Warning: if used without managing the auth state manually, auth
cannot be persisted due to browser restrictions.**
## TRUST_THRESHOLDS
Configure the reliability thresholds for flagging and commenting. (Default
`comment:2,-1;flag:2,-1`)
The form of the environment variable:
```plain
TRUST_THRESHOLDS=comment:<RELIABLE COMMENTER>,<UNRELIABLE COMMENTER>;flag:<RELIABLE FLAGGER>,<UNRELIABLE FLAGGER>
```
The default value of:
```plain
TRUST_THRESHOLDS=comment:2,-1;flag:2,-1
```
Could be read as:
- When a commenter has one comment rejected, their next comment must be
pre-moderated once in order to post freely again. If they instead get rejected
again, then they must have two of their comments approved in order to get
added back to the queue.
- At the moment of writing, behavior is not attached to the flagging
reliability, but it is recorded.
@@ -0,0 +1,106 @@
---
title: Required Configuration
permalink: /configuration/
class: configuration
---
Talk requires configuration in order to customize the installation. The default
behavior is to load it's configuration from the environment, following the
[12 Factor App Manifesto](https://12factor.net/){:target="_blank"}.
In development, you can specify configuration in a file named `.env` and it will
be loaded into the environment when you run `yarn dev-start`.
The following variables do not have defaults, and are **required** to start your
instance of Talk:
{% include toc.html %}
If you've already configured your application with the required configuration,
you can further customize it's behavior by applying
[Advanced Configuration]({{ "/advanced-configuration/" | relative_url }}).
## TALK_MONGO_URL
The database connection string for the MongoDB database. This usually takes the
form of:
```plain
TALK_MONGO_URL=mongodb://<DATABASE USER>:<DATABASE PASSWORD>@<DATABASE HOST>:<DATABASE PORT>/<DATABASE NAME>
```
Refer to [connection string uri format](https://docs.mongodb.com/manual/reference/connection-string/){:target="_blank"}
for the detailed url scheme of the MongoDB url.
## TALK_REDIS_URL
The database connection string for the Redis database. This usually takes the
form of:
```plain
TALK_REDIS_URL=redis://user:<DATABASE PASSWORD>@<DATABASE HOST>:<DATABASE PORT>/<DATABASE NUMBER>
```
If we for example, had Redis running on our local machine without a password,
where I want to use database #2, I could set the `TALK_REDIS_URL` to:
```plain
TALK_REDIS_URL=redis://127.0.0.1:6379/2
```
Refer to [uri scheme](http://www.iana.org/assignments/uri-schemes/prov/redis){:target="_blank"}
for the detailed url scheme of the Redis url.
## TALK_ROOT_URL
The root url of the installed application externally available in the format:
```plain
TALK_ROOT_URL=<SCHEME>://<HOST>:<PORT?>/<PATHNAME>
```
For example, if we installed our application onto the `talk.coralproject.net`
domain, where we used a proxy like [Caddy](https://caddyserver.com){:target="_blank"}
or [Nginx](https://nginx.org){:target="_blank"} to perform SSL termination, then
`TALK_ROOT_URL` would be:
```plain
TALK_ROOT_URL=https://talk.coralproject.net/
```
_Note that we omitted the `PORT`, as it was implied by setting the `SCHEME` to
`https`._
## TALK_JWT_SECRET
Used to specify the application signing secret. You can specify this using a
simple string, we recommend using a password generator and pasting it's output.
An example for `TALK_JWT_SECRET` could be:
```plain
TALK_JWT_SECRET=jX9y8G2ApcVLwyL{$6s3
```
{: .no-copy}
Be default, we sign our tokens with HMAC using a SHA-256 hash algorithm. If you
want to change the signing algorithm, or use multiple signing/verifying keys,
refer to our [Advanced Configuration]({{ "/advanced-configuration/" | relative_url }}) documentation.
## TALK_FACEBOOK_APP_ID
The Facebook App ID for your Facebook Login enabled app. You can learn more
about getting a Facebook App ID at the
[Facebook Developers Portal](https://developers.facebook.com){:target="_blank"}
or by visiting the
[Creating an App ID](https://developers.facebook.com/docs/apps/register){:target="_blank"}
guide. This is only required while the `talk-plugin-facebook-auth` plugin is
enabled.
## TALK_FACEBOOK_APP_SECRET
The Facebook App Secret for your Facebook Login enabled app. You can learn more
about getting a Facebook App Secret at the
[Facebook Developers Portal](https://developers.facebook.com){:target="_blank"}
or by visiting the
[Creating an App ID](https://developers.facebook.com/docs/apps/register){:target="_blank"}
guide. This is only required while the `talk-plugin-facebook-auth` plugin is
enabled.