├── .gitignore ├── .htaccess ├── LICENSE ├── README.md ├── _redirects ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── paths.js ├── polyfills.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── webpackDevServer.config.js ├── package.json ├── public ├── index.html ├── manifest.json └── static │ ├── livechat.png │ └── octopus.png ├── readme-assets ├── auth-provider.png ├── config.png ├── create-page.png ├── domain.png ├── edit-menu.png ├── edit-page.png ├── index.png ├── new-page.png ├── rules.png └── system-diagram.png ├── scripts ├── build.js ├── start.js └── test.js ├── src ├── App.css ├── App.js ├── App.test.js ├── components │ ├── markdown │ │ ├── Markdown.js │ │ ├── hljs-github.css │ │ └── index.js │ ├── menu-search │ │ ├── MenuSearch.css │ │ ├── MenuSearch.js │ │ └── index.js │ ├── page │ │ ├── Page.css │ │ ├── Page.js │ │ └── index.js │ ├── people-online │ │ ├── PeopleOnline.js │ │ └── index.js │ └── person │ │ ├── Person.js │ │ └── index.js ├── config │ ├── config.template.js │ └── index.js ├── containers │ └── page │ │ ├── PageContainer.js │ │ └── index.js ├── css │ ├── buttons.css │ ├── markdown.css │ ├── reset.css │ └── tooltips.css ├── index.js ├── markdown-plugins │ ├── diagram │ │ ├── diagram.css │ │ └── diagram.js │ ├── flowchart │ │ └── flowchart.js │ ├── google-storage-images │ │ └── google-storage-images.js │ ├── heading-anchors │ │ ├── heading-anchors.css │ │ └── heading-anchors.js │ └── react-router-links │ │ └── react-router-links.js ├── registerServiceWorker.js ├── templates │ ├── company-structure.js │ ├── diagrams-help.js │ ├── index-page.js │ ├── index.js │ ├── menu.js │ ├── new-page.js │ └── product-roadmap.js └── utils │ ├── Firebase.js │ ├── OnlineTracker.js │ ├── Utils.js │ └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # config 4 | /config/config.php 5 | 6 | # dependencies 7 | /node_modules 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # PHP Storm files 27 | .idea 28 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options FollowSymLinks MultiViews 3 | RewriteEngine on 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteRule . index.html [L] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LiveChat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Octopus 2 | 3 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 4 | 5 | **🐙 Octopus** is a server-less, easy-to-setup internal wiki page with built-in software diagrams support. It uses **Markdown** for writing content and enhances it with [Graphviz](http://graphviz.org) and [flowchart.js](http://flowchart.js.org/) diagrams for easy software documentation. 6 | 7 | Here's a default wiki page: 8 | 9 | 10 | 11 | 12 | 13 | ### Technology 14 | 15 | Octopus is a [JAMstack](https://jamstack.org/) app that uses [Google Firebase](https://firebase.google.com/) as its backend. You do not need a web server (such as Apache) or a database (like MySQL) to have it up and running. You can deploy the app to a CDN for free using [Netlify](https://www.netlify.com/). 16 | 17 | #### With Octopus, you can: 18 | * require users to be signed in to read or edit the wiki (supported providers: Google, GitHub, Email/Password, Twitter and Phone), 19 | * create access rules for reading and editing (restrict particular users or auth domains), 20 | * host wiki page under a custom domain. 21 | 22 | #### Features 23 | * create new wiki pages with Markdown language, 24 | * edit menu (using Markdown) to easily link to wiki pages, 25 | * include [Graphviz](http://www.graphviz.org/Gallery.php) and [flowchart.js](http://flowchart.js.org/) diagrams inside your Markdown content, 26 | * see who is currently reading your wiki articles, 27 | * adjust your Wiki front-end with React ([create-react-app](https://github.com/facebookincubator/create-react-app)). 28 | 29 | 30 | #### Example system diagram 31 | 32 | 33 | 34 | 35 | #### Page editing with Markdown 36 | 37 | 38 | 39 | 40 | ## Installation 41 | 1. Clone this repository to your computer. 42 | 2. Copy config template file (`src/config/config.template.js`) to `src/config/config.js`. 43 | 44 | ``` 45 | cp src/config/config.template.js src/config/config.js 46 | ``` 47 | 48 | > Note: you can safely commit `src/config/config.js` file to a repository. This file does not store sensitive credentials - all config variables will be publicly available in the `.js` file anyway. 49 | 50 | 3. Create a new Firebase project here: https://firebase.google.com/ 51 | 4. Pick "Add Firebase to your web app" and copy the config to `src/config/config.js` created in step 2. 52 | 53 | 54 | 55 | 56 |

57 | 58 | 5. In Firebase Console, go to **Authentication** > **Sign-in method**, choose a provider and enable it. Octopus has been tested with Google and GitHub providers. 59 | 60 | 61 | 62 |

63 | 64 | 6. Still in **Authentication** > **Sign-in method** section, add the domain that will host your app to **Authorized Domains** list. 65 | 66 | 67 | 68 |

