Merge pull request #481 from outline/docker-development

Docker Development
This commit is contained in:
Tom Moor
2017-12-11 20:37:03 -08:00
committed by GitHub
8 changed files with 117 additions and 42 deletions

View File

@@ -2,11 +2,11 @@
#
# Please use `openssl rand -hex 32` to create SECRET_KEY
DATABASE_URL=postgres://user:pass@example.com:5432/outline
DATABASE_URL_TEST=postgres://user:pass@example.com:5432/outline-test
DATABASE_URL=postgres://user:pass@localhost:5432/outline
DATABASE_URL_TEST=postgres://user:pass@localhost:5432/outline-test
SECRET_KEY=F0E5AD933D7F6FD8F4DBB3E038C501C052DC0593C686D21ACB30AE205D2F634B
PORT=3000
REDIS_URL=redis://localhost:6379
REDIS_URL=redis://redis:6379
SLACK_KEY=71315967491.XXXXXXXXXX
SLACK_SECRET=d2dc414f9953226bad0a356c794XXXXX
URL=http://localhost:3000
@@ -14,9 +14,15 @@ DEPLOYMENT=hosted
ENABLE_UPDATES=true
GOOGLE_ANALYTICS_ID=
AWS_ACCESS_KEY_ID=notcheckedindev
AWS_SECRET_ACCESS_KEY=notcheckedindev
AWS_S3_UPLOAD_BUCKET_URL=http://s3:4569
AWS_S3_UPLOAD_BUCKET_NAME=outline-dev
AWS_S3_UPLOAD_MAX_SIZE=26214400
SMTP_HOST=
SMTP_PORT=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_EMAIL=
SMTP_REPLY_EMAIL=
SMTP_REPLY_EMAIL=

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ node_modules/*
.env
npm-debug.log
.DS_Store
fakes3/*

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM node:latest
ENV APP_PATH /opt/outline
RUN mkdir -p $APP_PATH
WORKDIR $APP_PATH
COPY . $APP_PATH
RUN yarn
RUN cp -r /opt/outline/node_modules /opt/node_modules

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
up:
docker-compose up -d redis postgres s3
docker-compose run --rm outline yarn sequelize db:migrate
docker-compose up outline
build:
docker-compose build --pull outline
destroy:
docker-compose stop
docker-compose rm -f
.PHONY: up build destroy # let's go to reserve rules names

View File

@@ -8,37 +8,27 @@ An open, extensible, knowledge base for your team built using React and Node.js.
## Installation
Outline requires following dependencies to work:
Outline requires the following dependencies:
- Postgres >=9.5
- Redis
- S3 bucket configured to support CORS uploads
- Slack developer application
To install and run the application:
In development you can quickly can an environment running using Docker by
following these steps:
1. Install dependencies with `yarn`
1. Register a Slack app at https://api.slack.com/apps
1. Copy the file `.env.sample` to `.env` and fill out the keys
1. Run DB migrations `yarn sequelize db:migrate`
To run Outline in development mode with server and frontend code reloading:
1. Install [Docker for Desktop](https://www.docker.com) if you don't already have it.
1. Register a Slack app at https://api.slack.com/apps
1. Copy the file `.env.sample` to `.env` and fill out the Slack keys, everything
else should work well for development.
1. Run `make up`. This will download dependencies, build and launch a development version of Outline.
```shell
yarn dev
```
To run Outline in production mode:
```shell
yarn start
```
## Development
### Server
To enable debugging statements, set the following env vars:
To enable debugging statements, add the following to your `.env` file:
```
DEBUG=sql,cache,presenters

39
docker-compose.yml Normal file
View File

@@ -0,0 +1,39 @@
version: "3"
services:
redis:
image: redis
ports:
- "6379:6379"
postgres:
image: postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: outline
s3:
image: lphoward/fake-s3
ports:
- "4569:4569"
volumes:
- ./fakes3:/fakes3_root
outline:
image: outline:v001
command: yarn dev
build:
context: .
dockerfile: Dockerfile
args:
pull: 1
ports:
- "3000:3000"
volumes:
- .:/opt/outline
depends_on:
- postgres
- redis
- s3
environment:
NODE_PATH: "/opt/outline/node_modules:/opt/node_modules"
PATH: "/opt/outline/node_modules/.bin:/opt/node_modules/.bin:$PATH"

View File

@@ -1,8 +1,7 @@
// @flow
import uuid from 'uuid';
import Router from 'koa-router';
import { makePolicy, signPolicy } from '../utils/s3';
import { makePolicy, signPolicy, publicS3Endpoint } from '../utils/s3';
import auth from './middlewares/authentication';
import { presentUser } from '../presenters';
@@ -21,11 +20,12 @@ router.post('user.s3Upload', auth(), async ctx => {
const s3Key = uuid.v4();
const key = `uploads/${ctx.state.user.id}/${s3Key}/${filename}`;
const policy = makePolicy();
const endpoint = publicS3Endpoint();
ctx.body = {
data: {
maxUploadSize: process.env.AWS_S3_UPLOAD_MAX_SIZE,
uploadUrl: process.env.AWS_S3_UPLOAD_BUCKET_URL,
uploadUrl: endpoint,
form: {
AWSAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
'Cache-Control': 'max-age=31557600',
@@ -37,7 +37,7 @@ router.post('user.s3Upload', auth(), async ctx => {
},
asset: {
contentType: kind,
url: `${process.env.AWS_S3_UPLOAD_BUCKET_URL}${key}`,
url: `${endpoint}/${key}`,
name: filename,
size,
},

View File

@@ -6,15 +6,10 @@ import invariant from 'invariant';
import fetch from 'isomorphic-fetch';
import bugsnag from 'bugsnag';
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
const AWS_S3_UPLOAD_BUCKET_NAME = process.env.AWS_S3_UPLOAD_BUCKET_NAME;
const makePolicy = () => {
export const makePolicy = () => {
const policy = {
conditions: [
{ bucket: process.env.AWS_S3_UPLOAD_BUCKET_NAME },
@@ -32,7 +27,7 @@ const makePolicy = () => {
return new Buffer(JSON.stringify(policy)).toString('base64');
};
const signPolicy = (policy: any) => {
export const signPolicy = (policy: any) => {
invariant(AWS_SECRET_ACCESS_KEY, 'AWS_SECRET_ACCESS_KEY not set');
const signature = crypto
.createHmac('sha1', AWS_SECRET_ACCESS_KEY)
@@ -42,16 +37,33 @@ const signPolicy = (policy: any) => {
return signature;
};
const uploadToS3FromUrl = async (url: string, key: string) => {
const s3 = new AWS.S3();
export const publicS3Endpoint = () => {
// lose trailing slash if there is one and convert fake-s3 url to localhost
// for access outside of docker containers in local development
const host = process.env.AWS_S3_UPLOAD_BUCKET_URL.replace(
's3:',
'localhost:'
).replace(/\/$/, '');
return `${host}/${process.env.AWS_S3_UPLOAD_BUCKET_NAME}`;
};
export const uploadToS3FromUrl = async (url: string, key: string) => {
const s3 = new AWS.S3({
s3ForcePathStyle: true,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
endpoint: new AWS.Endpoint(process.env.AWS_S3_UPLOAD_BUCKET_URL),
});
invariant(AWS_S3_UPLOAD_BUCKET_NAME, 'AWS_S3_UPLOAD_BUCKET_NAME not set');
try {
// $FlowIssue dunno it's fine
// $FlowIssue https://github.com/facebook/flow/issues/2171
const res = await fetch(url);
const buffer = await res.buffer();
await s3
.putObject({
ACL: 'public-read',
Bucket: process.env.AWS_S3_UPLOAD_BUCKET_NAME,
Key: key,
ContentType: res.headers['content-type'],
@@ -59,10 +71,14 @@ const uploadToS3FromUrl = async (url: string, key: string) => {
Body: buffer,
})
.promise();
return `https://s3.amazonaws.com/${AWS_S3_UPLOAD_BUCKET_NAME}/${key}`;
} catch (e) {
bugsnag.notify(e);
const endpoint = publicS3Endpoint();
return `${endpoint}/${key}`;
} catch (err) {
if (process.env.NODE_ENV === 'production') {
bugsnag.notify(err);
} else {
throw err;
}
}
};
export { makePolicy, signPolicy, uploadToS3FromUrl };