mirror of
https://github.com/wassname/talk.git
synced 2026-09-09 11:38:08 +08:00
Updated docs
This commit is contained in:
@@ -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.
|
||||
@@ -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)!
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
@@ -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._
|
||||
@@ -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`).
|
||||
@@ -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.
|
||||
@@ -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>"}]
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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).
|
||||
@@ -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 don’t 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)
|
||||
@@ -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!
|
||||
@@ -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 I’m specifying that the MyComponent Component will take place within the `commentDetail` in Talk.
|
||||
|
||||
`commentDetail` it’s 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"
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -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');
|
||||
```
|
||||
@@ -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);
|
||||
```
|
||||
@@ -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.)
|
||||
@@ -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.
|
||||
@@ -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. It’s not only okay to ask for help or feedback often, it’s 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 people’s 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 you’re 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 someone’s 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 we’re not always going to succeed in meeting them. When something goes wrong—whether it’s 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 you’re 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 you’re 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 you’re 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 you’re 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 doesn’t 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, FreeBSD’s code of conduct, Vox Media’s product team code of conduct, Medium’s code of conduct, as well as adapted from the Contributor Covenant.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
title: REST API
|
||||
permalink: /docs/development/rest/
|
||||
---
|
||||
|
||||
Talk provides REST API documentation at [swagger.yaml]({{ "swagger.yaml" | absolute_url }}).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 JWT’s 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 JWT’s 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 JWT’s 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.
|
||||
Reference in New Issue
Block a user