69 | 70 | 7. Build the app: 71 | ``` 72 | npm install 73 | npm run build 74 | ``` 75 | 76 | 8. Deploy the `build/` directory to your webserver. 77 | 78 | ## Server-less deployment 79 | You can skip hosting the app on your webserver (step 8 from installation guide above). All you need is to deploy the app to a cloud-based CDN provider. 80 | 81 | We recommend using [Netlify](https://www.netlify.com/). 82 | 83 | It will automatically deploy your forked Octopus GitHub repo to production after each commit. It will also prepare a ready-to-use URL that all your users can access immediately. Later on, you can set up a custom domain to make it look more professional. 84 | 85 | Note: when deploying Octopus via Netlify, fill in the following details in the setup wizard: 86 | 87 | > Build command: `npm run build`
88 | > Publish directory: `build` 89 | 90 | ## Security 91 | By default, any user can view and edit your wiki page. Probably this is not what you expect! 92 | 93 | To make your content accessible only to your company, go to **Firebase Console** > **Database** > **Rules** and restrict who can read and edit your wiki page. 94 | 95 | Here's an example rule if you use Google sign-in provider and your company email address ends with **@livechatinc.com**: 96 | 97 | 98 | 99 | 100 |

101 | 102 | ``` 103 | { 104 | "rules": { 105 | ".read": "auth.token.email.endsWith('@livechatinc.com')", 106 | ".write": "auth.token.email.endsWith('@livechatinc.com')" 107 | } 108 | } 109 | ``` 110 | 111 | You can read more about possible security rules in [Realtime Database Rules](https://firebase.google.com/docs/database/security/) documentation. 112 | 113 | ## Using Octopus 114 | Octopus is designed to be very easy to use. 115 | 116 | To create a new page, just enter the URL you wish to create, for example: `https:///test/new-page`. If such page has not been created yet, you will see the following screen: 117 | 118 | 119 | 120 | 121 |

122 | 123 | When you click "Create this page", new page will be created and filled in with default content, ready to edit: 124 | 125 | 126 | 127 | 128 |

129 | 130 | Once the page is ready, you can easily include it in the menu: 131 | 132 | 133 | 134 | 135 |

136 | 137 | That's it. Send all your teammates link to Octopus and they're ready to contribute! 138 | 139 |
140 | 141 | ## Project background 142 | Why did we build Octopus? 143 | 144 | We've struggled with finding an easy-to-use software for documenting internal systems at [LiveChat](https://www.livechatinc.com/). 145 | 146 | We wanted it to be easy to contribute for **non-technical people** and include easily **editable software diagrams**. We couldn't find a satisfying product, so we've built one. 147 | 148 | ## Built with 149 | * React via [create-react-app](https://github.com/facebookincubator/create-react-app). 150 | * [viz.js](https://github.com/mdaines/viz.js/) 151 | * [flowchart.js](https://github.com/adrai/flowchart.js) 152 | * [Firebase](https://firebase.google.com/) 153 | 154 | ## Changelog 155 | * 15.09.2017 - "Open as .png" option 156 | * 13.09.2017 - Initial release 157 | 158 | ## Authors 159 | [Bartosz Olchówka](https://twitter.com/bolchowka/) / CTO @ [LiveChat](https://www.livechatinc.com/) 160 | 161 | ## License 162 | This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details 163 | -------------------------------------------------------------------------------- /_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. 31 | // https://github.com/motdotla/dotenv 32 | dotenvFiles.forEach(dotenvFile => { 33 | if (fs.existsSync(dotenvFile)) { 34 | require('dotenv').config({ 35 | path: dotenvFile, 36 | }); 37 | } 38 | }); 39 | 40 | // We support resolving modules according to `NODE_PATH`. 41 | // This lets you use absolute paths in imports inside large monorepos: 42 | // https://github.com/facebookincubator/create-react-app/issues/253. 43 | // It works similar to `NODE_PATH` in Node itself: 44 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 45 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 46 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 47 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 48 | // We also resolve them to make sure all tools using them work consistently. 49 | const appDirectory = fs.realpathSync(process.cwd()); 50 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 51 | .split(path.delimiter) 52 | .filter(folder => folder && !path.isAbsolute(folder)) 53 | .map(folder => path.resolve(appDirectory, folder)) 54 | .join(path.delimiter); 55 | 56 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 57 | // injected into the application via DefinePlugin in Webpack configuration. 58 | const REACT_APP = /^REACT_APP_/i; 59 | 60 | function getClientEnvironment(publicUrl) { 61 | const raw = Object.keys(process.env) 62 | .filter(key => REACT_APP.test(key)) 63 | .reduce( 64 | (env, key) => { 65 | env[key] = process.env[key]; 66 | return env; 67 | }, 68 | { 69 | // Useful for determining whether we’re running in production mode. 70 | // Most importantly, it switches React into the correct mode. 71 | NODE_ENV: process.env.NODE_ENV || 'development', 72 | // Useful for resolving the correct path to static assets in `public`. 73 | // For example, . 74 | // This should only be used as an escape hatch. Normally you would put 75 | // images into the `src` and `import` them in code to get their paths. 76 | PUBLIC_URL: publicUrl, 77 | } 78 | ); 79 | // Stringify all values so we can feed into Webpack DefinePlugin 80 | const stringified = { 81 | 'process.env': Object.keys(raw).reduce( 82 | (env, key) => { 83 | env[key] = JSON.stringify(raw[key]); 84 | return env; 85 | }, 86 | {} 87 | ), 88 | }; 89 | 90 | return { raw, stringified }; 91 | } 92 | 93 | module.exports = getClientEnvironment; 94 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right