├── .gitignore ├── .travis.yml ├── Apps.md ├── Hosts.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── apps ├── angular-template │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── ng-add-pug-loader.js │ ├── package-lock.json │ ├── package.json │ ├── prerender.ts │ ├── scripts │ │ ├── compare-versions.js │ │ ├── new-component.js │ │ ├── new-service.js │ │ └── update-angular.sh │ ├── server.ts │ ├── src │ │ ├── 404.html │ │ ├── app │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── app.routing.ts │ │ │ ├── app.server.module.ts │ │ │ ├── home │ │ │ │ ├── home.component.ts │ │ │ │ └── home.pug │ │ │ ├── log-in-modal │ │ │ │ ├── log-in-modal.component.ts │ │ │ │ └── log-in-modal.pug │ │ │ ├── navbar │ │ │ │ ├── navbar.component.ts │ │ │ │ └── navbar.pug │ │ │ ├── services │ │ │ │ ├── onedb.service.ts │ │ │ │ └── platform.service.ts │ │ │ └── styles │ │ │ │ ├── _variables.scss │ │ │ │ ├── app.scss │ │ │ │ └── styles.scss │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.server.ts │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── tsconfig.app.json │ │ └── tsconfig.server.json │ ├── static.paths.js │ ├── tsconfig.json │ ├── webpack.cli-additions.js │ └── webpack.server.config.js ├── chat │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── ng-add-pug-loader.js │ ├── package-lock.json │ ├── package.json │ ├── prerender.ts │ ├── scripts │ │ ├── compare-versions.js │ │ ├── new-component.js │ │ ├── new-service.js │ │ └── update-angular.sh │ ├── server.js │ ├── server.ts │ ├── src │ │ ├── 404.html │ │ ├── app │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── app.routing.ts │ │ │ ├── app.server.module.ts │ │ │ ├── chat │ │ │ │ ├── chat.component.ts │ │ │ │ └── chat.pug │ │ │ ├── home │ │ │ │ ├── home.component.ts │ │ │ │ └── home.pug │ │ │ ├── log-in-modal │ │ │ │ ├── log-in-modal.component.ts │ │ │ │ └── log-in-modal.pug │ │ │ ├── navbar │ │ │ │ ├── navbar.component.ts │ │ │ │ └── navbar.pug │ │ │ ├── rooms │ │ │ │ ├── rooms.component.ts │ │ │ │ └── rooms.pug │ │ │ ├── services │ │ │ │ ├── onedb.service.ts │ │ │ │ └── platform.service.ts │ │ │ └── styles │ │ │ │ ├── _variables.scss │ │ │ │ ├── app.scss │ │ │ │ └── styles.scss │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── img │ │ │ │ ├── Icon.png │ │ │ │ ├── Logo-white.svg │ │ │ │ ├── Logo.png │ │ │ │ └── Logo.svg │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.server.ts │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── tsconfig.app.json │ │ └── tsconfig.server.json │ ├── static.paths.js │ ├── tsconfig.json │ ├── types │ │ ├── conversation.acl.json │ │ ├── conversation.schema.json │ │ ├── message.acl.json │ │ └── message.schema.json │ ├── webpack.cli-additions.js │ └── webpack.server.config.js ├── data-explorer │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── ng-add-pug-loader.js │ ├── package-lock.json │ ├── package.json │ ├── prerender.ts │ ├── scripts │ │ ├── compare-versions.js │ │ ├── new-component.js │ │ ├── new-service.js │ │ └── update-angular.sh │ ├── server.js │ ├── server.ts │ ├── src │ │ ├── 404.html │ │ ├── app │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── app.routing.ts │ │ │ ├── app.server.module.ts │ │ │ ├── home │ │ │ │ ├── home.component.ts │ │ │ │ └── home.pug │ │ │ ├── item │ │ │ │ ├── item.component.ts │ │ │ │ └── item.pug │ │ │ ├── json-schema │ │ │ │ ├── json-schema-editor.component.ts │ │ │ │ ├── json-schema-editor.pug │ │ │ │ ├── schema-label.component.ts │ │ │ │ ├── schema-label.pug │ │ │ │ └── util.ts │ │ │ ├── log-in-modal │ │ │ │ ├── log-in-modal.component.ts │ │ │ │ └── log-in-modal.pug │ │ │ ├── namespace │ │ │ │ ├── namespace.component.ts │ │ │ │ └── namespace.pug │ │ │ ├── navbar │ │ │ │ ├── navbar.component.ts │ │ │ │ └── navbar.pug │ │ │ ├── services │ │ │ │ ├── onedb.service.ts │ │ │ │ └── platform.service.ts │ │ │ └── styles │ │ │ │ ├── _variables.scss │ │ │ │ ├── app.scss │ │ │ │ ├── bootswatch.scss │ │ │ │ └── styles.scss │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── img │ │ │ │ ├── Icon.png │ │ │ │ ├── Logo-white.svg │ │ │ │ ├── Logo.png │ │ │ │ └── Logo.svg │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.server.ts │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── tsconfig.app.json │ │ └── tsconfig.server.json │ ├── static.paths.js │ ├── tsconfig.json │ ├── webpack.cli-additions.js │ └── webpack.server.config.js ├── minimal │ ├── README.md │ ├── index.html │ ├── onedb-client.min.js │ ├── types │ │ ├── status.acl.json │ │ └── status.schema.json │ └── viewer.html └── todo │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── ng-add-pug-loader.js │ ├── package-lock.json │ ├── package.json │ ├── prerender.ts │ ├── scripts │ ├── compare-versions.js │ ├── new-component.js │ ├── new-service.js │ └── update-angular.sh │ ├── server.js │ ├── server.ts │ ├── src │ ├── 404.html │ ├── app │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.routing.ts │ │ ├── app.server.module.ts │ │ ├── autofocus.directive.ts │ │ ├── home │ │ │ ├── home.component.ts │ │ │ └── home.pug │ │ ├── list │ │ │ ├── list.component.ts │ │ │ └── list.pug │ │ ├── log-in-modal │ │ │ ├── log-in-modal.component.ts │ │ │ └── log-in-modal.pug │ │ ├── navbar │ │ │ ├── navbar.component.ts │ │ │ └── navbar.pug │ │ ├── services │ │ │ ├── onedb.service.ts │ │ │ └── platform.service.ts │ │ └── styles │ │ │ ├── _variables.scss │ │ │ ├── app.scss │ │ │ └── styles.scss │ ├── assets │ │ ├── .gitkeep │ │ └── img │ │ │ ├── Icon.png │ │ │ ├── Logo-white.svg │ │ │ ├── Logo.png │ │ │ └── Logo.svg │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.server.ts │ ├── main.ts │ ├── polyfills.ts │ ├── tsconfig.app.json │ └── tsconfig.server.json │ ├── static.paths.js │ ├── tsconfig.json │ ├── types │ ├── item.schema.json │ └── list.schema.json │ ├── webpack.cli-additions.js │ └── webpack.server.config.js ├── client ├── browser.js ├── dist │ ├── onedb-client.min.js │ └── onedb-client.min.js.map ├── index.js ├── lib │ ├── client.js │ └── login-form.js ├── package-lock.json ├── package.json ├── test │ ├── client.js │ └── multiple-instances.js └── webpack.config.js ├── cmd ├── bin │ └── onedb ├── cmd.js ├── index.js ├── lib │ ├── files.js │ ├── login.js │ ├── namespace.js │ └── serve.js ├── package-lock.json └── package.json ├── package-lock.json ├── package.json ├── server ├── .gitignore ├── index.js ├── lib │ ├── config.js │ ├── database.js │ ├── db-init.js │ ├── db-util.js │ ├── error-guard.js │ ├── fail.js │ ├── middleware │ │ ├── authenticate.js │ │ ├── authorize.js │ │ ├── checkUsername.js │ │ └── index.js │ ├── routes │ │ ├── crud.js │ │ ├── index.js │ │ ├── info.js │ │ └── users.js │ ├── server.js │ ├── util │ │ ├── index.js │ │ ├── iterateSchema.js │ │ ├── randomName.js │ │ └── scopes.js │ └── validate.js ├── namespaces │ ├── core │ │ ├── acl.js │ │ ├── info.js │ │ ├── namespace.js │ │ └── schema.js │ └── system │ │ ├── authorization_token.js │ │ ├── usage.js │ │ ├── user.js │ │ └── user_private.js ├── package-lock.json ├── package.json ├── test │ ├── database.js │ ├── db-util.js │ ├── multiserver.js │ ├── server.js │ └── validate.js └── web │ └── views │ ├── authorize.pug │ ├── layout.pug │ └── reset_password.pug └── web ├── docs ├── LucyBot.yml ├── assets │ └── img │ │ ├── Icon.png │ │ ├── Logo-white.svg │ │ ├── Logo.png │ │ ├── Logo.svg │ │ └── quickstart_screenshot.png ├── markdown │ ├── Authorization.md │ ├── Client_API.md │ ├── Data_Schemas.md │ ├── End_to_End.md │ ├── HTTP.md │ ├── Host_an_Instance.md │ ├── Multiple_Instances.md │ ├── Overview.md │ └── Quickstart.md ├── styles │ ├── bootstrap.scss │ └── styles.css └── templates │ └── navbar.html ├── homepage ├── img │ ├── Icon.png │ ├── Logo-white.svg │ ├── Logo.png │ ├── Logo.svg │ └── team │ │ ├── andrew.jpg │ │ ├── bob.png │ │ ├── bobby.jpg │ │ ├── joe.jpg │ │ ├── keep │ │ └── throop.jpg ├── styles.css └── views │ └── index.pug └── img ├── Icon.png ├── Logo-white.svg ├── Logo.png └── Logo.svg /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | apps/**/dist 3 | web/docs/dist 4 | web/homepage/index.html 5 | OneDB.yml 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "10" 5 | script: 6 | - cd server && npm install && cd .. 7 | - cd client && npm install && cd .. 8 | - cd cmd && npm install && cd .. 9 | - npm test 10 | notifications: 11 | email: 12 | recipients: 13 | - bobby@datafire.io 14 | on_success: change 15 | on_failure: always 16 | 17 | -------------------------------------------------------------------------------- /Apps.md: -------------------------------------------------------------------------------- 1 | # OneDB Apps 2 | 3 | > Have an app you'd like to share? Submit a pull request! 4 | 5 | The following is a list of apps built on OneDB: 6 | 7 | * [OneChat](https://chat.one-db.org) - Public chatrooms on any topic 8 | * [OneToDo](https://todo.one-db.org) - Create to-do lists 9 | -------------------------------------------------------------------------------- /Hosts.md: -------------------------------------------------------------------------------- 1 | # OneDB Hosts 2 | 3 | > Hosting a public OneDB instance? Submit a pull request to add it here. 4 | 5 | The following hosts are publicly available for new users: 6 | * `https://one-db.datafire.io` - Supports all namespaces, up to 10MB of data per user for free 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Bobby Brennan 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 | -------------------------------------------------------------------------------- /apps/angular-template/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | docs/ 3 | 4 | .idea 5 | .DS_Store 6 | morgan.log 7 | 8 | # Built # 9 | /__build__/ 10 | /__server_build__/ 11 | /node_modules/ 12 | /typings/ 13 | /tsd_typings/ 14 | /dist/ 15 | /dist-server/ 16 | /compiled/ 17 | 18 | # Node # 19 | npm-debug.log 20 | /npm-debug.log.* 21 | 22 | # Webpack # 23 | webpack.records.json 24 | 25 | # Angular # 26 | *.ngfactory.ts 27 | *.css.shim.ts 28 | *.ngsummary.json 29 | *.metadata.json 30 | *.shim.ngstyle.ts 31 | -------------------------------------------------------------------------------- /apps/angular-template/README.md: -------------------------------------------------------------------------------- 1 | # Angluar 6.0 Template Project 2 | I use this template as a starter for any new Angular projects. 3 | 4 | Check out [the demo](https://bobby-brennan.github.io/angular6-template) 5 | 6 | #### Features 7 | * Angular Universal (prerendering) 8 | * [Pug](https://pugjs.org) templates instead of HTML 9 | * Bootstrap Sass 10 | * Font Awesome 11 | * Angular HTML5 Router 12 | * Standard navbar/body layout 13 | 14 | ## Running the Demo 15 | 16 | #### Install 17 | 18 | ``` 19 | npm install 20 | ``` 21 | 22 | #### Run (development mode) 23 | Start the server on port 3000: 24 | 25 | ``` 26 | npm run start 27 | ``` 28 | 29 | 30 | #### Build (production) 31 | Build everything and put it in the `dist/` folder: 32 | 33 | ``` 34 | npm run build 35 | ``` 36 | 37 | Or build for GitHub pages by copying `dist/browser` to `docs/`. 38 | Be sure to change the settings in your repo to point GitHub pages to 39 | the `docs/` folder on the `master` branch. 40 | 41 | ## Customizing 42 | 43 | ### Base href 44 | The app uses the base href `/` for development builds, and `/angular6-template` 45 | for production builds (to accomodate GitHub pages, which uses the repository 46 | name in the URL's path). You will probably want to change the base href in 47 | `./src/environments/environment.prod.ts`. 48 | 49 | ### Prerendering 50 | Prerendering is a performance optimization - your page's HTML is generated 51 | at build time, so the user sees a near-instant load of the page, while Angular 52 | loads in the background. 53 | 54 | In this build, only the homepage is prerendered - you can add other routes 55 | by editing `./static.paths.ts`. 56 | 57 | ## New Components 58 | A helper script for creating new components is in `./scripts/new-component.js`. 59 | 60 | ``` 61 | node ./scripts/new-component.js --name "Widget Viewer" 62 | ``` 63 | -------------------------------------------------------------------------------- /apps/angular-template/ng-add-pug-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds the pug-loader inside Angular CLI's webpack config, if not there yet. 3 | * @see https://github.com/danguilherme/ng-cli-pug-loader 4 | */ 5 | const fs = require('fs'); 6 | const commonCliConfig = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js'; 7 | const pugRule = '{ test: /.pug$/, use: [ { loader: "apply-loader" }, { loader: "pug-loader" } ] },'; 8 | 9 | fs.readFile(commonCliConfig, (err, data) => { 10 | if (err) { throw err; } 11 | 12 | const configText = data.toString(); 13 | // make sure we don't add the rule if it already exists 14 | if (configText.indexOf(pugRule) > -1) { return; } 15 | 16 | // Insert the pug webpack rule 17 | const position = configText.indexOf('rules: [') + 8; 18 | const output = [configText.slice(0, position), pugRule, configText.slice(position)].join(''); 19 | const file = fs.openSync(commonCliConfig, 'r+'); 20 | fs.writeFile(file, output); 21 | fs.close(file); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/angular-template/prerender.ts: -------------------------------------------------------------------------------- 1 | // Load zone.js for the server. 2 | import 'zone.js/dist/zone-node'; 3 | import 'reflect-metadata'; 4 | import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; 5 | import { join } from 'path'; 6 | import { chdir } from 'process'; 7 | 8 | import { enableProdMode } from '@angular/core'; 9 | // Faster server renders w/ Prod mode (dev mode never needed) 10 | enableProdMode(); 11 | 12 | // Express Engine 13 | import { ngExpressEngine } from '@nguniversal/express-engine'; 14 | // Import module map for lazy loading 15 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 16 | 17 | import { renderModuleFactory } from '@angular/platform-server'; 18 | 19 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 20 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 21 | 22 | // Get route paths to prerender only static pages 23 | const PATHS = require('./static.paths'); 24 | 25 | const BROWSER_FOLDER = join(process.cwd(), 'browser'); 26 | 27 | // Load the index.html file containing referances to your application bundle. 28 | const index = readFileSync(join('browser', 'index.html'), 'utf8'); 29 | 30 | let prom = Promise.resolve(); 31 | 32 | // Iterate each route path 33 | PATHS.forEach(function (route) { 34 | // Changes current directory to ./dist/browser 35 | chdir(BROWSER_FOLDER); 36 | 37 | // Creates new directories (if not exists) and changes current directory for the nested one 38 | route.split('/').filter(val => val !== '') 39 | .forEach(function (dir) { 40 | if (!existsSync(dir)) { 41 | mkdirSync(dir); 42 | } 43 | chdir(dir); 44 | }); 45 | 46 | // Writes rendered HTML to index.html, replacing the file if it already exists. 47 | prom = prom.then(_ => renderModuleFactory(AppServerModuleNgFactory, { 48 | document: index, 49 | url: route, 50 | extraProviders: [ 51 | provideModuleMap(LAZY_MODULE_MAP) 52 | ] 53 | })).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html)); 54 | }); 55 | -------------------------------------------------------------------------------- /apps/angular-template/scripts/compare-versions.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let path = require('path'); 3 | let args = require('yargs').argv; 4 | 5 | function addDirToVersions(dir, versions) { 6 | fs.readdirSync(dir).forEach(d => { 7 | if (d.indexOf('.') === 0) { 8 | return; 9 | } 10 | if (d.indexOf('@') === 0) { 11 | addDirToVersions(dir + '/' + d, versions); 12 | } else { 13 | let pkg = require(dir + '/' + d + '/package.json'); 14 | versions[d] = pkg.version; 15 | } 16 | }) 17 | } 18 | 19 | let aVersions = {}; 20 | addDirToVersions(path.resolve(args.a + '/node_modules'), aVersions); 21 | let bVersions = {}; 22 | addDirToVersions(path.resolve(args.b + '/node_modules'), bVersions); 23 | 24 | let keys = Object.keys(aVersions); 25 | for (let key in bVersions) { 26 | if (keys.indexOf(key) === -1) keys.push(key); 27 | } 28 | 29 | keys.forEach(key => { 30 | let v1 = aVersions[key]; 31 | let v2 = bVersions[key]; 32 | if (v1 !== v2) { 33 | console.log(key + '\t' + aVersions[key] + '\t' + bVersions[key]); 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /apps/angular-template/scripts/new-component.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let componentCode = function(name, filename) { 5 | return ` 6 | import {Component} from '@angular/core'; 7 | 8 | @Component({ 9 | selector: '${filename}', 10 | templateUrl: './${filename}.pug', 11 | }) 12 | export class ${name}Component { 13 | constructor() {} 14 | } 15 | `.trim() 16 | } 17 | 18 | let viewCode = function(name) { 19 | return ` 20 | h1 ${name} 21 | `.trim(); 22 | } 23 | 24 | const APP_DIR = __dirname + '/../src/app/'; 25 | 26 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 27 | const componentName = args.name.replace(/\s/g, ''); 28 | const componentDir = APP_DIR + filename + '/'; 29 | const componentFile = componentDir + filename + '.component.ts'; 30 | const viewFile = componentDir + filename + '.pug'; 31 | const appFile = APP_DIR + 'app.module.ts'; 32 | 33 | let component = componentCode(componentName, filename); 34 | let view = viewCode(args.name); 35 | 36 | fs.mkdirSync(componentDir); 37 | fs.writeFileSync(viewFile, view); 38 | fs.writeFileSync(componentFile, component); 39 | 40 | let app = fs.readFileSync(appFile, 'utf8'); 41 | let lines = app.split('\n').reverse(); 42 | 43 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.component'/)); 44 | lines.splice(insertImportAt, 0, `import {${componentName}Component} from './${filename}/${filename}.component'`); 45 | 46 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Component,/)); 47 | lines.splice(insertDeclarationAt, 0, ` ${componentName}Component,`) 48 | 49 | lines.reverse(); 50 | fs.writeFileSync(appFile, lines.join('\n')); 51 | -------------------------------------------------------------------------------- /apps/angular-template/scripts/new-service.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let serviceCode = function(name, filename) { 5 | return ` 6 | import {Injectable} from '@angular/core'; 7 | 8 | @Injectable() 9 | export class ${name}Service { 10 | constructor() {} 11 | } 12 | `.trim() 13 | } 14 | 15 | const APP_DIR = __dirname + '/../src/app/'; 16 | 17 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 18 | const serviceName = args.name.replace(/\s/g, ''); 19 | const serviceDir = APP_DIR + 'services/'; 20 | const serviceFile = serviceDir + filename + '.service.ts'; 21 | const appFile = APP_DIR + 'app.module.ts'; 22 | 23 | let service = serviceCode(serviceName, filename); 24 | 25 | fs.writeFileSync(serviceFile, service); 26 | 27 | let app = fs.readFileSync(appFile, 'utf8'); 28 | let lines = app.split('\n').reverse(); 29 | 30 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.service'/)); 31 | lines.splice(insertImportAt, 0, `import {${serviceName}Service} from './services/${filename}.service'`); 32 | 33 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Service,/)); 34 | lines.splice(insertDeclarationAt, 0, ` ${serviceName}Service,`) 35 | 36 | lines.reverse(); 37 | fs.writeFileSync(appFile, lines.join('\n')); 38 | -------------------------------------------------------------------------------- /apps/angular-template/scripts/update-angular.sh: -------------------------------------------------------------------------------- 1 | npm i --save @angular/animations@latest @angular/router@latest @angular/common@latest @angular/compiler@latest @angular/core@latest @angular/forms@latest @angular/http@latest @angular/platform-browser@latest @angular/platform-browser-dynamic@latest @angular/platform-server@latest @angular/cli@latest @angular/compiler-cli@latest @angular/language-service@latest @nguniversal/express-engine@latest @nguniversal/module-map-ngfactory-loader@latest rxjs@latest 2 | -------------------------------------------------------------------------------- /apps/angular-template/server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import 'reflect-metadata'; 3 | import { renderModuleFactory } from '@angular/platform-server'; 4 | import { enableProdMode } from '@angular/core'; 5 | 6 | import * as express from 'express'; 7 | import { join } from 'path'; 8 | import { readFileSync } from 'fs'; 9 | 10 | // Faster server renders w/ Prod mode (dev mode never needed) 11 | enableProdMode(); 12 | 13 | // Express server 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 4000; 17 | const DIST_FOLDER = join(process.cwd(), 'dist'); 18 | 19 | // Our index.html we'll use as our template 20 | const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString(); 21 | 22 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 23 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 24 | 25 | // Express Engine 26 | import { ngExpressEngine } from '@nguniversal/express-engine'; 27 | // Import module map for lazy loading 28 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 29 | 30 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 31 | app.engine('html', ngExpressEngine({ 32 | bootstrap: AppServerModuleNgFactory, 33 | providers: [ 34 | provideModuleMap(LAZY_MODULE_MAP) 35 | ] 36 | })); 37 | 38 | app.set('view engine', 'html'); 39 | app.set('views', join(DIST_FOLDER, 'browser')); 40 | 41 | /* - Example Express Rest API endpoints - 42 | app.get('/api/**', (req, res) => { }); 43 | */ 44 | 45 | // Server static files from /browser 46 | app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), { 47 | maxAge: '1y' 48 | })); 49 | 50 | // ALl regular routes use the Universal engine 51 | app.get('*', (req, res) => { 52 | res.render('index', { req }); 53 | }); 54 | 55 | // Start up the Node server 56 | app.listen(PORT, () => { 57 | console.log(`Node Express server listening on http://localhost:${PORT}`); 58 | }); 59 | -------------------------------------------------------------------------------- /apps/angular-template/src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 |
8 | 9 |
10 | `, 11 | }) 12 | export class AppComponent { 13 | constructor() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouterModule } from '@angular/router'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpModule } from '@angular/http'; 6 | import {APP_BASE_HREF} from '@angular/common'; 7 | 8 | import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; 9 | 10 | import { appRoutes } from './app.routing'; 11 | import { AppComponent } from './app.component'; 12 | import { HomeComponent } from './home/home.component'; 13 | import { NavbarComponent } from './navbar/navbar.component'; 14 | import {LogInModalComponent} from './log-in-modal/log-in-modal.component' 15 | 16 | import {PlatformService} from './services/platform.service'; 17 | import {OneDBService} from './services/onedb.service' 18 | 19 | import { environment } from '../environments/environment'; 20 | 21 | @NgModule({ 22 | imports: [ 23 | BrowserModule.withServerTransition({appId: 'my-app'}), 24 | RouterModule.forRoot(appRoutes), 25 | HttpModule, 26 | FormsModule, 27 | NgbModule.forRoot(), 28 | ], 29 | providers: [ 30 | {provide: APP_BASE_HREF, useValue: environment.baseHref || '/'}, 31 | PlatformService, 32 | OneDBService, 33 | ], 34 | declarations: [ 35 | AppComponent, 36 | HomeComponent, 37 | NavbarComponent, 38 | LogInModalComponent, 39 | ], 40 | bootstrap: [ AppComponent ], 41 | }) 42 | export class AppModule { } 43 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import {HomeComponent} from './home/home.component'; 3 | 4 | export const appRoutes: Routes = [ 5 | { path: '', component: HomeComponent }, 6 | { path: '**', redirectTo: '' }, 7 | ]; 8 | 9 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader'; 4 | 5 | import {AppModule} from './app.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | // The AppServerModule should import your AppModule followed 11 | // by the ServerModule from @angular/platform-server. 12 | AppModule, 13 | ServerModule, 14 | ModuleMapLoaderModule, 15 | ], 16 | // Since the bootstrapped component is not inherited from your 17 | // imported AppModule, it needs to be repeated here. 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppServerModule {} 21 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | declare let window:any; 6 | declare let require:any; 7 | 8 | @Component({ 9 | selector: 'home', 10 | templateUrl: './home.pug', 11 | }) 12 | export class HomeComponent { 13 | @ViewChild('logInModal') logInModal; 14 | error:string; 15 | constructor(public onedb:OneDBService) { 16 | this.onedb.onLogin.subscribe(user => { 17 | console.log(user); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/home/home.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | h1 Welcome to OneDB! 3 | div(*ngIf="onedb.client.hosts.primary.user") 4 | p You're logged in as {{ onedb.client.hosts.primary.user.$.id }} 5 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/log-in-modal/log-in-modal.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {OneDBService} from '../services/onedb.service'; 3 | import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap'; 4 | import {DomSanitizer, SafeHtml} from '@angular/platform-browser' 5 | 6 | @Component({ 7 | selector: 'log-in-modal', 8 | templateUrl: './log-in-modal.pug', 9 | }) 10 | export class LogInModalComponent { 11 | @ViewChild('content') content; 12 | formContent:SafeHtml; 13 | modalRef:any; 14 | 15 | constructor( 16 | private onedb:OneDBService, 17 | private modals: NgbModal, 18 | private sanitizer:DomSanitizer) { 19 | this.refreshForm(); 20 | this.onedb.onLogin.subscribe(instance => { 21 | this.refreshForm(); 22 | if (this.modalRef && instance.user) this.modalRef.close(); 23 | }) 24 | } 25 | 26 | open() { 27 | this.modalRef = this.modals.open(this.content); 28 | } 29 | 30 | refreshForm() { 31 | this.formContent = this.sanitizer.bypassSecurityTrustHtml(this.onedb.client.loginForm()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/log-in-modal/log-in-modal.pug: -------------------------------------------------------------------------------- 1 | ng-template(#content let-c="close" let-d="dismiss") 2 | .modal-header 3 | h4.modal-title 4 | span(*ngIf="!onedb.client.hosts.primary.user") Log In or Sign Up 5 | span(*ngIf="onedb.client.hosts.primary.user") Log Out or Switch Accounts 6 | button.close(type="button", class="close", aria-label="Close", (click)="d('Cross click')") 7 | span(aria-hidden="true") × 8 | .modal-body 9 | div([innerHtml]="formContent") 10 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import {ViewChild, Component} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | @Component({ 6 | selector: 'navbar', 7 | templateUrl: './navbar.pug', 8 | }) 9 | export class NavbarComponent { 10 | @ViewChild('logInModal') logInModal; 11 | constructor(public router: Router, public onedb:OneDBService) {} 12 | } 13 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/navbar/navbar.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | nav.navbar.navbar-expand-lg.navbar-dark.bg-dark 3 | .container 4 | a.navbar-brand(routerLink="/") OneDB Sample App 5 | div 6 | ul.navbar-nav.ml-auto 7 | li 8 | a.nav-link((click)="logInModal.open()") 9 | i.fa.fa-left.fa-users 10 | span([ngSwitch]="!!onedb.client.hosts.primary.user") 11 | span(*ngSwitchCase="true") 12 | span {{onedb.client.hosts.primary.displayName}} 13 | span(*ngSwitchCase="false") Sign In 14 | 15 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/services/onedb.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | 4 | declare let window:any; 5 | declare let require:any; 6 | const Client = require('onedb-client').Client; 7 | const CORE_HOST = 'https://one-db.datafire.io'; 8 | 9 | const STORAGE_KEY = 'onedb_auth'; 10 | 11 | @Injectable() 12 | export class OneDBService { 13 | client:any; 14 | user:any; 15 | 16 | onLogin = new BehaviorSubject(null); 17 | 18 | constructor(private zone:NgZone) { 19 | window.onedbService = this; 20 | this.client = new Client({ 21 | hosts: { 22 | core: { 23 | location: CORE_HOST, 24 | } 25 | }, 26 | onLogin: user => { 27 | this.zone.run(_ => this.onLogin.next(user)); 28 | }, 29 | }); 30 | this.maybeRestore(); 31 | this.onLogin.subscribe(user => { 32 | this.user = user; 33 | if (!window.localStorage) return 34 | const toStore = { 35 | hosts: this.client.hosts, 36 | }; 37 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore)) 38 | }) 39 | } 40 | 41 | async maybeRestore() { 42 | if (!window.localStorage) return; 43 | let existing:any = window.localStorage.getItem(STORAGE_KEY); 44 | if (!existing) return; 45 | existing = JSON.parse(existing); 46 | if (!existing || !existing.hosts) return; 47 | let hosts = Object.assign({}, existing.hosts, {core: {location: CORE_HOST}}) 48 | await this.client.setHosts(hosts); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/services/platform.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, PLATFORM_ID } from '@angular/core' 2 | import { isPlatformBrowser, isPlatformServer } from '@angular/common' 3 | 4 | @Injectable() 5 | export class PlatformService { 6 | constructor(@Inject(PLATFORM_ID) private platformId: any) { } 7 | 8 | public isBrowser(): boolean { 9 | return isPlatformBrowser(this.platformId) 10 | } 11 | 12 | public isServer(): boolean { 13 | return isPlatformServer(this.platformId) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/styles/_variables.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/angular-template/src/app/styles/_variables.scss -------------------------------------------------------------------------------- /apps/angular-template/src/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | a { 2 | cursor: pointer; 3 | } 4 | 5 | .navbar { 6 | border-radius: 0px; 7 | margin-bottom: 30px; 8 | } 9 | 10 | body { 11 | position: relative; 12 | } 13 | 14 | app > .container { 15 | padding-bottom: 72px; 16 | } 17 | 18 | footer { 19 | position: absolute; 20 | bottom: 0; 21 | left: 0; 22 | right: 0; 23 | 24 | background-color: #fff; 25 | padding-top: 20px; 26 | padding-bottom: 20px; 27 | } 28 | 29 | footer, footer a { 30 | color: #3E3F3A; 31 | } 32 | 33 | .fa-left { 34 | margin-right: 8px; 35 | } 36 | .fa-right { 37 | margin-left: 8px; 38 | } 39 | 40 | .btn-min-width { 41 | min-width: 100px; 42 | } 43 | -------------------------------------------------------------------------------- /apps/angular-template/src/app/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | @import '~bootstrap/scss/bootstrap'; 4 | 5 | @import './app.scss'; 6 | 7 | -------------------------------------------------------------------------------- /apps/angular-template/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/angular-template/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/angular-template/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | baseHref: '/', 4 | }; 5 | -------------------------------------------------------------------------------- /apps/angular-template/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | baseHref: '/', 9 | }; 10 | -------------------------------------------------------------------------------- /apps/angular-template/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/angular-template/src/favicon.ico -------------------------------------------------------------------------------- /apps/angular-template/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OneDB 7 | 8 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /apps/angular-template/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /apps/angular-template/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/angular-template/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | import 'babel-polyfill'; 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /apps/angular-template/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/angular-template/src/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | // Set the module format to "commonjs": 7 | "module": "commonjs", 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "test.ts", 12 | "**/*.spec.ts" 13 | ], 14 | // Add "angularCompilerOptions" with the AppServerModule you wrote 15 | // set as the "entryModule". 16 | "angularCompilerOptions": { 17 | "entryModule": "app/app.server.module#AppServerModule" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/angular-template/static.paths.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '/', 3 | ]; 4 | -------------------------------------------------------------------------------- /apps/angular-template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types", 13 | "typings.d.ts" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "types": ["node"], 20 | "module": "es2015", 21 | "baseUrl": "./" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/angular-template/webpack.cli-additions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const commonCliConfig = 'node_modules/@angular/cli/models/webpack-configs/common.js'; 3 | const addition_rules = ` 4 | { test: /\.(pug|jade)$/, loader: 'apply-loader' }, 5 | { test: /\.(pug|jade)$/, 6 | loader: 'pug-loader', 7 | query: { doctype: 'html', plugins: [require('pug-plugin-ng')] }, 8 | }, { 9 | test: /\.json$/, 10 | use: [{ 11 | loader: 'json-loader', 12 | }] 13 | }, 14 | { 15 | test: /\.js$/, 16 | use: [{ 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['env'], 20 | } 21 | }], 22 | }, 23 | { test: /\.md$/, use: [{ loader: 'raw-loader' }, { loader: 'markdown-loader', }] } 24 | ,`; // make sure to have this last comma 25 | 26 | fs.readFile(commonCliConfig, (err, data) => { 27 | 28 | if (err) { throw err; } 29 | 30 | const configText = data.toString(); 31 | // make sure we don't include it (if we've already done this) 32 | if (configText.indexOf(addition_rules) > -1) { return; } 33 | 34 | console.log('-- Inserting additional webpack rules to node_modules CLI -- '); 35 | 36 | const position = configText.indexOf('rules: [') + 8; 37 | const output = [configText.slice(0, position), addition_rules, configText.slice(position)].join(''); 38 | const file = fs.openSync(commonCliConfig, 'r+'); 39 | 40 | fs.writeFile(file, output); 41 | fs.close(file); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /apps/angular-template/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | // Work around for https://github.com/angular/angular-cli/issues/7200 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | // This is our Express server for Dynamic universal 9 | server: './server.ts', 10 | // This is an example of Static prerendering (generative) 11 | prerender: './prerender.ts' 12 | }, 13 | target: 'node', 14 | resolve: { extensions: ['.ts', '.js'] }, 15 | // Make sure we include all node_modules etc 16 | externals: [/(node_modules|main\..*\.js)/,], 17 | output: { 18 | // Puts the output at the root of the dist folder 19 | path: path.join(__dirname, 'dist'), 20 | filename: '[name].js' 21 | }, 22 | module: { 23 | rules: [ 24 | { test: /\.ts$/, use: {loader: 'ts-loader', options: {}} } 25 | ] 26 | }, 27 | plugins: [ 28 | new webpack.ContextReplacementPlugin( 29 | // fixes WARNING Critical dependency: the request of a dependency is an expression 30 | /(.+)?angular(\\|\/)core(.+)?/, 31 | path.join(__dirname, 'src'), // location of your src 32 | {} // a map of your routes 33 | ), 34 | new webpack.ContextReplacementPlugin( 35 | // fixes WARNING Critical dependency: the request of a dependency is an expression 36 | /(.+)?express(\\|\/)(.+)?/, 37 | path.join(__dirname, 'src'), 38 | {} 39 | ) 40 | ] 41 | } 42 | 43 | -------------------------------------------------------------------------------- /apps/chat/.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | 3 | .idea 4 | .DS_Store 5 | morgan.log 6 | 7 | # Built # 8 | /__build__/ 9 | /__server_build__/ 10 | /node_modules/ 11 | /typings/ 12 | /tsd_typings/ 13 | /dist-server/ 14 | /compiled/ 15 | 16 | # Node # 17 | npm-debug.log 18 | /npm-debug.log.* 19 | 20 | # Webpack # 21 | webpack.records.json 22 | 23 | # Angular # 24 | *.ngfactory.ts 25 | *.css.shim.ts 26 | *.ngsummary.json 27 | *.metadata.json 28 | *.shim.ngstyle.ts 29 | -------------------------------------------------------------------------------- /apps/chat/README.md: -------------------------------------------------------------------------------- 1 | # Angluar 6.0 Template Project 2 | I use this template as a starter for any new Angular projects. 3 | 4 | Check out [the demo](https://bobby-brennan.github.io/angular6-template) 5 | 6 | #### Features 7 | * Angular Universal (prerendering) 8 | * [Pug](https://pugjs.org) templates instead of HTML 9 | * Bootstrap Sass 10 | * Font Awesome 11 | * Angular HTML5 Router 12 | * Standard navbar/body layout 13 | 14 | ## Running the Demo 15 | 16 | #### Install 17 | 18 | ``` 19 | npm install 20 | ``` 21 | 22 | #### Run (development mode) 23 | Start the server on port 3000: 24 | 25 | ``` 26 | npm run start 27 | ``` 28 | 29 | 30 | #### Build (production) 31 | Build everything and put it in the `dist/` folder: 32 | 33 | ``` 34 | npm run build 35 | ``` 36 | 37 | Or build for GitHub pages by copying `dist/browser` to `docs/`. 38 | Be sure to change the settings in your repo to point GitHub pages to 39 | the `docs/` folder on the `master` branch. 40 | 41 | ## Customizing 42 | 43 | ### Base href 44 | The app uses the base href `/` for development builds, and `/angular6-template` 45 | for production builds (to accomodate GitHub pages, which uses the repository 46 | name in the URL's path). You will probably want to change the base href in 47 | `./src/environments/environment.prod.ts`. 48 | 49 | ### Prerendering 50 | Prerendering is a performance optimization - your page's HTML is generated 51 | at build time, so the user sees a near-instant load of the page, while Angular 52 | loads in the background. 53 | 54 | In this build, only the homepage is prerendered - you can add other routes 55 | by editing `./static.paths.ts`. 56 | 57 | ## New Components 58 | A helper script for creating new components is in `./scripts/new-component.js`. 59 | 60 | ``` 61 | node ./scripts/new-component.js --name "Widget Viewer" 62 | ``` 63 | -------------------------------------------------------------------------------- /apps/chat/ng-add-pug-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds the pug-loader inside Angular CLI's webpack config, if not there yet. 3 | * @see https://github.com/danguilherme/ng-cli-pug-loader 4 | */ 5 | const fs = require('fs'); 6 | const commonCliConfig = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js'; 7 | const pugRule = '{ test: /.pug$/, use: [ { loader: "apply-loader" }, { loader: "pug-loader" } ] },'; 8 | 9 | fs.readFile(commonCliConfig, (err, data) => { 10 | if (err) { throw err; } 11 | 12 | const configText = data.toString(); 13 | // make sure we don't add the rule if it already exists 14 | if (configText.indexOf(pugRule) > -1) { return; } 15 | 16 | // Insert the pug webpack rule 17 | const position = configText.indexOf('rules: [') + 8; 18 | const output = [configText.slice(0, position), pugRule, configText.slice(position)].join(''); 19 | const file = fs.openSync(commonCliConfig, 'r+'); 20 | fs.writeFile(file, output); 21 | fs.close(file); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/chat/prerender.ts: -------------------------------------------------------------------------------- 1 | // Load zone.js for the server. 2 | import 'zone.js/dist/zone-node'; 3 | import 'reflect-metadata'; 4 | import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; 5 | import { join } from 'path'; 6 | import { chdir } from 'process'; 7 | 8 | import { enableProdMode } from '@angular/core'; 9 | // Faster server renders w/ Prod mode (dev mode never needed) 10 | enableProdMode(); 11 | 12 | // Express Engine 13 | import { ngExpressEngine } from '@nguniversal/express-engine'; 14 | // Import module map for lazy loading 15 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 16 | 17 | import { renderModuleFactory } from '@angular/platform-server'; 18 | 19 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 20 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 21 | 22 | // Get route paths to prerender only static pages 23 | const PATHS = require('./static.paths'); 24 | 25 | const BROWSER_FOLDER = join(process.cwd(), 'browser'); 26 | 27 | // Load the index.html file containing referances to your application bundle. 28 | const index = readFileSync(join('browser', 'index.html'), 'utf8'); 29 | 30 | let prom = Promise.resolve(); 31 | 32 | // Iterate each route path 33 | PATHS.forEach(function (route) { 34 | // Changes current directory to ./dist/browser 35 | chdir(BROWSER_FOLDER); 36 | 37 | // Creates new directories (if not exists) and changes current directory for the nested one 38 | route.split('/').filter(val => val !== '') 39 | .forEach(function (dir) { 40 | if (!existsSync(dir)) { 41 | mkdirSync(dir); 42 | } 43 | chdir(dir); 44 | }); 45 | 46 | // Writes rendered HTML to index.html, replacing the file if it already exists. 47 | prom = prom.then(_ => renderModuleFactory(AppServerModuleNgFactory, { 48 | document: index, 49 | url: route, 50 | extraProviders: [ 51 | provideModuleMap(LAZY_MODULE_MAP) 52 | ] 53 | })).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html)); 54 | }); 55 | -------------------------------------------------------------------------------- /apps/chat/scripts/compare-versions.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let path = require('path'); 3 | let args = require('yargs').argv; 4 | 5 | function addDirToVersions(dir, versions) { 6 | fs.readdirSync(dir).forEach(d => { 7 | if (d.indexOf('.') === 0) { 8 | return; 9 | } 10 | if (d.indexOf('@') === 0) { 11 | addDirToVersions(dir + '/' + d, versions); 12 | } else { 13 | let pkg = require(dir + '/' + d + '/package.json'); 14 | versions[d] = pkg.version; 15 | } 16 | }) 17 | } 18 | 19 | let aVersions = {}; 20 | addDirToVersions(path.resolve(args.a + '/node_modules'), aVersions); 21 | let bVersions = {}; 22 | addDirToVersions(path.resolve(args.b + '/node_modules'), bVersions); 23 | 24 | let keys = Object.keys(aVersions); 25 | for (let key in bVersions) { 26 | if (keys.indexOf(key) === -1) keys.push(key); 27 | } 28 | 29 | keys.forEach(key => { 30 | let v1 = aVersions[key]; 31 | let v2 = bVersions[key]; 32 | if (v1 !== v2) { 33 | console.log(key + '\t' + aVersions[key] + '\t' + bVersions[key]); 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /apps/chat/scripts/new-component.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let componentCode = function(name, filename) { 5 | return ` 6 | import {Component} from '@angular/core'; 7 | 8 | @Component({ 9 | selector: '${filename}', 10 | templateUrl: './${filename}.pug', 11 | }) 12 | export class ${name}Component { 13 | constructor() {} 14 | } 15 | `.trim() 16 | } 17 | 18 | let viewCode = function(name) { 19 | return ` 20 | h1 ${name} 21 | `.trim(); 22 | } 23 | 24 | const APP_DIR = __dirname + '/../src/app/'; 25 | 26 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 27 | const componentName = args.name.replace(/\s/g, ''); 28 | const componentDir = APP_DIR + filename + '/'; 29 | const componentFile = componentDir + filename + '.component.ts'; 30 | const viewFile = componentDir + filename + '.pug'; 31 | const appFile = APP_DIR + 'app.module.ts'; 32 | 33 | let component = componentCode(componentName, filename); 34 | let view = viewCode(args.name); 35 | 36 | fs.mkdirSync(componentDir); 37 | fs.writeFileSync(viewFile, view); 38 | fs.writeFileSync(componentFile, component); 39 | 40 | let app = fs.readFileSync(appFile, 'utf8'); 41 | let lines = app.split('\n').reverse(); 42 | 43 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.component'/)); 44 | lines.splice(insertImportAt, 0, `import {${componentName}Component} from './${filename}/${filename}.component'`); 45 | 46 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Component,/)); 47 | lines.splice(insertDeclarationAt, 0, ` ${componentName}Component,`) 48 | 49 | lines.reverse(); 50 | fs.writeFileSync(appFile, lines.join('\n')); 51 | -------------------------------------------------------------------------------- /apps/chat/scripts/new-service.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let serviceCode = function(name, filename) { 5 | return ` 6 | import {Injectable} from '@angular/core'; 7 | 8 | @Injectable() 9 | export class ${name}Service { 10 | constructor() {} 11 | } 12 | `.trim() 13 | } 14 | 15 | const APP_DIR = __dirname + '/../src/app/'; 16 | 17 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 18 | const serviceName = args.name.replace(/\s/g, ''); 19 | const serviceDir = APP_DIR + 'services/'; 20 | const serviceFile = serviceDir + filename + '.service.ts'; 21 | const appFile = APP_DIR + 'app.module.ts'; 22 | 23 | let service = serviceCode(serviceName, filename); 24 | 25 | fs.writeFileSync(serviceFile, service); 26 | 27 | let app = fs.readFileSync(appFile, 'utf8'); 28 | let lines = app.split('\n').reverse(); 29 | 30 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.service'/)); 31 | lines.splice(insertImportAt, 0, `import {${serviceName}Service} from './services/${filename}.service'`); 32 | 33 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Service,/)); 34 | lines.splice(insertDeclarationAt, 0, ` ${serviceName}Service,`) 35 | 36 | lines.reverse(); 37 | fs.writeFileSync(appFile, lines.join('\n')); 38 | -------------------------------------------------------------------------------- /apps/chat/scripts/update-angular.sh: -------------------------------------------------------------------------------- 1 | npm i --save @angular/animations@latest @angular/router@latest @angular/common@latest @angular/compiler@latest @angular/core@latest @angular/forms@latest @angular/http@latest @angular/platform-browser@latest @angular/platform-browser-dynamic@latest @angular/platform-server@latest @angular/cli@latest @angular/compiler-cli@latest @angular/language-service@latest @nguniversal/express-engine@latest @nguniversal/module-map-ngfactory-loader@latest rxjs@latest 2 | -------------------------------------------------------------------------------- /apps/chat/server.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let app = express(); 3 | 4 | const DIR = __dirname + '/dist/browser'; 5 | const INDEX = require('fs').readFileSync(DIR + '/index.html'); 6 | 7 | app.use(express.static(__dirname + '/dist/browser')); 8 | app.get('*', (req, res) => { 9 | res.end(INDEX); 10 | }) 11 | 12 | app.listen(process.env.PORT || 4030); 13 | -------------------------------------------------------------------------------- /apps/chat/server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import 'reflect-metadata'; 3 | import { renderModuleFactory } from '@angular/platform-server'; 4 | import { enableProdMode } from '@angular/core'; 5 | 6 | import * as express from 'express'; 7 | import { join } from 'path'; 8 | import { readFileSync } from 'fs'; 9 | 10 | // Faster server renders w/ Prod mode (dev mode never needed) 11 | enableProdMode(); 12 | 13 | // Express server 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 4000; 17 | const DIST_FOLDER = join(process.cwd(), 'dist'); 18 | 19 | // Our index.html we'll use as our template 20 | const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString(); 21 | 22 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 23 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 24 | 25 | // Express Engine 26 | import { ngExpressEngine } from '@nguniversal/express-engine'; 27 | // Import module map for lazy loading 28 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 29 | 30 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 31 | app.engine('html', ngExpressEngine({ 32 | bootstrap: AppServerModuleNgFactory, 33 | providers: [ 34 | provideModuleMap(LAZY_MODULE_MAP) 35 | ] 36 | })); 37 | 38 | app.set('view engine', 'html'); 39 | app.set('views', join(DIST_FOLDER, 'browser')); 40 | 41 | /* - Example Express Rest API endpoints - 42 | app.get('/api/**', (req, res) => { }); 43 | */ 44 | 45 | // Server static files from /browser 46 | app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), { 47 | maxAge: '1y' 48 | })); 49 | 50 | // ALl regular routes use the Universal engine 51 | app.get('*', (req, res) => { 52 | res.render('index', { req }); 53 | }); 54 | 55 | // Start up the Node server 56 | app.listen(PORT, () => { 57 | console.log(`Node Express server listening on http://localhost:${PORT}`); 58 | }); 59 | -------------------------------------------------------------------------------- /apps/chat/src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/chat/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 | 8 | `, 9 | }) 10 | export class AppComponent { 11 | constructor() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/chat/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouterModule } from '@angular/router'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpModule } from '@angular/http'; 6 | import {APP_BASE_HREF} from '@angular/common'; 7 | 8 | import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; 9 | 10 | import { appRoutes } from './app.routing'; 11 | import { AppComponent } from './app.component'; 12 | import { HomeComponent } from './home/home.component'; 13 | import { NavbarComponent } from './navbar/navbar.component'; 14 | import {LogInModalComponent} from './log-in-modal/log-in-modal.component' 15 | import {ChatComponent} from './chat/chat.component' 16 | import {RoomsComponent} from './rooms/rooms.component' 17 | 18 | import {PlatformService} from './services/platform.service'; 19 | import {OneDBService} from './services/onedb.service' 20 | 21 | import { environment } from '../environments/environment'; 22 | 23 | @NgModule({ 24 | imports: [ 25 | BrowserModule.withServerTransition({appId: 'my-app'}), 26 | RouterModule.forRoot(appRoutes), 27 | HttpModule, 28 | FormsModule, 29 | NgbModule.forRoot(), 30 | ], 31 | providers: [ 32 | {provide: APP_BASE_HREF, useValue: environment.baseHref || '/'}, 33 | PlatformService, 34 | OneDBService, 35 | ], 36 | declarations: [ 37 | AppComponent, 38 | HomeComponent, 39 | NavbarComponent, 40 | LogInModalComponent, 41 | ChatComponent, 42 | RoomsComponent, 43 | ], 44 | bootstrap: [ AppComponent ], 45 | }) 46 | export class AppModule { } 47 | -------------------------------------------------------------------------------- /apps/chat/src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import {HomeComponent} from './home/home.component'; 3 | import {ChatComponent} from './chat/chat.component'; 4 | 5 | export const appRoutes: Routes = [ 6 | { path: '', component: HomeComponent }, 7 | { path: 'chat/:chat_id', component: HomeComponent }, 8 | { path: '**', redirectTo: '' }, 9 | ]; 10 | 11 | -------------------------------------------------------------------------------- /apps/chat/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader'; 4 | 5 | import {AppModule} from './app.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | // The AppServerModule should import your AppModule followed 11 | // by the ServerModule from @angular/platform-server. 12 | AppModule, 13 | ServerModule, 14 | ModuleMapLoaderModule, 15 | ], 16 | // Since the bootstrapped component is not inherited from your 17 | // imported AppModule, it needs to be repeated here. 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppServerModule {} 21 | -------------------------------------------------------------------------------- /apps/chat/src/app/chat/chat.pug: -------------------------------------------------------------------------------- 1 | .alert.alert-danger(*ngIf="error") {{ error }} 2 | div(*ngIf="!chatID") 3 | h1 Welcome to OneChat! 4 | p. 5 | OneChat is a decentralized chat application. It is built on OneDB, which means 6 | you'll retain control over any messages and conversations you create here. 7 | p. 8 | To get started, pick a OneDB server to host your data (the default is one-db.datafire.io). 9 | When you post a message, the text will be stored on that server. When other users 10 | load the conversation, that's where the data will come from. 11 | p. 12 | At any point, you can use the Data Explorer to review, 13 | modify, and delete data you've created with this app. 14 | h1.text-center(*ngIf="loading") 15 | i.fa.fa-spin.fa-refresh 16 | div(*ngIf="chat && !loading") 17 | h1(*ngIf="!editingTitle") 18 | span {{ chat.title || chat.$.id }} 19 | a.btn.btn-warning.pull-right(*ngIf="acl?.owner === onedb.client.hosts.primary.user?.$.id", (click)="editingTitle = true") 20 | i.fa.fa-edit 21 | .form-group(*ngIf="editingTitle") 22 | .input-group 23 | input.input-lg.form-control(placeholder="Name this conversation...", [(ngModel)]="chat.title") 24 | .input-group-append 25 | button.btn.btn-success((click)="save()", [disabled]="saving") 26 | span(*ngIf="!saving") Save 27 | i.fa.fa-spin.fa-refresh(*ngIf="saving") 28 | .message-list(#messageList, [style.max-height]="maxChatHeight + 'px'") 29 | .text-center(*ngIf="hasEarlierMessages") 30 | button.btn.btn-link((click)="loadEarlierMessages()") 31 | span Load more messages 32 | i.fa.fa-right.fa-arrow-up 33 | .message(*ngFor="let message of messages") 34 | .info 35 | span {{ message.$.info.created_by }} 36 | span {{ prettyDate(message.$.info.created) }} 37 | i(*ngIf="message.$.info.created !== message.$.info.updated") (edited) 38 | div([innerHtml]="marked(message.message)") 39 | hr 40 | .form-group 41 | textarea.form-control(placeholder="Write a new message", [(ngModel)]="message", (keydown)="onKey($event)") 42 | .form-group 43 | button.btn.btn-lg.btn-success((click)="sendMessage()", [disabled]="sendingMessage") 44 | span(*ngIf="!sendingMessage") Send 45 | i.fa.fa-spin.fa-refresh(*ngIf="sendingMessage") 46 | -------------------------------------------------------------------------------- /apps/chat/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | const SMALL_SIDEBAR_WIDTH = 72; 4 | const LARGE_SIDEBAR_WIDTH = 300; 5 | 6 | @Component({ 7 | selector: 'home', 8 | templateUrl: './home.pug', 9 | styles: [` 10 | .content { 11 | width: 100%; 12 | position: absolute; 13 | top: 56px; 14 | bottom: 0px; 15 | } 16 | .sidebar, .main-content { 17 | padding: 15px; 18 | } 19 | .sidebar { 20 | position: absolute; 21 | left: 0; 22 | top: 0; 23 | bottom: 0; 24 | border-right: 1px solid #32334a; 25 | } 26 | .main-content { 27 | min-width: 300px; 28 | } 29 | `] 30 | }) 31 | export class HomeComponent { 32 | public collapsed:boolean; 33 | public sidebarWidth:number; 34 | 35 | constructor() { 36 | if (typeof window !== 'undefined' && window.innerWidth < 600) { 37 | this.collapse(); 38 | } else { 39 | this.expand(); 40 | } 41 | } 42 | 43 | collapse() { 44 | this.collapsed = true; 45 | this.sidebarWidth = SMALL_SIDEBAR_WIDTH; 46 | } 47 | 48 | expand() { 49 | this.collapsed = false; 50 | this.sidebarWidth = LARGE_SIDEBAR_WIDTH; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /apps/chat/src/app/home/home.pug: -------------------------------------------------------------------------------- 1 | .content 2 | .sidebar([style.width]="sidebarWidth + 'px'") 3 | .form-group 4 | button.btn.btn-dark((click)="collapsed ? expand() : collapse()") 5 | i.fa([class.fa-bars]="collapsed", [class.fa-times]="!collapsed") 6 | rooms([hidden]="collapsed") 7 | .main-content([style.margin-left]="sidebarWidth + 'px'") 8 | chat 9 | -------------------------------------------------------------------------------- /apps/chat/src/app/log-in-modal/log-in-modal.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {OneDBService} from '../services/onedb.service'; 3 | import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap'; 4 | import {DomSanitizer, SafeHtml} from '@angular/platform-browser' 5 | 6 | @Component({ 7 | selector: 'log-in-modal', 8 | templateUrl: './log-in-modal.pug', 9 | }) 10 | export class LogInModalComponent { 11 | @ViewChild('content') content; 12 | formContent:SafeHtml; 13 | modalRef:any; 14 | 15 | constructor( 16 | private onedb:OneDBService, 17 | private modals: NgbModal, 18 | private sanitizer:DomSanitizer) { 19 | this.refreshForm(); 20 | this.onedb.onLogin.subscribe(instance => { 21 | this.refreshForm(); 22 | if (this.modalRef && instance.user) this.modalRef.close(); 23 | }) 24 | } 25 | 26 | open() { 27 | this.modalRef = this.modals.open(this.content); 28 | } 29 | 30 | refreshForm() { 31 | this.formContent = this.sanitizer.bypassSecurityTrustHtml(this.onedb.client.loginForm()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/chat/src/app/log-in-modal/log-in-modal.pug: -------------------------------------------------------------------------------- 1 | ng-template(#content let-c="close" let-d="dismiss") 2 | .modal-header 3 | h4.modal-title 4 | span(*ngIf="!onedb.client.hosts.primary.user") Log In or Sign Up 5 | span(*ngIf="onedb.client.hosts.primary.user") Log Out or Switch Accounts 6 | button.close(type="button", class="close", aria-label="Close", (click)="d('Cross click')") 7 | span(aria-hidden="true") × 8 | .modal-body 9 | p OneChat uses OneDB, so you get to decide where your data is stored. 10 | p. 11 | You can create a free account on one-db.datafire.io 12 | 13 | host your own instance 14 | , or choose another provider. 15 | div([innerHtml]="formContent") 16 | -------------------------------------------------------------------------------- /apps/chat/src/app/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import {ViewChild, Component} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | @Component({ 6 | selector: 'navbar', 7 | templateUrl: './navbar.pug', 8 | styles: [` 9 | nav { 10 | display: block; 11 | } 12 | `] 13 | }) 14 | export class NavbarComponent { 15 | @ViewChild('logInModal') logInModal; 16 | constructor(public router: Router, public onedb:OneDBService) {} 17 | } 18 | -------------------------------------------------------------------------------- /apps/chat/src/app/navbar/navbar.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | nav.navbar.navbar-expand-lg.navbar-dark.bg-dark 3 | a.navbar-brand(routerLink="/") OneChat 4 | .float-right 5 | ul.navbar-nav.ml-auto 6 | li 7 | a.nav-link((click)="logInModal.open()") 8 | i.fa.fa-left.fa-users 9 | span([ngSwitch]="!!onedb.client.hosts.primary.user") 10 | span(*ngSwitchCase="true") 11 | span.d-sm-none.d-md-inline-block {{onedb.client.hosts.primary.displayName}} 12 | span(*ngSwitchCase="false") Sign In 13 | 14 | -------------------------------------------------------------------------------- /apps/chat/src/app/rooms/rooms.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | declare let window:any; 6 | declare let require:any; 7 | 8 | @Component({ 9 | selector: 'rooms', 10 | templateUrl: './rooms.pug', 11 | styles: [` 12 | .btn-success { 13 | width: 100%; 14 | } 15 | `] 16 | }) 17 | export class RoomsComponent { 18 | @ViewChild('logInModal') logInModal; 19 | listingChats:boolean; 20 | error:string; 21 | chatRoomName:string; 22 | 23 | userChats:any[]; 24 | ownedChats:any[]; 25 | publicChats:any[]; 26 | 27 | constructor(public onedb:OneDBService, private router:Router) { 28 | this.onedb.onLogin.subscribe(instance => { 29 | if (instance === this.onedb.client.hosts.primary && instance.user) { 30 | this.listChats(); 31 | } 32 | }); 33 | if (this.onedb.client.hosts.primary.user) { 34 | this.listChats(); 35 | } 36 | } 37 | 38 | async listChats() { 39 | if (this.listingChats) return setTimeout(this.listChats.bind(this), 100); 40 | this.listingChats = true; 41 | const userID = this.onedb.client.hosts.primary.user.$.id; 42 | this.publicChats = (await this.onedb.client.list('chat', 'conversation')).items; 43 | this.ownedChats = (await this.onedb.client.list('chat', 'conversation', {owner: userID})).items; 44 | let userMessages = await this.onedb.client.list('chat', 'message', {owner: userID}); 45 | let chatIDs = []; 46 | for (let message of userMessages.items) { 47 | if (chatIDs.indexOf(message.conversationID) === -1) { 48 | chatIDs.push(message.conversationID); 49 | } 50 | } 51 | this.userChats = await Promise.all(chatIDs.map(id => this.onedb.client.get('chat', 'conversation', id))) 52 | this.listingChats = false; 53 | } 54 | 55 | async startChat() { 56 | this.error = null; 57 | let chatID = this.chatRoomName ? this.chatRoomName.replace(/\W+/g, '_') : undefined; 58 | try { 59 | const chat = {title: this.chatRoomName || ''}; 60 | chatID = await this.onedb.client.create('chat', 'conversation', chatID, chat); 61 | } catch (e) { 62 | this.error = e.message; 63 | return; 64 | } 65 | this.router.navigate(['/chat', chatID]) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /apps/chat/src/app/rooms/rooms.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | .alert.alert-danger(*ngIf="error") {{ error }} 3 | div(*ngIf="onedb.client.hosts.primary.user") 4 | form((submit)="startChat()") 5 | .form-group 6 | button.btn.btn-success(type="submit") Start a new chat 7 | hr 8 | mixin rooms(title, arr) 9 | h4(*ngIf=arr)= title 10 | p(*ngIf=arr + "&& !" + arr + ".length") 11 | i None found 12 | .chat(*ngFor="let chat of " + arr) 13 | a([routerLink]="['/chat', chat.$.id]") {{ chat.title || chat.$.id }} 14 | +rooms('Chats you own', 'ownedChats') 15 | hr 16 | +rooms('Chats you participate in', 'userChats') 17 | hr 18 | +rooms('Public chats', 'publicChats') 19 | div(*ngIf="!onedb.client.hosts.primary.user") 20 | p 21 | a((click)="logInModal.open()") Sign in to get started 22 | -------------------------------------------------------------------------------- /apps/chat/src/app/services/onedb.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | 4 | declare let window:any; 5 | declare let require:any; 6 | const Client = require('onedb-client').Client; 7 | const CORE_HOST = 'https://one-db.datafire.io'; 8 | 9 | const STORAGE_KEY = 'onedb_auth'; 10 | 11 | @Injectable() 12 | export class OneDBService { 13 | client:any; 14 | user:any; 15 | 16 | onLogin = new BehaviorSubject(null); 17 | 18 | constructor(private zone:NgZone) { 19 | window.onedbService = this; 20 | this.client = new Client({ 21 | hosts: { 22 | core: { 23 | location: CORE_HOST, 24 | } 25 | }, 26 | onLogin: user => { 27 | this.zone.run(_ => this.onLogin.next(user)); 28 | }, 29 | scope: ['chat:read', 'chat:create', 'chat:write', 'chat:delete', 'chat:modify_acl', 'chat:append'], 30 | }); 31 | this.maybeRestore(); 32 | this.onLogin.subscribe(user => { 33 | this.user = user; 34 | if (!window.localStorage) return 35 | const toStore = { 36 | hosts: this.client.hosts, 37 | }; 38 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore)) 39 | }) 40 | } 41 | 42 | async maybeRestore() { 43 | if (!window.localStorage) return; 44 | let existing:any = window.localStorage.getItem(STORAGE_KEY); 45 | if (!existing) return; 46 | existing = JSON.parse(existing); 47 | if (!existing || !existing.hosts) return; 48 | let hosts = Object.assign({}, existing.hosts, {core: {location: CORE_HOST}}) 49 | await this.client.setHosts(hosts); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/chat/src/app/services/platform.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, PLATFORM_ID } from '@angular/core' 2 | import { isPlatformBrowser, isPlatformServer } from '@angular/common' 3 | 4 | @Injectable() 5 | export class PlatformService { 6 | constructor(@Inject(PLATFORM_ID) private platformId: any) { } 7 | 8 | public isBrowser(): boolean { 9 | return isPlatformBrowser(this.platformId) 10 | } 11 | 12 | public isServer(): boolean { 13 | return isPlatformServer(this.platformId) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/chat/src/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | a { 6 | cursor: pointer; 7 | } 8 | 9 | nav { 10 | margin-bottom: 0px; 11 | } 12 | 13 | body { 14 | position: relative; 15 | } 16 | 17 | app > .container { 18 | padding-bottom: 72px; 19 | } 20 | 21 | footer { 22 | position: absolute; 23 | bottom: 0; 24 | left: 0; 25 | right: 0; 26 | 27 | background-color: #fff; 28 | padding-top: 20px; 29 | padding-bottom: 20px; 30 | } 31 | 32 | footer, footer a { 33 | color: #3E3F3A; 34 | } 35 | 36 | .fa-left { 37 | margin-right: 8px; 38 | } 39 | .fa-right { 40 | margin-left: 8px; 41 | } 42 | 43 | .btn-min-width { 44 | min-width: 100px; 45 | } 46 | -------------------------------------------------------------------------------- /apps/chat/src/app/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | @import '~bootstrap/scss/bootstrap'; 4 | 5 | @import './app.scss'; 6 | 7 | -------------------------------------------------------------------------------- /apps/chat/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/chat/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/chat/src/assets/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/chat/src/assets/img/Icon.png -------------------------------------------------------------------------------- /apps/chat/src/assets/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/chat/src/assets/img/Logo.png -------------------------------------------------------------------------------- /apps/chat/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | baseHref: '/', 4 | }; 5 | -------------------------------------------------------------------------------- /apps/chat/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | baseHref: '/', 9 | }; 10 | -------------------------------------------------------------------------------- /apps/chat/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/chat/src/favicon.ico -------------------------------------------------------------------------------- /apps/chat/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OneChat - a OneDB chat application 7 | 8 | 9 | 10 | 11 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /apps/chat/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /apps/chat/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/chat/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | import 'babel-polyfill'; 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /apps/chat/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/chat/src/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | // Set the module format to "commonjs": 7 | "module": "commonjs", 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "test.ts", 12 | "**/*.spec.ts" 13 | ], 14 | // Add "angularCompilerOptions" with the AppServerModule you wrote 15 | // set as the "entryModule". 16 | "angularCompilerOptions": { 17 | "entryModule": "app/app.server.module#AppServerModule" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/chat/static.paths.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '/', 3 | ]; 4 | -------------------------------------------------------------------------------- /apps/chat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types", 13 | "typings.d.ts" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "types": ["node"], 20 | "module": "es2015", 21 | "baseUrl": "./" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/chat/types/conversation.acl.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow": { 3 | "read": ["_all"], 4 | "write": ["_owner"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/chat/types/conversation.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": false, 4 | "properties": { 5 | "title": {"type": "string", "maxLength": 500}, 6 | "description": {"type": "string", "maxLength": 10000}, 7 | "banned": { 8 | "type": "array", 9 | "items": {"type": "string"} 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/chat/types/message.acl.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow": { 3 | "read": ["_all"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /apps/chat/types/message.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": false, 4 | "required": ["conversationID", "message"], 5 | "properties": { 6 | "conversationID": {"type": "string"}, 7 | "message": {"type": "string", "maxLength": 10000} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/chat/webpack.cli-additions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const commonCliConfig = 'node_modules/@angular/cli/models/webpack-configs/common.js'; 3 | const addition_rules = ` 4 | { test: /\.(pug|jade)$/, loader: 'apply-loader' }, 5 | { test: /\.(pug|jade)$/, 6 | loader: 'pug-loader', 7 | query: { doctype: 'html', plugins: [require('pug-plugin-ng')] }, 8 | }, { 9 | test: /\.json$/, 10 | use: [{ 11 | loader: 'json-loader', 12 | }] 13 | }, 14 | { 15 | test: /\.js$/, 16 | use: [{ 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['env'], 20 | } 21 | }], 22 | }, 23 | { test: /\.md$/, use: [{ loader: 'raw-loader' }, { loader: 'markdown-loader', }] } 24 | ,`; // make sure to have this last comma 25 | 26 | fs.readFile(commonCliConfig, (err, data) => { 27 | 28 | if (err) { throw err; } 29 | 30 | const configText = data.toString(); 31 | // make sure we don't include it (if we've already done this) 32 | if (configText.indexOf(addition_rules) > -1) { return; } 33 | 34 | console.log('-- Inserting additional webpack rules to node_modules CLI -- '); 35 | 36 | const position = configText.indexOf('rules: [') + 8; 37 | const output = [configText.slice(0, position), addition_rules, configText.slice(position)].join(''); 38 | const file = fs.openSync(commonCliConfig, 'r+'); 39 | 40 | fs.writeFile(file, output); 41 | fs.close(file); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /apps/chat/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | // Work around for https://github.com/angular/angular-cli/issues/7200 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | // This is our Express server for Dynamic universal 9 | server: './server.ts', 10 | // This is an example of Static prerendering (generative) 11 | prerender: './prerender.ts' 12 | }, 13 | target: 'node', 14 | resolve: { extensions: ['.ts', '.js'] }, 15 | // Make sure we include all node_modules etc 16 | externals: [/(node_modules|main\..*\.js)/,], 17 | output: { 18 | // Puts the output at the root of the dist folder 19 | path: path.join(__dirname, 'dist'), 20 | filename: '[name].js' 21 | }, 22 | module: { 23 | rules: [ 24 | { test: /\.ts$/, use: {loader: 'ts-loader', options: {}} } 25 | ] 26 | }, 27 | plugins: [ 28 | new webpack.ContextReplacementPlugin( 29 | // fixes WARNING Critical dependency: the request of a dependency is an expression 30 | /(.+)?angular(\\|\/)core(.+)?/, 31 | path.join(__dirname, 'src'), // location of your src 32 | {} // a map of your routes 33 | ), 34 | new webpack.ContextReplacementPlugin( 35 | // fixes WARNING Critical dependency: the request of a dependency is an expression 36 | /(.+)?express(\\|\/)(.+)?/, 37 | path.join(__dirname, 'src'), 38 | {} 39 | ) 40 | ] 41 | } 42 | 43 | -------------------------------------------------------------------------------- /apps/data-explorer/.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | 3 | .idea 4 | .DS_Store 5 | morgan.log 6 | 7 | # Built # 8 | /__build__/ 9 | /__server_build__/ 10 | /node_modules/ 11 | /typings/ 12 | /tsd_typings/ 13 | /dist-server/ 14 | /compiled/ 15 | 16 | # Node # 17 | npm-debug.log 18 | /npm-debug.log.* 19 | 20 | # Webpack # 21 | webpack.records.json 22 | 23 | # Angular # 24 | *.ngfactory.ts 25 | *.css.shim.ts 26 | *.ngsummary.json 27 | *.metadata.json 28 | *.shim.ngstyle.ts 29 | -------------------------------------------------------------------------------- /apps/data-explorer/README.md: -------------------------------------------------------------------------------- 1 | # Angluar 6.0 Template Project 2 | I use this template as a starter for any new Angular projects. 3 | 4 | Check out [the demo](https://bobby-brennan.github.io/angular6-template) 5 | 6 | #### Features 7 | * Angular Universal (prerendering) 8 | * [Pug](https://pugjs.org) templates instead of HTML 9 | * Bootstrap Sass 10 | * Font Awesome 11 | * Angular HTML5 Router 12 | * Standard navbar/body layout 13 | 14 | ## Running the Demo 15 | 16 | #### Install 17 | 18 | ``` 19 | npm install 20 | ``` 21 | 22 | #### Run (development mode) 23 | Start the server on port 3000: 24 | 25 | ``` 26 | npm run start 27 | ``` 28 | 29 | 30 | #### Build (production) 31 | Build everything and put it in the `dist/` folder: 32 | 33 | ``` 34 | npm run build 35 | ``` 36 | 37 | Or build for GitHub pages by copying `dist/browser` to `docs/`. 38 | Be sure to change the settings in your repo to point GitHub pages to 39 | the `docs/` folder on the `master` branch. 40 | 41 | ## Customizing 42 | 43 | ### Base href 44 | The app uses the base href `/` for development builds, and `/angular6-template` 45 | for production builds (to accomodate GitHub pages, which uses the repository 46 | name in the URL's path). You will probably want to change the base href in 47 | `./src/environments/environment.prod.ts`. 48 | 49 | ### Prerendering 50 | Prerendering is a performance optimization - your page's HTML is generated 51 | at build time, so the user sees a near-instant load of the page, while Angular 52 | loads in the background. 53 | 54 | In this build, only the homepage is prerendered - you can add other routes 55 | by editing `./static.paths.ts`. 56 | 57 | ## New Components 58 | A helper script for creating new components is in `./scripts/new-component.js`. 59 | 60 | ``` 61 | node ./scripts/new-component.js --name "Widget Viewer" 62 | ``` 63 | -------------------------------------------------------------------------------- /apps/data-explorer/ng-add-pug-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds the pug-loader inside Angular CLI's webpack config, if not there yet. 3 | * @see https://github.com/danguilherme/ng-cli-pug-loader 4 | */ 5 | const fs = require('fs'); 6 | const commonCliConfig = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js'; 7 | const pugRule = '{ test: /.pug$/, use: [ { loader: "apply-loader" }, { loader: "pug-loader" } ] },'; 8 | 9 | fs.readFile(commonCliConfig, (err, data) => { 10 | if (err) { throw err; } 11 | 12 | const configText = data.toString(); 13 | // make sure we don't add the rule if it already exists 14 | if (configText.indexOf(pugRule) > -1) { return; } 15 | 16 | // Insert the pug webpack rule 17 | const position = configText.indexOf('rules: [') + 8; 18 | const output = [configText.slice(0, position), pugRule, configText.slice(position)].join(''); 19 | const file = fs.openSync(commonCliConfig, 'r+'); 20 | fs.writeFile(file, output); 21 | fs.close(file); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/data-explorer/prerender.ts: -------------------------------------------------------------------------------- 1 | // Load zone.js for the server. 2 | import 'zone.js/dist/zone-node'; 3 | import 'reflect-metadata'; 4 | import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; 5 | import { join } from 'path'; 6 | import { chdir } from 'process'; 7 | 8 | import { enableProdMode } from '@angular/core'; 9 | // Faster server renders w/ Prod mode (dev mode never needed) 10 | enableProdMode(); 11 | 12 | // Express Engine 13 | import { ngExpressEngine } from '@nguniversal/express-engine'; 14 | // Import module map for lazy loading 15 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 16 | 17 | import { renderModuleFactory } from '@angular/platform-server'; 18 | 19 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 20 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 21 | 22 | // Get route paths to prerender only static pages 23 | const PATHS = require('./static.paths'); 24 | 25 | const BROWSER_FOLDER = join(process.cwd(), 'browser'); 26 | 27 | // Load the index.html file containing referances to your application bundle. 28 | const index = readFileSync(join('browser', 'index.html'), 'utf8'); 29 | 30 | let prom = Promise.resolve(); 31 | 32 | // Iterate each route path 33 | PATHS.forEach(function (route) { 34 | // Changes current directory to ./dist/browser 35 | chdir(BROWSER_FOLDER); 36 | 37 | // Creates new directories (if not exists) and changes current directory for the nested one 38 | route.split('/').filter(val => val !== '') 39 | .forEach(function (dir) { 40 | if (!existsSync(dir)) { 41 | mkdirSync(dir); 42 | } 43 | chdir(dir); 44 | }); 45 | 46 | // Writes rendered HTML to index.html, replacing the file if it already exists. 47 | prom = prom.then(_ => renderModuleFactory(AppServerModuleNgFactory, { 48 | document: index, 49 | url: route, 50 | extraProviders: [ 51 | provideModuleMap(LAZY_MODULE_MAP) 52 | ] 53 | })).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html)); 54 | }); 55 | -------------------------------------------------------------------------------- /apps/data-explorer/scripts/compare-versions.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let path = require('path'); 3 | let args = require('yargs').argv; 4 | 5 | function addDirToVersions(dir, versions) { 6 | fs.readdirSync(dir).forEach(d => { 7 | if (d.indexOf('.') === 0) { 8 | return; 9 | } 10 | if (d.indexOf('@') === 0) { 11 | addDirToVersions(dir + '/' + d, versions); 12 | } else { 13 | let pkg = require(dir + '/' + d + '/package.json'); 14 | versions[d] = pkg.version; 15 | } 16 | }) 17 | } 18 | 19 | let aVersions = {}; 20 | addDirToVersions(path.resolve(args.a + '/node_modules'), aVersions); 21 | let bVersions = {}; 22 | addDirToVersions(path.resolve(args.b + '/node_modules'), bVersions); 23 | 24 | let keys = Object.keys(aVersions); 25 | for (let key in bVersions) { 26 | if (keys.indexOf(key) === -1) keys.push(key); 27 | } 28 | 29 | keys.forEach(key => { 30 | let v1 = aVersions[key]; 31 | let v2 = bVersions[key]; 32 | if (v1 !== v2) { 33 | console.log(key + '\t' + aVersions[key] + '\t' + bVersions[key]); 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /apps/data-explorer/scripts/new-component.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let componentCode = function(name, filename) { 5 | return ` 6 | import {Component} from '@angular/core'; 7 | 8 | @Component({ 9 | selector: '${filename}', 10 | templateUrl: './${filename}.pug', 11 | }) 12 | export class ${name}Component { 13 | constructor() {} 14 | } 15 | `.trim() 16 | } 17 | 18 | let viewCode = function(name) { 19 | return ` 20 | h1 ${name} 21 | `.trim(); 22 | } 23 | 24 | const APP_DIR = __dirname + '/../src/app/'; 25 | 26 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 27 | const componentName = args.name.replace(/\s/g, ''); 28 | const componentDir = APP_DIR + filename + '/'; 29 | const componentFile = componentDir + filename + '.component.ts'; 30 | const viewFile = componentDir + filename + '.pug'; 31 | const appFile = APP_DIR + 'app.module.ts'; 32 | 33 | let component = componentCode(componentName, filename); 34 | let view = viewCode(args.name); 35 | 36 | fs.mkdirSync(componentDir); 37 | fs.writeFileSync(viewFile, view); 38 | fs.writeFileSync(componentFile, component); 39 | 40 | let app = fs.readFileSync(appFile, 'utf8'); 41 | let lines = app.split('\n').reverse(); 42 | 43 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.component'/)); 44 | lines.splice(insertImportAt, 0, `import {${componentName}Component} from './${filename}/${filename}.component'`); 45 | 46 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Component,/)); 47 | lines.splice(insertDeclarationAt, 0, ` ${componentName}Component,`) 48 | 49 | lines.reverse(); 50 | fs.writeFileSync(appFile, lines.join('\n')); 51 | -------------------------------------------------------------------------------- /apps/data-explorer/scripts/new-service.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let serviceCode = function(name, filename) { 5 | return ` 6 | import {Injectable} from '@angular/core'; 7 | 8 | @Injectable() 9 | export class ${name}Service { 10 | constructor() {} 11 | } 12 | `.trim() 13 | } 14 | 15 | const APP_DIR = __dirname + '/../src/app/'; 16 | 17 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 18 | const serviceName = args.name.replace(/\s/g, ''); 19 | const serviceDir = APP_DIR + 'services/'; 20 | const serviceFile = serviceDir + filename + '.service.ts'; 21 | const appFile = APP_DIR + 'app.module.ts'; 22 | 23 | let service = serviceCode(serviceName, filename); 24 | 25 | fs.writeFileSync(serviceFile, service); 26 | 27 | let app = fs.readFileSync(appFile, 'utf8'); 28 | let lines = app.split('\n').reverse(); 29 | 30 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.service'/)); 31 | lines.splice(insertImportAt, 0, `import {${serviceName}Service} from './services/${filename}.service'`); 32 | 33 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Service,/)); 34 | lines.splice(insertDeclarationAt, 0, ` ${serviceName}Service,`) 35 | 36 | lines.reverse(); 37 | fs.writeFileSync(appFile, lines.join('\n')); 38 | -------------------------------------------------------------------------------- /apps/data-explorer/scripts/update-angular.sh: -------------------------------------------------------------------------------- 1 | npm i --save @angular/animations@latest @angular/router@latest @angular/common@latest @angular/compiler@latest @angular/core@latest @angular/forms@latest @angular/http@latest @angular/platform-browser@latest @angular/platform-browser-dynamic@latest @angular/platform-server@latest @angular/cli@latest @angular/compiler-cli@latest @angular/language-service@latest @nguniversal/express-engine@latest @nguniversal/module-map-ngfactory-loader@latest rxjs@latest 2 | -------------------------------------------------------------------------------- /apps/data-explorer/server.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let app = express(); 3 | 4 | const DIR = __dirname + '/dist/browser'; 5 | const INDEX = require('fs').readFileSync(DIR + '/index.html'); 6 | 7 | app.use(express.static(__dirname + '/dist/browser')); 8 | app.get('*', (req, res) => { 9 | res.end(INDEX); 10 | }) 11 | 12 | app.listen(process.env.PORT || 4020); 13 | -------------------------------------------------------------------------------- /apps/data-explorer/server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import 'reflect-metadata'; 3 | import { renderModuleFactory } from '@angular/platform-server'; 4 | import { enableProdMode } from '@angular/core'; 5 | 6 | import * as express from 'express'; 7 | import { join } from 'path'; 8 | import { readFileSync } from 'fs'; 9 | 10 | // Faster server renders w/ Prod mode (dev mode never needed) 11 | enableProdMode(); 12 | 13 | // Express server 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 4000; 17 | const DIST_FOLDER = join(process.cwd(), 'dist'); 18 | 19 | // Our index.html we'll use as our template 20 | const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString(); 21 | 22 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 23 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 24 | 25 | // Express Engine 26 | import { ngExpressEngine } from '@nguniversal/express-engine'; 27 | // Import module map for lazy loading 28 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 29 | 30 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 31 | app.engine('html', ngExpressEngine({ 32 | bootstrap: AppServerModuleNgFactory, 33 | providers: [ 34 | provideModuleMap(LAZY_MODULE_MAP) 35 | ] 36 | })); 37 | 38 | app.set('view engine', 'html'); 39 | app.set('views', join(DIST_FOLDER, 'browser')); 40 | 41 | /* - Example Express Rest API endpoints - 42 | app.get('/api/**', (req, res) => { }); 43 | */ 44 | 45 | // Server static files from /browser 46 | app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), { 47 | maxAge: '1y' 48 | })); 49 | 50 | // ALl regular routes use the Universal engine 51 | app.get('*', (req, res) => { 52 | res.render('index', { req }); 53 | }); 54 | 55 | // Start up the Node server 56 | app.listen(PORT, () => { 57 | console.log(`Node Express server listening on http://localhost:${PORT}`); 58 | }); 59 | -------------------------------------------------------------------------------- /apps/data-explorer/src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 |
8 | 9 |
10 | `, 11 | }) 12 | export class AppComponent { 13 | constructor() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouterModule } from '@angular/router'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpModule } from '@angular/http'; 6 | import {APP_BASE_HREF} from '@angular/common'; 7 | 8 | import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; 9 | 10 | import { appRoutes } from './app.routing'; 11 | import { AppComponent } from './app.component'; 12 | import { HomeComponent } from './home/home.component'; 13 | import { NavbarComponent } from './navbar/navbar.component'; 14 | import {LogInModalComponent} from './log-in-modal/log-in-modal.component' 15 | import {NamespaceComponent} from './namespace/namespace.component' 16 | import {ItemComponent} from './item/item.component' 17 | import {JSONSchemaEditorComponent} from './json-schema/json-schema-editor.component' 18 | import {SchemaLabelComponent} from './json-schema/schema-label.component' 19 | 20 | import {PlatformService} from './services/platform.service'; 21 | import {OneDBService} from './services/onedb.service' 22 | 23 | import { environment } from '../environments/environment'; 24 | 25 | @NgModule({ 26 | imports: [ 27 | BrowserModule.withServerTransition({appId: 'my-app'}), 28 | RouterModule.forRoot(appRoutes), 29 | HttpModule, 30 | FormsModule, 31 | NgbModule.forRoot(), 32 | ], 33 | providers: [ 34 | {provide: APP_BASE_HREF, useValue: environment.baseHref || '/'}, 35 | PlatformService, 36 | OneDBService, 37 | ], 38 | declarations: [ 39 | AppComponent, 40 | HomeComponent, 41 | NavbarComponent, 42 | LogInModalComponent, 43 | NamespaceComponent, 44 | ItemComponent, 45 | JSONSchemaEditorComponent, 46 | SchemaLabelComponent, 47 | ], 48 | bootstrap: [ AppComponent ], 49 | }) 50 | export class AppModule { } 51 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import {HomeComponent} from './home/home.component'; 3 | import {NamespaceComponent} from './namespace/namespace.component' 4 | import {ItemComponent} from './item/item.component' 5 | 6 | export const appRoutes: Routes = [ 7 | { path: '', component: HomeComponent }, 8 | { path: 'data/:namespace', component: NamespaceComponent }, 9 | { path: 'data/:namespace/:type', component: ItemComponent }, 10 | { path: 'data/:namespace/:type/:item_id', component: ItemComponent }, 11 | { path: '**', redirectTo: '' }, 12 | ]; 13 | 14 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader'; 4 | 5 | import {AppModule} from './app.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | // The AppServerModule should import your AppModule followed 11 | // by the ServerModule from @angular/platform-server. 12 | AppModule, 13 | ServerModule, 14 | ModuleMapLoaderModule, 15 | ], 16 | // Since the bootstrapped component is not inherited from your 17 | // imported AppModule, it needs to be repeated here. 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppServerModule {} 21 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | declare let window:any; 6 | declare let require:any; 7 | 8 | @Component({ 9 | selector: 'home', 10 | templateUrl: './home.pug', 11 | styles: [` 12 | ul { 13 | padding-left: 0px; 14 | list-style: none; 15 | } 16 | .col { 17 | min-width: 250px; 18 | } 19 | `] 20 | }) 21 | export class HomeComponent { 22 | @ViewChild('logInModal') logInModal; 23 | error:string; 24 | usage:any; 25 | allNamespaces:any[]; 26 | 27 | constructor(public onedb:OneDBService) { 28 | this.loadNamespaces(); 29 | onedb.onLogin.subscribe(instance => { 30 | this.loadUsage(); 31 | }) 32 | } 33 | 34 | async loadUsage() { 35 | if (!this.onedb.client.hosts.primary.user) { 36 | this.usage = null; 37 | return; 38 | } 39 | this.usage = await this.onedb.client.get('system', 'usage', this.onedb.client.hosts.primary.user.$.id); 40 | 41 | // TODO: remove this block - just for a few legacy users 42 | if (!this.usage && this.onedb.client.hosts.primary.user.namespaces) { 43 | this.usage = this.onedb.client.hosts.primary.user; 44 | } 45 | } 46 | 47 | async loadNamespaces() { 48 | this.allNamespaces = (await this.onedb.client.list('core', 'namespace')).items; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/home/home.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | h1 OneDB Data Explorer 3 | p. 4 | This site allows you to browse, modify, and delete data on your OneDB accounts. 5 | p. 6 | Below are the datasets currently available in OneDB. 7 | Developers may be interested in 8 | 9 | creating a new dataset 10 | . 11 | .row 12 | .col 13 | h4 My Data 14 | div(*ngIf="!usage || !usage.namespaces?.length") 15 | p(*ngIf="!onedb.client.hosts.primary.user") 16 | a((click)="logInModal.open()" href="javascript:void(0)") Sign in to see your data 17 | p(*ngIf="onedb.client.hosts.primary.user") 18 | i You haven't added any data to this OneDB instance yet. 19 | div(*ngIf="usage && usage.namespaces?.length") 20 | p Below are the datasets that contain information you've created while using OneDB apps. 21 | ul 22 | li(*ngFor="let namespace of usage.namespaces") 23 | a([routerLink]="['/data', namespace]") {{namespace}} 24 | .col 25 | h4 All Data 26 | p Below are all the datasets currently available on OneDB. 27 | //.form-group 28 | .input-group 29 | input.form-control(type="text", [(ngModel)]="query") 30 | .input-group-append 31 | a.btn.btn-success([routerLink]="['/data', query]") View Data 32 | div(*ngIf="allNamespaces") 33 | ul 34 | li(*ngFor="let namespace of allNamespaces") 35 | a([routerLink]="['/data', namespace.$.id]") {{ namespace.$.id }} 36 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/item/item.pug: -------------------------------------------------------------------------------- 1 | h1 2 | a([routerLink]="['/data', namespace]") {{ namespace }} 3 | span /{{ type }}/{{item_id || '<new>' }} 4 | div(*ngIf="item") 5 | ul.info(*ngIf="info") 6 | li 7 | span.info-label Owned by 8 | span.info-data {{ acl.owner }} 9 | li 10 | span.info-label Created by 11 | span.info-data {{ info.created_by }} 12 | li 13 | span.info-label Created on 14 | span.info-data {{ datestr(info.created) }} 15 | li 16 | span.info-label Last updated 17 | span.info-data {{ datestr(info.updated) }} 18 | .form-group 19 | h2(*ngIf="loading") 20 | i.fa.fa-spin.fa-refresh 21 | .btn-toolbar(*ngIf="!loading") 22 | button.btn.btn-success.mr-3((click)="wrapAsync('save')") 23 | span(*ngIf="item_id") Save 24 | span(*ngIf="!item_id") Create 25 | i.fa.fa-right.fa-cloud-upload 26 | button.btn.btn-danger(*ngIf="item_id", (click)="confirmDelete = true") 27 | span Delete 28 | i.fa.fa-right.fa-trash 29 | .alert.alert-danger(*ngIf="error") 30 | span {{error}} 31 | .alert.alert-warning(*ngIf="confirmDelete") 32 | p Are you sure? This action cannot be undone. 33 | .btn-toolbar 34 | a.btn.btn-danger((click)="wrapAsync('delete')") Delete this item 35 | a.btn.btn-link((click)="confirmDelete = false") Cancel 36 | .form-group(*ngIf="acl") 37 | a((click)="expandACL = !expandACL" href="javascript:void(0)") 38 | i.fa.fa-left([class.fa-plus-square-o]="!expandACL", [class.fa-minus-square-o]="expandACL") 39 | span {{ expandACL ? 'Hide' : 'Show' }} Access Control 40 | div(*ngIf="expandACL") 41 | p Use these fields to control who has access to read, write, append to, and delete your data. 42 | ul 43 | li The allow field is a whitelist of users who can perform the given action. 44 | li The disallow field is a blacklist of users who cannot perform the given action. 45 | li The modify field is a whitelist of users who can change the allow and disallow fields. 46 | li The user _owner refers to the owner of this item. 47 | li The user _all refers to everyone. 48 | .form-group(*ngFor="let accessType of ACCESS_TYPES") 49 | h4.text-capitalize {{ accessType }} 50 | .row 51 | .col(*ngFor="let aclType of ACL_TYPES") 52 | label.text-capitalize {{ aclType }} 53 | input.form-control( 54 | [value]="(acl[aclType][accessType] || []).join(', ')", 55 | (input)="setACLString($event.target.value, aclType, accessType)") 56 | a((click)="toggleEditMode()", href="javascript:void(0)") 57 | span {{ editMode === 'json' ? 'View form editor' : 'Edit as JSON' }} 58 | json-schema-editor( 59 | *ngIf="editMode === 'form'", 60 | [schema]="schema", 61 | [refBase]="schema", 62 | [depth]="3", 63 | [value]="item") 64 | textarea.form-control(*ngIf="editMode === 'json'", rows="20", [(ngModel)]="itemString") 65 | 66 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/json-schema/schema-label.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core'; 2 | 3 | import {util} from './util'; 4 | 5 | @Component({ 6 | selector: 'schema-label', 7 | templateUrl: './schema-label.pug', 8 | styles: [` 9 | .type-choice.dropdown { 10 | display: inline-block; 11 | margin-left: 5px; 12 | } 13 | `] 14 | }) 15 | export class SchemaLabelComponent { 16 | @Input('schema') inputSchema:any; 17 | schema:any; 18 | @Input() refBase; 19 | @Input() label:string; 20 | @Input() required:boolean; 21 | @Input() expand:boolean; 22 | @Input() showRemove:boolean = false; 23 | @Input() showExpand:boolean; 24 | @Output() typeChange = new EventEmitter(); 25 | @Output() expandChange = new EventEmitter(); 26 | @Output() schemaChange = new EventEmitter(); 27 | @Output() onRemoved = new EventEmitter(); 28 | @ViewChild('descriptionTooltip') descriptionTooltip; 29 | type:string; 30 | typeChoices:string[]; 31 | description:string; 32 | 33 | constructor() {} 34 | 35 | clickRemove() { 36 | this.onRemoved.emit(); 37 | } 38 | 39 | ngOnChanges() { 40 | if (!this.inputSchema) return 41 | this.setSchema(this.inputSchema); 42 | if (this.expand) this.maybeResolveRef(); 43 | } 44 | 45 | setSchema(schema) { 46 | if (schema === this.schema) return; 47 | this.schema = schema; 48 | let resolved = schema.$ref ? util.resolveReference(schema.$ref, this.refBase) : schema; 49 | this.label = this.label || resolved.title; 50 | this.description = resolved.description; 51 | this.typeChoices = null; 52 | let type = util.getTypeForSchema(resolved, this.refBase); 53 | if (Array.isArray(type)) { 54 | if (resolved.type.length > 1) { 55 | this.typeChoices = resolved.type; 56 | } 57 | this.type = type.filter(t => t !== 'null')[0] || type[0]; 58 | } else { 59 | this.type = type; 60 | } 61 | if (this.type !== 'object' && this.type !== 'array' && !this.expand) { 62 | this.toggleExpand(); 63 | } 64 | setTimeout(() => { 65 | this.typeChange.emit(this.type); 66 | this.schemaChange.emit(this.schema); 67 | }); 68 | } 69 | 70 | toggleExpand() { 71 | this.expand = !this.expand; 72 | this.maybeResolveRef(); 73 | this.expandChange.emit(this.expand); 74 | } 75 | 76 | maybeResolveRef() { 77 | if (!this.schema.$ref) return; 78 | this.setSchema(util.resolveReference(this.schema.$ref, this.refBase)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/json-schema/schema-label.pug: -------------------------------------------------------------------------------- 1 | label.monospace 2 | a(*ngIf="showRemove", (click)="clickRemove()") 3 | i.fa.fa-left.fa-times.text-danger 4 | span.text-primary(*ngIf="required") * 5 | span {{label}} 6 | span(*ngIf="label") : 7 | span.hl-key(*ngIf="!typeChoices") {{type}} 8 | .type-choice.dropdown(*ngIf="typeChoices", ngbDropdown) 9 | a.btn.btn-xs.btn-default.dropdown-toggle(data-toggle="dropdown", ngbDropdownToggle) 10 | span {{type}} 11 | .dropdown-menu(ngbDropdownMenu) 12 | a.dropdown-item(*ngFor="let t of typeChoices", (click)="type = t; typeChange.emit(type)") 13 | span {{t}} 14 | a(*ngIf="(showExpand && (type === 'object' || type === 'array')) || schema.$ref", 15 | (click)="toggleExpand()") 16 | i.fa.fa-right([class.fa-plus-square-o]="!expand", [class.fa-minus-square-o]="expand") 17 | p.text-danger(*ngIf="!schema") Unknown schema 18 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/log-in-modal/log-in-modal.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {OneDBService} from '../services/onedb.service'; 3 | import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap'; 4 | import {DomSanitizer, SafeHtml} from '@angular/platform-browser' 5 | 6 | @Component({ 7 | selector: 'log-in-modal', 8 | templateUrl: './log-in-modal.pug', 9 | }) 10 | export class LogInModalComponent { 11 | @ViewChild('content') content; 12 | formContent:SafeHtml; 13 | modalRef:any; 14 | 15 | constructor( 16 | private onedb:OneDBService, 17 | private modals: NgbModal, 18 | private sanitizer:DomSanitizer) { 19 | this.refreshForm(); 20 | this.onedb.onLogin.subscribe(instance => { 21 | this.refreshForm(); 22 | if (this.modalRef && instance.user) this.modalRef.close(); 23 | }) 24 | } 25 | 26 | open() { 27 | this.modalRef = this.modals.open(this.content); 28 | } 29 | 30 | refreshForm() { 31 | this.formContent = this.sanitizer.bypassSecurityTrustHtml(this.onedb.client.loginForm()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/log-in-modal/log-in-modal.pug: -------------------------------------------------------------------------------- 1 | ng-template(#content let-c="close" let-d="dismiss") 2 | .modal-header 3 | h4.modal-title 4 | span(*ngIf="!onedb.client.hosts.primary.user") Log In or Sign Up 5 | span(*ngIf="onedb.client.hosts.primary.user") Log Out or Switch Accounts 6 | button.close(type="button", class="close", aria-label="Close", (click)="d('Cross click')") 7 | span(aria-hidden="true") × 8 | .modal-body 9 | div([innerHtml]="formContent") 10 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/namespace/namespace.pug: -------------------------------------------------------------------------------- 1 | .alert.alert-danger(*ngIf="error") 2 | p {{error}} 3 | p Check the namespace ID and refresh the page to try again. 4 | div(*ngIf="namespace") 5 | h1 {{ namespace.$.id }} 6 | .form-group.view-all-form-group 7 | label Viewing: 8 | .btn-group 9 | button.btn.btn-dark([class.active]="!viewAllData", (click)="setViewAllData(false)") 10 | span My Data 11 | button.btn.btn-dark([class.active]="viewAllData", (click)="setViewAllData(true)") 12 | span All Data 13 | p 14 | span Below is all the data 15 | b {{ !viewAllData ? 'you own ' : 'you have permission to see'}} 16 | span in the {{ namespace.$.id }} namespace 17 | .row 18 | .col.namespace(*ngFor="let type of types; let idx = index;") 19 | h2 {{ type.id }} 20 | a.btn.btn-success([routerLink]="['/data', namespace.$.id, type.id]") 21 | i.fa.fa-left.fa-plus 22 | span Create a new {{ type.id }} 23 | div([ngSwitch]="!viewAllData && !onedb.client.hosts.primary.user") 24 | div(*ngSwitchCase="true") 25 | p Please sign in to view your data 26 | div(*ngSwitchCase="false") 27 | p(*ngIf="!data[type.id]") 28 | i.fa.fa-spin.fa-refresh 29 | p(*ngIf="data[type.id] && !data[type.id].items.length") 30 | span No items found 31 | table(*ngIf="data[type.id] && data[type.id].items.length") 32 | thead 33 | tr 34 | th ID 35 | th(*ngIf="viewAllData") Owner 36 | th(*ngFor="let prop of type.properties") {{ prop }} 37 | tbody 38 | tr.item(*ngFor="let item of data[type.id].items") 39 | td 40 | .cell-content 41 | a([routerLink]="['/data', namespace.$.id, type.id, item.$.id]") 42 | span {{ item.$.id }} 43 | td(*ngIf="viewAllData") 44 | .cell-content 45 | span {{ item.$.owner }} 46 | td(*ngFor="let prop of type.properties") 47 | .cell-content 48 | span {{ item[prop] }} 49 | ul.pagination(*ngIf="data[type.id] && data[type.id].pages") 50 | li.page-item(*ngFor="let page of data[type.id].pages", [class.active]="page.active") 51 | button.page-link((click)="goToPage(type.id, page.skip)", [disabled]="page.disabled") {{ page.label }} 52 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import {ViewChild, Component} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | @Component({ 6 | selector: 'navbar', 7 | templateUrl: './navbar.pug', 8 | }) 9 | export class NavbarComponent { 10 | @ViewChild('logInModal') logInModal; 11 | constructor(public router: Router, public onedb:OneDBService) {} 12 | } 13 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/navbar/navbar.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | nav.navbar.navbar-expand-lg.navbar-dark.bg-dark 3 | .container 4 | a.navbar-brand(routerLink="/") OneDB Data Explorer 5 | div 6 | ul.navbar-nav.ml-auto 7 | li 8 | a.nav-link((click)="logInModal.open()") 9 | i.fa.fa-left.fa-users 10 | span([ngSwitch]="!!onedb.client.hosts.primary.user") 11 | span(*ngSwitchCase="true") 12 | span {{onedb.client.hosts.primary.displayName}} 13 | span(*ngSwitchCase="false") Sign In 14 | 15 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/services/onedb.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | 4 | declare let window:any; 5 | declare let require:any; 6 | const Client = require('onedb-client').Client; 7 | const CORE_HOST = 'https://one-db.datafire.io'; 8 | 9 | const STORAGE_KEY = 'onedb_auth'; 10 | 11 | @Injectable() 12 | export class OneDBService { 13 | client:any; 14 | 15 | onLogin = new BehaviorSubject(null); 16 | 17 | constructor(private zone:NgZone) { 18 | window.onedbService = this; 19 | this.client = new Client({ 20 | hosts: { 21 | core: { 22 | location: CORE_HOST, 23 | } 24 | }, 25 | onLogin: instance => { 26 | this.zone.run(_ => this.onLogin.next(instance)); 27 | }, 28 | }); 29 | this.maybeRestore(); 30 | this.onLogin.subscribe(instance => { 31 | if (!window.localStorage) return 32 | const toStore = { 33 | hosts: this.client.hosts, 34 | }; 35 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore)) 36 | }) 37 | } 38 | 39 | async maybeRestore() { 40 | if (!window.localStorage) return; 41 | let existing:any = window.localStorage.getItem(STORAGE_KEY); 42 | if (!existing) return; 43 | existing = JSON.parse(existing); 44 | if (!existing || !existing.hosts) return; 45 | let hosts = Object.assign({}, existing.hosts, {core: {location: CORE_HOST}}) 46 | await this.client.setHosts(hosts); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/services/platform.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, PLATFORM_ID } from '@angular/core' 2 | import { isPlatformBrowser, isPlatformServer } from '@angular/common' 3 | 4 | @Injectable() 5 | export class PlatformService { 6 | constructor(@Inject(PLATFORM_ID) private platformId: any) { } 7 | 8 | public isBrowser(): boolean { 9 | return isPlatformBrowser(this.platformId) 10 | } 11 | 12 | public isServer(): boolean { 13 | return isPlatformServer(this.platformId) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding-bottom: 50px; 3 | } 4 | 5 | a { 6 | cursor: pointer; 7 | } 8 | 9 | .navbar { 10 | border-radius: 0px; 11 | margin-bottom: 30px; 12 | } 13 | 14 | body { 15 | position: relative; 16 | } 17 | 18 | app > .container { 19 | padding-bottom: 72px; 20 | } 21 | 22 | footer { 23 | position: absolute; 24 | bottom: 0; 25 | left: 0; 26 | right: 0; 27 | 28 | background-color: #fff; 29 | padding-top: 20px; 30 | padding-bottom: 20px; 31 | } 32 | 33 | footer, footer a { 34 | color: #3E3F3A; 35 | } 36 | 37 | .fa-left { 38 | margin-right: 8px; 39 | } 40 | .fa-right { 41 | margin-left: 8px; 42 | } 43 | 44 | .btn-min-width { 45 | min-width: 100px; 46 | } 47 | -------------------------------------------------------------------------------- /apps/data-explorer/src/app/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | @import '~bootstrap/scss/bootstrap'; 4 | 5 | @import './app.scss'; 6 | @import './bootswatch.scss'; 7 | 8 | -------------------------------------------------------------------------------- /apps/data-explorer/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/data-explorer/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/data-explorer/src/assets/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/data-explorer/src/assets/img/Icon.png -------------------------------------------------------------------------------- /apps/data-explorer/src/assets/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/data-explorer/src/assets/img/Logo.png -------------------------------------------------------------------------------- /apps/data-explorer/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | baseHref: '/', 4 | }; 5 | -------------------------------------------------------------------------------- /apps/data-explorer/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | baseHref: '/', 9 | }; 10 | -------------------------------------------------------------------------------- /apps/data-explorer/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/data-explorer/src/favicon.ico -------------------------------------------------------------------------------- /apps/data-explorer/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OneDB Data Explorer 7 | 8 | 9 | 10 | 11 | 12 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /apps/data-explorer/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /apps/data-explorer/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/data-explorer/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | import 'babel-polyfill'; 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /apps/data-explorer/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/data-explorer/src/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | // Set the module format to "commonjs": 7 | "module": "commonjs", 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "test.ts", 12 | "**/*.spec.ts" 13 | ], 14 | // Add "angularCompilerOptions" with the AppServerModule you wrote 15 | // set as the "entryModule". 16 | "angularCompilerOptions": { 17 | "entryModule": "app/app.server.module#AppServerModule" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/data-explorer/static.paths.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '/', 3 | ]; 4 | -------------------------------------------------------------------------------- /apps/data-explorer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types", 13 | "typings.d.ts" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "types": ["node"], 20 | "module": "es2015", 21 | "baseUrl": "./" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/data-explorer/webpack.cli-additions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const commonCliConfig = 'node_modules/@angular/cli/models/webpack-configs/common.js'; 3 | const addition_rules = ` 4 | { test: /\.(pug|jade)$/, loader: 'apply-loader' }, 5 | { test: /\.(pug|jade)$/, 6 | loader: 'pug-loader', 7 | query: { doctype: 'html', plugins: [require('pug-plugin-ng')] }, 8 | }, { 9 | test: /\.json$/, 10 | use: [{ 11 | loader: 'json-loader', 12 | }] 13 | }, 14 | { 15 | test: /\.js$/, 16 | use: [{ 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['env'], 20 | } 21 | }], 22 | }, 23 | { test: /\.md$/, use: [{ loader: 'raw-loader' }, { loader: 'markdown-loader', }] } 24 | ,`; // make sure to have this last comma 25 | 26 | fs.readFile(commonCliConfig, (err, data) => { 27 | 28 | if (err) { throw err; } 29 | 30 | const configText = data.toString(); 31 | // make sure we don't include it (if we've already done this) 32 | if (configText.indexOf(addition_rules) > -1) { return; } 33 | 34 | console.log('-- Inserting additional webpack rules to node_modules CLI -- '); 35 | 36 | const position = configText.indexOf('rules: [') + 8; 37 | const output = [configText.slice(0, position), addition_rules, configText.slice(position)].join(''); 38 | const file = fs.openSync(commonCliConfig, 'r+'); 39 | 40 | fs.writeFile(file, output); 41 | fs.close(file); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /apps/data-explorer/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | // Work around for https://github.com/angular/angular-cli/issues/7200 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | // This is our Express server for Dynamic universal 9 | server: './server.ts', 10 | // This is an example of Static prerendering (generative) 11 | prerender: './prerender.ts' 12 | }, 13 | target: 'node', 14 | resolve: { extensions: ['.ts', '.js'] }, 15 | // Make sure we include all node_modules etc 16 | externals: [/(node_modules|main\..*\.js)/,], 17 | output: { 18 | // Puts the output at the root of the dist folder 19 | path: path.join(__dirname, 'dist'), 20 | filename: '[name].js' 21 | }, 22 | module: { 23 | rules: [ 24 | { test: /\.ts$/, use: {loader: 'ts-loader', options: {}} } 25 | ] 26 | }, 27 | plugins: [ 28 | new webpack.ContextReplacementPlugin( 29 | // fixes WARNING Critical dependency: the request of a dependency is an expression 30 | /(.+)?angular(\\|\/)core(.+)?/, 31 | path.join(__dirname, 'src'), // location of your src 32 | {} // a map of your routes 33 | ), 34 | new webpack.ContextReplacementPlugin( 35 | // fixes WARNING Critical dependency: the request of a dependency is an expression 36 | /(.+)?express(\\|\/)(.+)?/, 37 | path.join(__dirname, 'src'), 38 | {} 39 | ) 40 | ] 41 | } 42 | 43 | -------------------------------------------------------------------------------- /apps/minimal/README.md: -------------------------------------------------------------------------------- 1 | # OneDB minimal app 2 | 3 | This app contains two pages: 4 | * index.html allows you to set your status 5 | * viewer.html allows you to see all status updates 6 | 7 | Additionally, the schemas for the `status` namespace are contained in the directory `./types`. 8 | -------------------------------------------------------------------------------- /apps/minimal/types/status.acl.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow": { 3 | "read": ["_all"] 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /apps/minimal/types/status.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "status": { 5 | "type": "string", 6 | "maxLength": 280, 7 | "minLength": 1 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/minimal/viewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 |

Latest status updates

14 |
15 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /apps/todo/.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | 3 | .idea 4 | .DS_Store 5 | morgan.log 6 | 7 | # Built # 8 | /__build__/ 9 | /__server_build__/ 10 | /node_modules/ 11 | /typings/ 12 | /tsd_typings/ 13 | /dist-server/ 14 | /compiled/ 15 | 16 | # Node # 17 | npm-debug.log 18 | /npm-debug.log.* 19 | 20 | # Webpack # 21 | webpack.records.json 22 | 23 | # Angular # 24 | *.ngfactory.ts 25 | *.css.shim.ts 26 | *.ngsummary.json 27 | *.metadata.json 28 | *.shim.ngstyle.ts 29 | -------------------------------------------------------------------------------- /apps/todo/README.md: -------------------------------------------------------------------------------- 1 | # Angluar 6.0 Template Project 2 | I use this template as a starter for any new Angular projects. 3 | 4 | Check out [the demo](https://bobby-brennan.github.io/angular6-template) 5 | 6 | #### Features 7 | * Angular Universal (prerendering) 8 | * [Pug](https://pugjs.org) templates instead of HTML 9 | * Bootstrap Sass 10 | * Font Awesome 11 | * Angular HTML5 Router 12 | * Standard navbar/body layout 13 | 14 | ## Running the Demo 15 | 16 | #### Install 17 | 18 | ``` 19 | npm install 20 | ``` 21 | 22 | #### Run (development mode) 23 | Start the server on port 3000: 24 | 25 | ``` 26 | npm run start 27 | ``` 28 | 29 | 30 | #### Build (production) 31 | Build everything and put it in the `dist/` folder: 32 | 33 | ``` 34 | npm run build 35 | ``` 36 | 37 | Or build for GitHub pages by copying `dist/browser` to `docs/`. 38 | Be sure to change the settings in your repo to point GitHub pages to 39 | the `docs/` folder on the `master` branch. 40 | 41 | ## Customizing 42 | 43 | ### Base href 44 | The app uses the base href `/` for development builds, and `/angular6-template` 45 | for production builds (to accomodate GitHub pages, which uses the repository 46 | name in the URL's path). You will probably want to change the base href in 47 | `./src/environments/environment.prod.ts`. 48 | 49 | ### Prerendering 50 | Prerendering is a performance optimization - your page's HTML is generated 51 | at build time, so the user sees a near-instant load of the page, while Angular 52 | loads in the background. 53 | 54 | In this build, only the homepage is prerendered - you can add other routes 55 | by editing `./static.paths.ts`. 56 | 57 | ## New Components 58 | A helper script for creating new components is in `./scripts/new-component.js`. 59 | 60 | ``` 61 | node ./scripts/new-component.js --name "Widget Viewer" 62 | ``` 63 | -------------------------------------------------------------------------------- /apps/todo/ng-add-pug-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds the pug-loader inside Angular CLI's webpack config, if not there yet. 3 | * @see https://github.com/danguilherme/ng-cli-pug-loader 4 | */ 5 | const fs = require('fs'); 6 | const commonCliConfig = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js'; 7 | const pugRule = '{ test: /.pug$/, use: [ { loader: "apply-loader" }, { loader: "pug-loader" } ] },'; 8 | 9 | fs.readFile(commonCliConfig, (err, data) => { 10 | if (err) { throw err; } 11 | 12 | const configText = data.toString(); 13 | // make sure we don't add the rule if it already exists 14 | if (configText.indexOf(pugRule) > -1) { return; } 15 | 16 | // Insert the pug webpack rule 17 | const position = configText.indexOf('rules: [') + 8; 18 | const output = [configText.slice(0, position), pugRule, configText.slice(position)].join(''); 19 | const file = fs.openSync(commonCliConfig, 'r+'); 20 | fs.writeFile(file, output); 21 | fs.close(file); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular4-template", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/bobby-brennan/angular4-template.git" 8 | }, 9 | "scripts": { 10 | "ng": "ng", 11 | "start": "ng serve --port 3000 --host 0.0.0.0", 12 | "clean": "rm -rf dist", 13 | "build": "npm run clean && npm run build:static", 14 | "build:gh-pages": "npm run build && rm -rf ./docs && cp -r ./dist/browser ./docs", 15 | "build:client": "ng build browser --prod", 16 | "build:server": "ng run browser:server", 17 | "build:static": "npm run build:client && npm run build:server && npm run webpack:server", 18 | "build:prerender": "npm run build:client && npm run build:server && npm run webpack:server && npm run prerender", 19 | "build:dynamic": "npm run build:client && npm run build:server && npm run webpack:server", 20 | "webpack:server": "webpack --config webpack.server.config.js --progress --colors --mode development", 21 | "prerender": "cd dist && node prerender", 22 | "serve:static": "cd dist/browser && http-server", 23 | "serve:dynamic": "node dist/server", 24 | "postinstall": "node ./ng-add-pug-loader.js" 25 | }, 26 | "private": true, 27 | "dependencies": { 28 | "@angular/animations": "^6.1.0", 29 | "@angular/common": "^6.1.0", 30 | "@angular/compiler": "^6.1.0", 31 | "@angular/core": "^6.1.0", 32 | "@angular/forms": "^6.1.0", 33 | "@angular/http": "^6.1.0", 34 | "@angular/platform-browser": "^6.1.0", 35 | "@angular/platform-browser-dynamic": "^6.1.0", 36 | "@angular/platform-server": "^6.1.0", 37 | "@angular/router": "^6.1.0", 38 | "@ng-bootstrap/ng-bootstrap": "^2.2.0", 39 | "@nguniversal/express-engine": "^6.0.0", 40 | "@nguniversal/module-map-ngfactory-loader": "^6.0.0", 41 | "bootstrap": "^4.1.3", 42 | "core-js": "^2.4.1", 43 | "express": "^4.16.3", 44 | "marked": "^0.4.0", 45 | "ng-cli-pug-loader": "^0.1.3", 46 | "ngx-markdown": "^6.1.0", 47 | "rxjs": "^6.2.2", 48 | "zone.js": "^0.8.14" 49 | }, 50 | "devDependencies": { 51 | "@angular-devkit/build-angular": "~0.7.0", 52 | "@angular/cli": "^6.1.1", 53 | "@angular/compiler-cli": "^6.1.0", 54 | "@angular/language-service": "^6.1.0", 55 | "@types/node": "^10.5.3", 56 | "apply-loader": "^2.0.0", 57 | "babel-core": "^6.26.0", 58 | "babel-loader": "^7.1.2", 59 | "babel-polyfill": "^6.26.0", 60 | "babel-preset-env": "^1.6.1", 61 | "cpy-cli": "^1.0.1", 62 | "font-awesome": "^4.7.0", 63 | "http-server": "^0.10.0", 64 | "jquery": "^3.2.1", 65 | "json-loader": "^0.5.7", 66 | "markdown-loader": "^2.0.1", 67 | "onedb-client": "0.0.3", 68 | "pug": "^2.0.3", 69 | "pug-html-loader": "^1.1.5", 70 | "pug-loader": "^2.4.0", 71 | "pug-plugin-ng": "0.0.2", 72 | "reflect-metadata": "^0.1.10", 73 | "ts-loader": "^4.4.2", 74 | "typescript": "^2.9.2", 75 | "webpack-cli": "^3.1.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /apps/todo/prerender.ts: -------------------------------------------------------------------------------- 1 | // Load zone.js for the server. 2 | import 'zone.js/dist/zone-node'; 3 | import 'reflect-metadata'; 4 | import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; 5 | import { join } from 'path'; 6 | import { chdir } from 'process'; 7 | 8 | import { enableProdMode } from '@angular/core'; 9 | // Faster server renders w/ Prod mode (dev mode never needed) 10 | enableProdMode(); 11 | 12 | // Express Engine 13 | import { ngExpressEngine } from '@nguniversal/express-engine'; 14 | // Import module map for lazy loading 15 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 16 | 17 | import { renderModuleFactory } from '@angular/platform-server'; 18 | 19 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 20 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 21 | 22 | // Get route paths to prerender only static pages 23 | const PATHS = require('./static.paths'); 24 | 25 | const BROWSER_FOLDER = join(process.cwd(), 'browser'); 26 | 27 | // Load the index.html file containing referances to your application bundle. 28 | const index = readFileSync(join('browser', 'index.html'), 'utf8'); 29 | 30 | let prom = Promise.resolve(); 31 | 32 | // Iterate each route path 33 | PATHS.forEach(function (route) { 34 | // Changes current directory to ./dist/browser 35 | chdir(BROWSER_FOLDER); 36 | 37 | // Creates new directories (if not exists) and changes current directory for the nested one 38 | route.split('/').filter(val => val !== '') 39 | .forEach(function (dir) { 40 | if (!existsSync(dir)) { 41 | mkdirSync(dir); 42 | } 43 | chdir(dir); 44 | }); 45 | 46 | // Writes rendered HTML to index.html, replacing the file if it already exists. 47 | prom = prom.then(_ => renderModuleFactory(AppServerModuleNgFactory, { 48 | document: index, 49 | url: route, 50 | extraProviders: [ 51 | provideModuleMap(LAZY_MODULE_MAP) 52 | ] 53 | })).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html)); 54 | }); 55 | -------------------------------------------------------------------------------- /apps/todo/scripts/compare-versions.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let path = require('path'); 3 | let args = require('yargs').argv; 4 | 5 | function addDirToVersions(dir, versions) { 6 | fs.readdirSync(dir).forEach(d => { 7 | if (d.indexOf('.') === 0) { 8 | return; 9 | } 10 | if (d.indexOf('@') === 0) { 11 | addDirToVersions(dir + '/' + d, versions); 12 | } else { 13 | let pkg = require(dir + '/' + d + '/package.json'); 14 | versions[d] = pkg.version; 15 | } 16 | }) 17 | } 18 | 19 | let aVersions = {}; 20 | addDirToVersions(path.resolve(args.a + '/node_modules'), aVersions); 21 | let bVersions = {}; 22 | addDirToVersions(path.resolve(args.b + '/node_modules'), bVersions); 23 | 24 | let keys = Object.keys(aVersions); 25 | for (let key in bVersions) { 26 | if (keys.indexOf(key) === -1) keys.push(key); 27 | } 28 | 29 | keys.forEach(key => { 30 | let v1 = aVersions[key]; 31 | let v2 = bVersions[key]; 32 | if (v1 !== v2) { 33 | console.log(key + '\t' + aVersions[key] + '\t' + bVersions[key]); 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /apps/todo/scripts/new-component.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let componentCode = function(name, filename) { 5 | return ` 6 | import {Component} from '@angular/core'; 7 | 8 | @Component({ 9 | selector: '${filename}', 10 | templateUrl: './${filename}.pug', 11 | }) 12 | export class ${name}Component { 13 | constructor() {} 14 | } 15 | `.trim() 16 | } 17 | 18 | let viewCode = function(name) { 19 | return ` 20 | h1 ${name} 21 | `.trim(); 22 | } 23 | 24 | const APP_DIR = __dirname + '/../src/app/'; 25 | 26 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 27 | const componentName = args.name.replace(/\s/g, ''); 28 | const componentDir = APP_DIR + filename + '/'; 29 | const componentFile = componentDir + filename + '.component.ts'; 30 | const viewFile = componentDir + filename + '.pug'; 31 | const appFile = APP_DIR + 'app.module.ts'; 32 | 33 | let component = componentCode(componentName, filename); 34 | let view = viewCode(args.name); 35 | 36 | fs.mkdirSync(componentDir); 37 | fs.writeFileSync(viewFile, view); 38 | fs.writeFileSync(componentFile, component); 39 | 40 | let app = fs.readFileSync(appFile, 'utf8'); 41 | let lines = app.split('\n').reverse(); 42 | 43 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.component'/)); 44 | lines.splice(insertImportAt, 0, `import {${componentName}Component} from './${filename}/${filename}.component'`); 45 | 46 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Component,/)); 47 | lines.splice(insertDeclarationAt, 0, ` ${componentName}Component,`) 48 | 49 | lines.reverse(); 50 | fs.writeFileSync(appFile, lines.join('\n')); 51 | -------------------------------------------------------------------------------- /apps/todo/scripts/new-service.js: -------------------------------------------------------------------------------- 1 | const args = require('yargs').argv; 2 | const fs = require('fs'); 3 | 4 | let serviceCode = function(name, filename) { 5 | return ` 6 | import {Injectable} from '@angular/core'; 7 | 8 | @Injectable() 9 | export class ${name}Service { 10 | constructor() {} 11 | } 12 | `.trim() 13 | } 14 | 15 | const APP_DIR = __dirname + '/../src/app/'; 16 | 17 | const filename = args.name.toLowerCase().replace(/\s/g, '-'); 18 | const serviceName = args.name.replace(/\s/g, ''); 19 | const serviceDir = APP_DIR + 'services/'; 20 | const serviceFile = serviceDir + filename + '.service.ts'; 21 | const appFile = APP_DIR + 'app.module.ts'; 22 | 23 | let service = serviceCode(serviceName, filename); 24 | 25 | fs.writeFileSync(serviceFile, service); 26 | 27 | let app = fs.readFileSync(appFile, 'utf8'); 28 | let lines = app.split('\n').reverse(); 29 | 30 | let insertImportAt = lines.findIndex(l => l.match(/^import .* from '\.\/.*.service'/)); 31 | lines.splice(insertImportAt, 0, `import {${serviceName}Service} from './services/${filename}.service'`); 32 | 33 | let insertDeclarationAt = lines.findIndex(l => l.match(/^\s+\w+Service,/)); 34 | lines.splice(insertDeclarationAt, 0, ` ${serviceName}Service,`) 35 | 36 | lines.reverse(); 37 | fs.writeFileSync(appFile, lines.join('\n')); 38 | -------------------------------------------------------------------------------- /apps/todo/scripts/update-angular.sh: -------------------------------------------------------------------------------- 1 | npm i --save @angular/animations@latest @angular/router@latest @angular/common@latest @angular/compiler@latest @angular/core@latest @angular/forms@latest @angular/http@latest @angular/platform-browser@latest @angular/platform-browser-dynamic@latest @angular/platform-server@latest @angular/cli@latest @angular/compiler-cli@latest @angular/language-service@latest @nguniversal/express-engine@latest @nguniversal/module-map-ngfactory-loader@latest rxjs@latest 2 | -------------------------------------------------------------------------------- /apps/todo/server.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let app = express(); 3 | 4 | const DIR = __dirname + '/dist/browser'; 5 | const INDEX = require('fs').readFileSync(DIR + '/index.html'); 6 | 7 | app.use(express.static(__dirname + '/dist/browser')); 8 | app.get('*', (req, res) => { 9 | res.end(INDEX); 10 | }) 11 | 12 | app.listen(process.env.PORT || 4010); 13 | -------------------------------------------------------------------------------- /apps/todo/server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import 'reflect-metadata'; 3 | import { renderModuleFactory } from '@angular/platform-server'; 4 | import { enableProdMode } from '@angular/core'; 5 | 6 | import * as express from 'express'; 7 | import { join } from 'path'; 8 | import { readFileSync } from 'fs'; 9 | 10 | // Faster server renders w/ Prod mode (dev mode never needed) 11 | enableProdMode(); 12 | 13 | // Express server 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 4000; 17 | const DIST_FOLDER = join(process.cwd(), 'dist'); 18 | 19 | // Our index.html we'll use as our template 20 | const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString(); 21 | 22 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 23 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 24 | 25 | // Express Engine 26 | import { ngExpressEngine } from '@nguniversal/express-engine'; 27 | // Import module map for lazy loading 28 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 29 | 30 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 31 | app.engine('html', ngExpressEngine({ 32 | bootstrap: AppServerModuleNgFactory, 33 | providers: [ 34 | provideModuleMap(LAZY_MODULE_MAP) 35 | ] 36 | })); 37 | 38 | app.set('view engine', 'html'); 39 | app.set('views', join(DIST_FOLDER, 'browser')); 40 | 41 | /* - Example Express Rest API endpoints - 42 | app.get('/api/**', (req, res) => { }); 43 | */ 44 | 45 | // Server static files from /browser 46 | app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), { 47 | maxAge: '1y' 48 | })); 49 | 50 | // ALl regular routes use the Universal engine 51 | app.get('*', (req, res) => { 52 | res.render('index', { req }); 53 | }); 54 | 55 | // Start up the Node server 56 | app.listen(PORT, () => { 57 | console.log(`Node Express server listening on http://localhost:${PORT}`); 58 | }); 59 | -------------------------------------------------------------------------------- /apps/todo/src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/todo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 |
8 | 9 |
10 | `, 11 | }) 12 | export class AppComponent { 13 | constructor() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/todo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouterModule } from '@angular/router'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpModule } from '@angular/http'; 6 | import {APP_BASE_HREF} from '@angular/common'; 7 | 8 | import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; 9 | 10 | import { appRoutes } from './app.routing'; 11 | import { AppComponent } from './app.component'; 12 | import { HomeComponent } from './home/home.component'; 13 | import { NavbarComponent } from './navbar/navbar.component'; 14 | import {ListComponent} from './list/list.component' 15 | import {LogInModalComponent} from './log-in-modal/log-in-modal.component' 16 | 17 | import {PlatformService} from './services/platform.service'; 18 | import {OneDBService} from './services/onedb.service' 19 | 20 | import { environment } from '../environments/environment'; 21 | import { AutofocusDirective } from './autofocus.directive'; 22 | 23 | @NgModule({ 24 | imports: [ 25 | BrowserModule.withServerTransition({appId: 'my-app'}), 26 | RouterModule.forRoot(appRoutes), 27 | HttpModule, 28 | FormsModule, 29 | NgbModule.forRoot(), 30 | ], 31 | providers: [ 32 | {provide: APP_BASE_HREF, useValue: environment.baseHref || '/'}, 33 | PlatformService, 34 | OneDBService, 35 | ], 36 | declarations: [ 37 | AppComponent, 38 | HomeComponent, 39 | NavbarComponent, 40 | ListComponent, 41 | LogInModalComponent, 42 | AutofocusDirective, 43 | ], 44 | bootstrap: [ AppComponent ], 45 | }) 46 | export class AppModule { } 47 | -------------------------------------------------------------------------------- /apps/todo/src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import {HomeComponent} from './home/home.component'; 3 | import {ListComponent} from './list/list.component'; 4 | 5 | export const appRoutes: Routes = [ 6 | { path: '', component: HomeComponent }, 7 | { path: 'new-list', component: ListComponent }, 8 | { path: 'list/:list_id', component: ListComponent }, 9 | { path: '**', redirectTo: '' }, 10 | ]; 11 | 12 | -------------------------------------------------------------------------------- /apps/todo/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader'; 4 | 5 | import {AppModule} from './app.module'; 6 | import {AppComponent} from './app.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | // The AppServerModule should import your AppModule followed 11 | // by the ServerModule from @angular/platform-server. 12 | AppModule, 13 | ServerModule, 14 | ModuleMapLoaderModule, 15 | ], 16 | // Since the bootstrapped component is not inherited from your 17 | // imported AppModule, it needs to be repeated here. 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppServerModule {} 21 | -------------------------------------------------------------------------------- /apps/todo/src/app/autofocus.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, AfterViewInit, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[autofocus]' 5 | }) 6 | export class AutofocusDirective implements AfterViewInit { 7 | constructor(private el: ElementRef) {} 8 | 9 | ngAfterViewInit() { 10 | this.el.nativeElement.focus(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/todo/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | declare let window:any; 6 | declare let require:any; 7 | 8 | @Component({ 9 | selector: 'home', 10 | templateUrl: './home.pug', 11 | }) 12 | export class HomeComponent { 13 | @ViewChild('logInModal') logInModal; 14 | lists:any[]; 15 | error:string; 16 | constructor(public onedb:OneDBService) { 17 | this.onedb.onLogin.subscribe(host => { 18 | if (host === this.onedb.client.hosts.primary) { 19 | if (host.user) { 20 | this.loadTodoLists(); 21 | } else { 22 | this.lists = []; 23 | } 24 | } 25 | }); 26 | this.initialize(); 27 | } 28 | 29 | async initialize() { 30 | if (this.onedb.user) this.loadTodoLists(); 31 | } 32 | 33 | async loadTodoLists() { 34 | this.error = null; 35 | try { 36 | this.lists = (await this.onedb.client.list('todo', 'list')).items; 37 | } catch (e) { 38 | this.error = e.message; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/todo/src/app/home/home.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | h1 Hey there! 3 | p. 4 | OneToDo is a free and open source tool for creating to-do lists. 5 | p. 6 | OneToDo uses OneDB 7 | for cloud storage, which means you get to decide where your data is stored - 8 | in the cloud, or on your company's servers, or on your local hard drive. 9 | p(*ngIf="!onedb.client.hosts.primary.user") 10 | a((click)="logInModal.open()" href="javascript:void(0)") Sign in 11 | span or 12 | a((click)="logInModal.open()" href="javascript:void(0)") get a free account 13 | 14 | .alert.alert-danger(*ngIf="error" role="alert") {{ error }} 15 | div(*ngIf="onedb.client.hosts.primary.user") 16 | hr 17 | h2.text-center Your To-Do Lists 18 | h2(*ngIf="!lists") 19 | i.fa.fa-spin.fa-refresh 20 | div(*ngIf="lists") 21 | div(*ngIf="!lists.length") 22 | h1.text-center 23 | i.fa.fa-4x.fa-list-ul 24 | p.text-center 25 | span You haven't made any to-do lists. 26 | a(routerLink="/new-list") Start one now 27 | div(*ngIf="lists.length") 28 | .list(*ngFor="let list of lists") 29 | h4 30 | a([routerLink]="['/list', list.$.id]") {{ list.title }} 31 | a.btn.btn-min-width.btn-success(routerLink="/new-list") Create a new to-do list 32 | -------------------------------------------------------------------------------- /apps/todo/src/app/list/list.pug: -------------------------------------------------------------------------------- 1 | div(*ngIf="!list && !error") 2 | h1.text-center 3 | i.fa.fa-spin.fa-refresh 4 | div(*ngIf="list") 5 | h1 Editing {{ list.title }} 6 | .form-group 7 | input.form-control.form-control-lg(type="text", [(ngModel)]="list.title" autofocus) 8 | .item(*ngFor="let item of list.items") 9 | a.checkbox((click)="item.done = !item.$ref && !item.done") 10 | i.fa.fa-2x.fa-left([class.fa-square-o]="!item.done", [class.fa-check-square]="item.done") 11 | .item-title 12 | .input-group 13 | input.form-control( 14 | type="text", 15 | [(ngModel)]="item.$ref ? 'Item ' + item.$ref + ' not found' : item.title", 16 | [class.missing]="item.$ref", [class.done]="item.done", 17 | [disabled]="item.$ref", autofocus) 18 | .input-group-append 19 | button.btn.btn-danger(type="button", (click)="deleteItem(item)") 20 | i.fa.fa-times 21 | .form-group 22 | .row 23 | .col 24 | button.btn.btn-min-width.btn-secondary((click)="list.items.push(newItem())") Add a new item 25 | .col 26 | .btn-toolbar.float-right 27 | button.btn.btn-min-width.btn-success.mr-4((click)="save()") 28 | span(*ngIf="!saving") Save 29 | i.fa.fa-spin.fa-refresh(*ngIf="saving") 30 | button.btn.btn-min-width.btn-danger((click)="delete()") 31 | span Delete 32 | .alert.alert-danger(*ngIf="error" role="alert") {{ error }} 33 | -------------------------------------------------------------------------------- /apps/todo/src/app/log-in-modal/log-in-modal.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewChild} from '@angular/core'; 2 | import {OneDBService} from '../services/onedb.service'; 3 | import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap'; 4 | import {DomSanitizer, SafeHtml} from '@angular/platform-browser' 5 | 6 | @Component({ 7 | selector: 'log-in-modal', 8 | templateUrl: './log-in-modal.pug', 9 | }) 10 | export class LogInModalComponent { 11 | @ViewChild('content') content; 12 | formContent:SafeHtml; 13 | modalRef:any; 14 | 15 | constructor( 16 | private onedb:OneDBService, 17 | private modals: NgbModal, 18 | private sanitizer:DomSanitizer) { 19 | this.refreshForm(); 20 | this.onedb.onLogin.subscribe(instance => { 21 | this.refreshForm(); 22 | if (this.modalRef && instance.user) this.modalRef.close(); 23 | }) 24 | } 25 | 26 | open() { 27 | this.modalRef = this.modals.open(this.content); 28 | } 29 | 30 | refreshForm() { 31 | this.formContent = this.sanitizer.bypassSecurityTrustHtml(this.onedb.client.loginForm()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/todo/src/app/log-in-modal/log-in-modal.pug: -------------------------------------------------------------------------------- 1 | ng-template(#content let-c="close" let-d="dismiss") 2 | .modal-header 3 | h4.modal-title 4 | span(*ngIf="!onedb.client.hosts.primary.user") Log In or Sign Up 5 | span(*ngIf="onedb.client.hosts.primary.user") Log Out or Switch Accounts 6 | button.close(type="button", class="close", aria-label="Close", (click)="d('Cross click')") 7 | span(aria-hidden="true") × 8 | .modal-body 9 | p Open Todo uses OneDB, so you get to decide where your data is stored. 10 | p. 11 | You can create a free account on one-db.datafire.io 12 | 13 | host your own instance 14 | , or choose another provider. 15 | div([innerHtml]="formContent") 16 | -------------------------------------------------------------------------------- /apps/todo/src/app/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import {ViewChild, Component} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import {OneDBService} from '../services/onedb.service'; 4 | 5 | @Component({ 6 | selector: 'navbar', 7 | templateUrl: './navbar.pug', 8 | }) 9 | export class NavbarComponent { 10 | @ViewChild('logInModal') logInModal; 11 | constructor(public router: Router, public onedb:OneDBService) {} 12 | } 13 | -------------------------------------------------------------------------------- /apps/todo/src/app/navbar/navbar.pug: -------------------------------------------------------------------------------- 1 | log-in-modal(#logInModal) 2 | nav.navbar.navbar-expand-lg.navbar-dark.bg-dark 3 | .container 4 | a.navbar-brand(routerLink="/") OneToDo 5 | div 6 | ul.navbar-nav.ml-auto 7 | li 8 | a.nav-link((click)="logInModal.open()") 9 | i.fa.fa-left.fa-users 10 | span([ngSwitch]="!!onedb.client.hosts.primary.user") 11 | span(*ngSwitchCase="true") 12 | span {{onedb.client.hosts.primary.displayName}} 13 | span(*ngSwitchCase="false") Sign In 14 | 15 | -------------------------------------------------------------------------------- /apps/todo/src/app/services/onedb.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | 4 | declare let window:any; 5 | declare let require:any; 6 | const Client = require('onedb-client').Client; 7 | const CORE_HOST = 'https://one-db.datafire.io'; 8 | 9 | const STORAGE_KEY = 'onedb_auth'; 10 | 11 | @Injectable() 12 | export class OneDBService { 13 | client:any; 14 | user:any; 15 | 16 | onLogin = new BehaviorSubject(null); 17 | 18 | constructor(private zone:NgZone) { 19 | window.onedbService = this; 20 | this.client = new Client({ 21 | hosts: { 22 | core: { 23 | location: CORE_HOST, 24 | } 25 | }, 26 | onLogin: user => { 27 | this.zone.run(_ => this.onLogin.next(user)); 28 | }, 29 | scope: ['todo:read', 'todo:create', 'todo:write', 'todo:delete', 'todo:modify_acl', 'todo:append'], 30 | }); 31 | this.maybeRestore(); 32 | this.onLogin.subscribe(user => { 33 | this.user = user; 34 | if (!window.localStorage) return 35 | const toStore = { 36 | hosts: this.client.hosts, 37 | }; 38 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore)) 39 | }) 40 | } 41 | 42 | async maybeRestore() { 43 | if (!window.localStorage) return; 44 | let existing:any = window.localStorage.getItem(STORAGE_KEY); 45 | if (!existing) return; 46 | existing = JSON.parse(existing); 47 | if (!existing || !existing.hosts) return; 48 | let hosts = Object.assign({}, existing.hosts, {core: {location: CORE_HOST}}) 49 | await this.client.setHosts(hosts); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/todo/src/app/services/platform.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, PLATFORM_ID } from '@angular/core' 2 | import { isPlatformBrowser, isPlatformServer } from '@angular/common' 3 | 4 | @Injectable() 5 | export class PlatformService { 6 | constructor(@Inject(PLATFORM_ID) private platformId: any) { } 7 | 8 | public isBrowser(): boolean { 9 | return isPlatformBrowser(this.platformId) 10 | } 11 | 12 | public isServer(): boolean { 13 | return isPlatformServer(this.platformId) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/todo/src/app/styles/_variables.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/todo/src/app/styles/_variables.scss -------------------------------------------------------------------------------- /apps/todo/src/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | a { 2 | cursor: pointer; 3 | } 4 | 5 | .navbar { 6 | border-radius: 0px; 7 | margin-bottom: 30px; 8 | } 9 | 10 | body { 11 | position: relative; 12 | } 13 | 14 | app > .container { 15 | padding-bottom: 72px; 16 | } 17 | 18 | footer { 19 | position: absolute; 20 | bottom: 0; 21 | left: 0; 22 | right: 0; 23 | 24 | background-color: #fff; 25 | padding-top: 20px; 26 | padding-bottom: 20px; 27 | } 28 | 29 | footer, footer a { 30 | color: #3E3F3A; 31 | } 32 | 33 | .fa-left { 34 | margin-right: 8px; 35 | } 36 | .fa-right { 37 | margin-left: 8px; 38 | } 39 | 40 | .btn-min-width { 41 | min-width: 100px; 42 | } 43 | -------------------------------------------------------------------------------- /apps/todo/src/app/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | @import '~bootstrap/scss/bootstrap'; 4 | 5 | @import './app.scss'; 6 | 7 | -------------------------------------------------------------------------------- /apps/todo/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/todo/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/todo/src/assets/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/todo/src/assets/img/Icon.png -------------------------------------------------------------------------------- /apps/todo/src/assets/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/todo/src/assets/img/Logo.png -------------------------------------------------------------------------------- /apps/todo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | baseHref: '/', 4 | }; 5 | -------------------------------------------------------------------------------- /apps/todo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | baseHref: '/', 9 | }; 10 | -------------------------------------------------------------------------------- /apps/todo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/apps/todo/src/favicon.ico -------------------------------------------------------------------------------- /apps/todo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OneToDo 7 | 8 | 9 | 10 | 11 | 12 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /apps/todo/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /apps/todo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/todo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | import 'babel-polyfill'; 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /apps/todo/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/todo/src/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | // Set the module format to "commonjs": 7 | "module": "commonjs", 8 | "types": [] 9 | }, 10 | "exclude": [ 11 | "test.ts", 12 | "**/*.spec.ts" 13 | ], 14 | // Add "angularCompilerOptions" with the AppServerModule you wrote 15 | // set as the "entryModule". 16 | "angularCompilerOptions": { 17 | "entryModule": "app/app.server.module#AppServerModule" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/todo/static.paths.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '/', 3 | ]; 4 | -------------------------------------------------------------------------------- /apps/todo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types", 13 | "typings.d.ts" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "types": ["node"], 20 | "module": "es2015", 21 | "baseUrl": "./" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/todo/types/item.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "title": {"type": "string", "maxLength": 100, "minLength": 1}, 5 | "description": {"type": "string", "maxLength": 1000, "minLength": 1}, 6 | "done": {"type": "boolean", "default": false} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/todo/types/list.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "title": {"type": "string", "maxLength": 100, "minLength": 1}, 5 | "items": { 6 | "type": "array", 7 | "items": {"$ref": "#/definitions/item"} 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/todo/webpack.cli-additions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const commonCliConfig = 'node_modules/@angular/cli/models/webpack-configs/common.js'; 3 | const addition_rules = ` 4 | { test: /\.(pug|jade)$/, loader: 'apply-loader' }, 5 | { test: /\.(pug|jade)$/, 6 | loader: 'pug-loader', 7 | query: { doctype: 'html', plugins: [require('pug-plugin-ng')] }, 8 | }, { 9 | test: /\.json$/, 10 | use: [{ 11 | loader: 'json-loader', 12 | }] 13 | }, 14 | { 15 | test: /\.js$/, 16 | use: [{ 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['env'], 20 | } 21 | }], 22 | }, 23 | { test: /\.md$/, use: [{ loader: 'raw-loader' }, { loader: 'markdown-loader', }] } 24 | ,`; // make sure to have this last comma 25 | 26 | fs.readFile(commonCliConfig, (err, data) => { 27 | 28 | if (err) { throw err; } 29 | 30 | const configText = data.toString(); 31 | // make sure we don't include it (if we've already done this) 32 | if (configText.indexOf(addition_rules) > -1) { return; } 33 | 34 | console.log('-- Inserting additional webpack rules to node_modules CLI -- '); 35 | 36 | const position = configText.indexOf('rules: [') + 8; 37 | const output = [configText.slice(0, position), addition_rules, configText.slice(position)].join(''); 38 | const file = fs.openSync(commonCliConfig, 'r+'); 39 | 40 | fs.writeFile(file, output); 41 | fs.close(file); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /apps/todo/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | // Work around for https://github.com/angular/angular-cli/issues/7200 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = { 7 | entry: { 8 | // This is our Express server for Dynamic universal 9 | server: './server.ts', 10 | // This is an example of Static prerendering (generative) 11 | prerender: './prerender.ts' 12 | }, 13 | target: 'node', 14 | resolve: { extensions: ['.ts', '.js'] }, 15 | // Make sure we include all node_modules etc 16 | externals: [/(node_modules|main\..*\.js)/,], 17 | output: { 18 | // Puts the output at the root of the dist folder 19 | path: path.join(__dirname, 'dist'), 20 | filename: '[name].js' 21 | }, 22 | module: { 23 | rules: [ 24 | { test: /\.ts$/, use: {loader: 'ts-loader', options: {}} } 25 | ] 26 | }, 27 | plugins: [ 28 | new webpack.ContextReplacementPlugin( 29 | // fixes WARNING Critical dependency: the request of a dependency is an expression 30 | /(.+)?angular(\\|\/)core(.+)?/, 31 | path.join(__dirname, 'src'), // location of your src 32 | {} // a map of your routes 33 | ), 34 | new webpack.ContextReplacementPlugin( 35 | // fixes WARNING Critical dependency: the request of a dependency is an expression 36 | /(.+)?express(\\|\/)(.+)?/, 37 | path.join(__dirname, 'src'), 38 | {} 39 | ) 40 | ] 41 | } 42 | 43 | -------------------------------------------------------------------------------- /client/browser.js: -------------------------------------------------------------------------------- 1 | require('babel-polyfill') 2 | window.OneDBClient = require('./lib/client'); 3 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Client: require('./lib/client'), 3 | } 4 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onedb-client", 3 | "version": "0.4.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --exit", 8 | "build": "webpack -p" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "babel-core": "^6.26.0", 14 | "babel-loader": "^7.1.2", 15 | "babel-polyfill": "^6.26.0", 16 | "babel-preset-env": "^1.6.1", 17 | "chai": "^4.1.2", 18 | "mocha": "^5.2.0", 19 | "mongodb-memory-server": "^1.9.0", 20 | "webpack": "^3.5.5" 21 | }, 22 | "dependencies": { 23 | "ajv": "^6.5.2", 24 | "axios": "^0.18.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | module.exports = { 3 | entry: { 4 | "onedb-client": "./browser.js" 5 | }, 6 | output: { 7 | path: __dirname, 8 | filename: "dist/[name].min.js" 9 | }, 10 | resolve: { 11 | extensions: ['.js'] 12 | }, 13 | devtool: 'source-map', 14 | module: { 15 | loaders: [{ 16 | test: /\.js$/, 17 | loader: 'babel-loader?presets[]=env', 18 | }] 19 | }, 20 | node: { 21 | fs: "empty" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cmd/bin/onedb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | require('../cmd.js'); 5 | -------------------------------------------------------------------------------- /cmd/cmd.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const DEFAULT_HOST = "https://one-db.datafire.io"; 4 | 5 | const commands = require('./index'); 6 | 7 | let args = require('yargs') 8 | .option('v', {alias: 'verbose'}) 9 | .global('v') 10 | .recommendCommands(); 11 | 12 | function attempt(fn) { 13 | return async function(args) { 14 | // TODO: why are these not set by yargs defaults? 15 | args.host = args.host || DEFAULT_HOST; 16 | args.directory = args.directory || process.cwd(); 17 | try { 18 | await commands[fn](args); 19 | } catch (e) { 20 | console.log(e.message); 21 | } 22 | } 23 | } 24 | 25 | args = args.command( 26 | 'login', 27 | "Start a OneDB session", 28 | yargs => { 29 | return yargs.option('host', { 30 | alias: 'h', 31 | default: DEFAULT_HOST, 32 | describe: "The OneDB instance to log into", 33 | }) 34 | }, 35 | attempt('login')); 36 | 37 | args = args.command( 38 | 'serve', 39 | "Start a OneDB server", 40 | yargs => { 41 | return yargs.option('port', { 42 | alias: 'p', 43 | describe: 'The port to listen on', 44 | default: 3000, 45 | }) 46 | }, 47 | attempt('serve'), 48 | ); 49 | args = args.command( 50 | 'namespace', 51 | "Create or update a namespace", 52 | yargs => { 53 | yargs = yargs.option('name', { 54 | alias: 'n', 55 | describe: "The ID of the namespace", 56 | demand: true, 57 | }) 58 | yargs = yargs.option('directory', { 59 | alias: 'd', 60 | describe: "The directory containing schema and ACL files", 61 | default: process.cwd(), 62 | }); 63 | yargs = yargs.option('host', { 64 | alias: 'h', 65 | describe: "The OneDB host to send the namespace to", 66 | default: DEFAULT_HOST, 67 | type: 'string', 68 | }) 69 | yargs = yargs.option('username', { 70 | alias: 'u', 71 | describe: "Your username", 72 | }) 73 | yargs = yargs.option('password', { 74 | alias: 'p', 75 | describe: "Your password", 76 | }) 77 | return yargs; 78 | }, 79 | attempt('namespace'), 80 | ) 81 | 82 | args = args.demandCommand(1); 83 | args = args.help('h').alias('h', 'help').strict().argv; 84 | -------------------------------------------------------------------------------- /cmd/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | serve: require('./lib/serve'), 3 | namespace: require('./lib/namespace'), 4 | login: require('./lib/login'), 5 | } 6 | -------------------------------------------------------------------------------- /cmd/lib/files.js: -------------------------------------------------------------------------------- 1 | const homedir = require('os').homedir(); 2 | const npath = require('path'); 3 | module.exports.ONEDB_DIR = npath.join(homedir, '.onedb'); 4 | module.exports.CREDENTIALS_FILE = npath.join(module.exports.ONEDB_DIR, 'credentials.json'); 5 | 6 | -------------------------------------------------------------------------------- /cmd/lib/login.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer'); 2 | const files = require('./files'); 3 | const fs = require('fs'); 4 | const Client = require('onedb-client').Client; 5 | 6 | const USERNAME_QUESTION = {type: 'string', name: 'username', message: "Your OneDB username or email address"}; 7 | const PASSWORD_QUESTION = {type: 'password', name: 'password', message: "Your OneDB password"}; 8 | 9 | module.exports = async function(args) { 10 | if (!fs.existsSync(files.ONEDB_DIR)) { 11 | fs.mkdirSync(files.ONEDB_DIR); 12 | } 13 | if (!fs.existsSync(files.CREDENTIALS_FILE)) { 14 | fs.writeFileSync(files.CREDENTIALS_FILE, '{}'); 15 | } 16 | const creds = require(files.CREDENTIALS_FILE); 17 | const prompt = inquirer.createPromptModule(); 18 | const answers = await prompt([USERNAME_QUESTION, PASSWORD_QUESTION]); 19 | const client = new Client({hosts: {primary: { 20 | location: args.host, 21 | username: answers.username, 22 | password: answers.password, 23 | }}}); 24 | const token = await client.generateToken(client.hosts.primary); 25 | creds[args.host] = {token, username: answers.username}; 26 | fs.writeFileSync(files.CREDENTIALS_FILE, JSON.stringify(creds, null, 2)); 27 | } 28 | -------------------------------------------------------------------------------- /cmd/lib/namespace.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const npath = require('path'); 4 | const Client = require('onedb-client').Client; 5 | const CREDS_FILE = require('./files').CREDENTIALS_FILE; 6 | 7 | module.exports = async function(args) { 8 | let creds = {}; 9 | if (fs.existsSync(CREDS_FILE)) { 10 | creds = require(CREDS_FILE); 11 | creds = creds[args.host] || {}; 12 | } 13 | const host = {location: args.host}; 14 | if (args.username) { 15 | host.username = args.username; 16 | host.password = args.password; 17 | } else if (process.env.ONEDB_USERNAME) { 18 | host.username = process.env.ONEDB_USERNAME; 19 | host.password = process.env.ONEDB_PASSWORD; 20 | } else if (creds.token || creds.password) { 21 | host.username = creds.username; 22 | host.password = creds.password; 23 | host.token = creds.token; 24 | } else { 25 | throw new Error("No credentials found. Please run:\nonedb login"); 26 | } 27 | const client = new Client({ 28 | hosts: { 29 | primary: host, 30 | core: host, 31 | } 32 | }); 33 | 34 | const DIR = npath.join(process.cwd(), args.directory); 35 | 36 | const files = fs.readdirSync(DIR).filter(f => f.endsWith('.schema.json')); 37 | const types = {} 38 | for (let file of files) { 39 | const name = file.replace(/\.schema\.json$/, ''); 40 | const schema = require(npath.join(DIR, file)); 41 | types[name] = {schema} 42 | } 43 | for (let type in types) { 44 | const aclFile = npath.join(DIR, type + '.acl.json'); 45 | if (fs.existsSync(aclFile)) { 46 | types[type].initial_acl = require(aclFile); 47 | } 48 | } 49 | const ns = { 50 | versions: [{types}] 51 | } 52 | let existing = null; 53 | try { 54 | existing = await client.get('core', 'namespace', args.name); 55 | } catch (e) { 56 | if (e.statusCode !== 404) throw e; 57 | } 58 | if (existing) { 59 | await client.append('core', 'namespace', args.name, ns); 60 | console.log("Updated namespace " + args.name); 61 | } else { 62 | await client.create('core', 'namespace', args.name, ns); 63 | console.log("Created namespace " + args.name); 64 | } 65 | process.exit(0); 66 | } 67 | 68 | ;(async () => { 69 | if (require.main === module) { 70 | try { 71 | await module.exports({}) 72 | } catch (e) { 73 | console.error(e.message); 74 | } 75 | } 76 | })(); 77 | -------------------------------------------------------------------------------- /cmd/lib/serve.js: -------------------------------------------------------------------------------- 1 | const Server = require('onedb-server').Server; 2 | const fs = require('fs'); 3 | const npath = require('path'); 4 | const YAML = require('yamljs'); 5 | 6 | const CONFIG_FILE = npath.join(process.cwd(), 'OneDB.yml'); 7 | const DEFAULT_CORE_HOST = "https://one-db.datafire.io"; 8 | 9 | module.exports = function(opts) { 10 | if (fs.existsSync(CONFIG_FILE)) { 11 | opts = Object.assign({}, YAML.load(CONFIG_FILE), opts); 12 | } 13 | opts.namespaces = opts.namespaces || {}; 14 | opts.namespaces.proxy = opts.namespaces.proxy || {}; 15 | if (opts.namespaces.proxy.core === undefined) { 16 | opts.namespaces.proxy.core = DEFAULT_CORE_HOST; 17 | } 18 | let server = new Server(opts); 19 | return server.listen(opts.port); 20 | } 21 | 22 | ;(async () => { 23 | if (require.main === module) { 24 | let opts = require('yargs').argv; 25 | try { 26 | await module.exports(opts); 27 | console.log('OneDB listening on port ' + opts.port); 28 | } catch (e) { 29 | console.log(e.message); 30 | console.log(e.stack); 31 | throw e; 32 | } 33 | } 34 | })(); 35 | -------------------------------------------------------------------------------- /cmd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onedb-cli", 3 | "version": "0.2.0", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=8.0.0" 8 | }, 9 | "scripts": { 10 | "test": "mocha --exit", 11 | "build": "webpack -p" 12 | }, 13 | "bin": { 14 | "onedb": "./bin/onedb" 15 | }, 16 | "author": "", 17 | "license": "MIT", 18 | "devDependencies": {}, 19 | "dependencies": { 20 | "inquirer": "^6.2.0", 21 | "onedb-client": "^0.4.0", 22 | "onedb-server": "0.1.0", 23 | "yamljs": "^0.3.0", 24 | "yargs": "^12.0.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onedb", 3 | "version": "1.0.0", 4 | "description": "OneDB is a decentralized backend-as-a-service.", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "pug-cli": "^1.0.0-alpha6" 9 | }, 10 | "scripts": { 11 | "test": "cd server && npm test ; cd ../client && npm test", 12 | "build": "npm run build:client && npm run build:apps && npm run build:docs && npm run build:homepage", 13 | "build:client": "cd client && npm run build", 14 | "build:apps": "cp client/dist/onedb-client.min.js apps/minimal/ ; cd apps/todo && npm run build ; cd ../data-explorer && npm run build ; cd ../chat && npm run build", 15 | "build:docs": "lucybot build --directory ./web/docs --destination ./web/docs/dist", 16 | "build:homepage": "pug < web/homepage/views/index.pug > web/homepage/index.html" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/bobby-brennan/OneDB.git" 21 | }, 22 | "author": "", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/bobby-brennan/OneDB/issues" 26 | }, 27 | "homepage": "https://github.com/bobby-brennan/OneDB#readme" 28 | } 29 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | plugins/ 2 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Server: require('./lib/server'), 3 | } 4 | -------------------------------------------------------------------------------- /server/lib/config.js: -------------------------------------------------------------------------------- 1 | const ONE_MIN = 60 * 1000; 2 | const FIVE_MIN = 5 * ONE_MIN; 3 | const FIFTEEN_MIN = 15 * ONE_MIN; 4 | 5 | module.exports = { 6 | maxBytesPerItem: 100 * 1000, // 100 kiB 7 | maxItemsPerUser: 10 * 1000, // 1 GiB total 8 | host: 'http://localhost:3000', 9 | namespaces: {}, 10 | rateLimit: { 11 | all: { 12 | windowMs: FIFTEEN_MIN, 13 | max: 900, 14 | delayMs: 0, 15 | }, 16 | users: { 17 | windowMs: FIFTEEN_MIN, 18 | max: 900, 19 | delayMs: 0, 20 | }, 21 | createUser: { 22 | windowMs: FIFTEEN_MIN, 23 | max: 15, 24 | delayMs: 3 * 1000, 25 | delayAfter: 10, 26 | }, 27 | getData: { // 1 qps 28 | windowMs: FIFTEEN_MIN, 29 | max: 900, 30 | delayMs: 0, 31 | }, 32 | mutateData: { // .25 qps 33 | windowMs: FIFTEEN_MIN, 34 | max: 225, 35 | delayMs: 500, 36 | delayAfter: 100, 37 | }, 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /server/lib/error-guard.js: -------------------------------------------------------------------------------- 1 | const errorGuard = module.exports = function(fn) { 2 | return async function(req, res, next) { 3 | try { 4 | await fn(req, res, next); 5 | } catch (err) { 6 | res.status(err.statusCode || 500); 7 | res.json({message: err.message || "Unknown error"}); 8 | } 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /server/lib/fail.js: -------------------------------------------------------------------------------- 1 | const fail = module.exports = function(message, statusCode) { 2 | let err = new Error(message); 3 | if (statusCode) err.statusCode = statusCode; 4 | throw err; 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /server/lib/middleware/authenticate.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const fail = require('../fail'); 3 | const errorGuard = require('../error-guard'); 4 | 5 | module.exports = function(config) { 6 | return errorGuard(async (req, res, next) => { 7 | let auth = req.get('authorization'); 8 | if (!auth) { 9 | req.db = await req.systemDB.user('_all'); 10 | return next(); 11 | } 12 | let parts = auth.split(' '); 13 | if (parts[0] === 'Basic') { 14 | let creds = (new Buffer(parts[1], 'base64')).toString().split(':'); 15 | if (creds.length !== 2) return fail("Invalid authorization header", 401); 16 | email = creds[0]; 17 | password = creds[1]; 18 | req.user = await req.systemDB.signIn(email, password); 19 | req.db = await req.systemDB.user(req.user); 20 | next(); 21 | } else if (parts[0] === 'Bearer') { 22 | let token = parts[1]; 23 | try { 24 | jwt.verify(token, config.jwtSecret); 25 | } catch (e) { 26 | return fail("Invalid Bearer token", 401); 27 | } 28 | const {id, permissions} = await req.systemDB.signInWithToken(token); 29 | req.user = id; 30 | req.db = await req.systemDB.user(id, permissions); 31 | next(); 32 | } else { 33 | return fail("Invalid authorization header", 401); 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /server/lib/middleware/authorize.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const fail = require('../fail'); 3 | const errorGuard = require('../error-guard'); 4 | const validate = require('../validate'); 5 | const util = require('../util'); 6 | 7 | module.exports = function(config) { 8 | return errorGuard(async (req, res, next) => { 9 | const auth = req.get('authorization'); 10 | if (!auth) { 11 | return fail("No authorization header", 401) 12 | } 13 | 14 | // TODO: disallow empty scope 15 | req.query.scope = req.query.scope || ''; 16 | const err = validate.validators.scope(req.query.scope); 17 | if (err) { 18 | return res.status(400).send(err); 19 | } 20 | const permissionsToGrant = req.query.scope ? util.scopes(req.query.scope) : null; 21 | const expiration = req.query.expires_in ? parseInt(req.query.expires_in) : undefined; 22 | 23 | function getToken(email) { 24 | const data = {email, permissionsToGrant}; 25 | const opts = {expiresIn: '1d'}; 26 | return jwt.sign(data, config.jwtSecret, opts); 27 | } 28 | 29 | const parts = auth.split(' '); 30 | if (parts[0] === 'Basic') { 31 | const creds = (new Buffer(parts[1], 'base64')).toString().split(':'); 32 | if (creds.length !== 2) return fail("Invalid authorization header", 401); 33 | const email = creds[0]; 34 | const password = creds[1]; 35 | const token = getToken(email); 36 | await req.systemDB.addToken(email, token, permissionsToGrant, expiration); 37 | res.json(token); 38 | } else if (parts[0] === 'Bearer') { 39 | const currentToken = parts[1]; 40 | try { 41 | jwt.verify(currentToken, config.jwtSecret); 42 | } catch (e) { 43 | return fail("Invalid Bearer token, JWT failed", 401); 44 | } 45 | const {id, email, permissions} = await req.systemDB.signInWithToken(currentToken); 46 | if (permissions === null) { 47 | const token = getToken(email); 48 | await req.systemDB.addToken(email, token, permissionsToGrant, expiration); 49 | res.json(token); 50 | } else { 51 | return fail("Selected token does not have permission to create new auth tokens", 401); 52 | } 53 | } else { 54 | return fail("Invalid authorization header", 401); 55 | } 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /server/lib/middleware/checkUsername.js: -------------------------------------------------------------------------------- 1 | const errorGuard = require('../error-guard'); 2 | 3 | module.exports = errorGuard(async (req, res, next) => { 4 | let available = await req.systemDB.getAvailableUsername(req.params.username); 5 | res.json(available); 6 | }); 7 | -------------------------------------------------------------------------------- /server/lib/middleware/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | fs.readdirSync(__dirname).forEach(file => { 3 | if (file === 'index.js') return; 4 | module.exports[file.replace('.js', '')] = require('./' + file); 5 | }) 6 | -------------------------------------------------------------------------------- /server/lib/routes/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | fs.readdirSync(__dirname).forEach(file => { 3 | if (file === 'index.js') return; 4 | module.exports[file.replace('.js', '')] = require('./' + file); 5 | }) 6 | -------------------------------------------------------------------------------- /server/lib/routes/info.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const packageInfo = require('../../package.json'); 3 | 4 | const router = module.exports = new express.Router(); 5 | router.get('/ping', (req, res) => { 6 | res.json('pong'); 7 | }); 8 | router.get('/info', (req, res) => { 9 | res.json({version: packageInfo.version}); 10 | }); 11 | -------------------------------------------------------------------------------- /server/lib/util/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | fs.readdirSync(__dirname).forEach(file => { 3 | if (file === 'index.js') return; 4 | module.exports[file.replace('.js', '')] = require('./' + file); 5 | }) 6 | -------------------------------------------------------------------------------- /server/lib/util/iterateSchema.js: -------------------------------------------------------------------------------- 1 | const iterateSchema = module.exports = function(schema, fn) { 2 | fn(schema); 3 | if (schema.items) iterateSchema(schema.items, fn); 4 | for (let key in schema.properties) { 5 | iterateSchema(schema.properties[key], fn); 6 | } 7 | if (typeof schema.additionalProperties === 'object') { 8 | iterateSchema(schema.additionalProperties, fn); 9 | } 10 | if (schema.not) iterateSchema(schema.not, fn); 11 | for (let sub of schema.oneOf || []) { 12 | iterateSchema(sub, fn); 13 | } 14 | for (let sub of schema.anyOf || []) { 15 | iterateSchema(sub, fn); 16 | } 17 | for (let sub of schema.allOf || []) { 18 | iterateSchema(sub, fn); 19 | } 20 | for (let key in schema.definitions || {}) { 21 | iterateSchema(schema.definitions[key], fn); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/lib/util/scopes.js: -------------------------------------------------------------------------------- 1 | const SCOPE_ORDER = ['read', 'create', 'write', 'append', 'delete', 'modify_acl']; 2 | 3 | module.exports = function(scopeString) { 4 | const permissions = {}; 5 | scopeString.split(' ').forEach(perm => { 6 | let [namespace, access] = perm.split(':'); 7 | permissions[namespace] = permissions[namespace] || []; 8 | if (!permissions[namespace].includes(access)) { 9 | permissions[namespace].push(access); 10 | } 11 | }); 12 | for (let namespace in permissions) { 13 | permissions[namespace].sort((a1, a2) => { 14 | return SCOPE_ORDER.indexOf(a1) < SCOPE_ORDER.indexOf(a2) ? -1 : 1; 15 | }) 16 | } 17 | return permissions; 18 | } 19 | -------------------------------------------------------------------------------- /server/namespaces/core/acl.js: -------------------------------------------------------------------------------- 1 | const IDENTITY_SCHEMA = { 2 | type: 'string', 3 | minLength: 2, 4 | maxLength: 100, 5 | } 6 | 7 | const ACL_LIST_SCHEMA = { 8 | type: 'array', 9 | items: IDENTITY_SCHEMA, 10 | } 11 | 12 | const ACL_LIST_WITH_DEFAULT_SCHEMA = Object.assign({}, ACL_LIST_SCHEMA, {default: ['_owner']}); 13 | 14 | const ACL_SET_SCHEMA = { 15 | type: 'object', 16 | default: {}, 17 | additionalProperties: false, 18 | properties: { 19 | read: ACL_LIST_SCHEMA, 20 | write: ACL_LIST_SCHEMA, 21 | append: ACL_LIST_SCHEMA, 22 | delete: ACL_LIST_SCHEMA, 23 | } 24 | } 25 | const ACL_SET_WITH_DEFAULT_SCHEMA = { 26 | type: 'object', 27 | default: {}, 28 | additionalProperties: false, 29 | properties: { 30 | read: ACL_LIST_WITH_DEFAULT_SCHEMA, 31 | write: ACL_LIST_WITH_DEFAULT_SCHEMA, 32 | append: ACL_LIST_WITH_DEFAULT_SCHEMA, 33 | delete: ACL_LIST_WITH_DEFAULT_SCHEMA, 34 | } 35 | } 36 | 37 | const ACL_SCHEMA = module.exports = { 38 | type: 'object', 39 | additionalProperties: false, 40 | properties: { 41 | owner: IDENTITY_SCHEMA, 42 | allow: ACL_SET_WITH_DEFAULT_SCHEMA, 43 | disallow: ACL_SET_SCHEMA, 44 | modify: ACL_SET_WITH_DEFAULT_SCHEMA, 45 | }, 46 | } 47 | 48 | -------------------------------------------------------------------------------- /server/namespaces/core/info.js: -------------------------------------------------------------------------------- 1 | const INFO_SCHEMA = module.exports = { 2 | type: 'object', 3 | required: ['created', 'updated', 'created_by'], 4 | additionalProperties: false, 5 | properties: { 6 | created: {type: 'string'}, 7 | updated: {type: 'string'}, 8 | created_by: {type: 'string'}, 9 | }, 10 | } 11 | 12 | -------------------------------------------------------------------------------- /server/namespaces/core/namespace.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | additionalProperties: false, 4 | properties: { 5 | versions: { 6 | type: 'array', 7 | minItems: 1, 8 | items: { 9 | type: 'object', 10 | additionalProperties: false, 11 | properties: { 12 | version: {type: 'string'}, 13 | types: { 14 | type: 'object', 15 | additionalProperties: { 16 | type: 'object', 17 | required: ['schema'], 18 | additionalProperties: false, 19 | properties: { 20 | schema: {$ref: '#/definitions/schema'}, 21 | initial_acl: require('./acl'), 22 | } 23 | } 24 | } 25 | } 26 | } 27 | }, 28 | }, 29 | } 30 | 31 | -------------------------------------------------------------------------------- /server/namespaces/system/authorization_token.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | additionalProperties: false, 4 | properties: { 5 | username: {type: 'string'}, 6 | token: {type: 'string'}, 7 | expires: {type: 'string', format: 'date-time'}, 8 | permissions: { 9 | type: 'object', 10 | additionalProperties: { 11 | type: 'array', 12 | items: { 13 | type: 'string', 14 | enum: ['read', 'write', 'append', 'delete', 'modify_acl', 'create'], 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/namespaces/system/usage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | additionalProperties: false, 4 | properties: { 5 | items: {type: 'integer', minimum: 0}, 6 | namespaces: { 7 | type: 'array', 8 | items: {type: 'string'}, 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/namespaces/system/user.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | properties: { 4 | publicKey: {type: 'string'}, 5 | }, 6 | additionalProperties: false, 7 | } 8 | 9 | -------------------------------------------------------------------------------- /server/namespaces/system/user_private.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | additionalProperties: false, 4 | properties: { 5 | id: {type: 'string'}, 6 | email: {type: 'string'}, 7 | hash: {type: 'string'}, 8 | salt: {type: 'string'}, 9 | email_confirmation: { 10 | type: 'object', 11 | additionalProperties: false, 12 | properties: { 13 | confirmed: {type: 'boolean'}, 14 | code: {type: 'string'}, 15 | expires: {type: 'string', format: 'date-time'}, 16 | } 17 | }, 18 | password_reset: { 19 | type: 'object', 20 | additionalProperties: false, 21 | properties: { 22 | code: {type: 'string'}, 23 | expires: {type: 'string', format: 'date-time'}, 24 | } 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onedb-server", 3 | "version": "0.3.3", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=8.0.0" 8 | }, 9 | "scripts": { 10 | "test": "mocha --exit" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "ajv": "^6.5.1", 16 | "aws-sdk": "^2.292.0", 17 | "axios": "^0.18.0", 18 | "body-parser": "^1.18.3", 19 | "cors": "^2.8.4", 20 | "express": "^4.16.3", 21 | "express-rate-limit": "^2.11.0", 22 | "jsonwebtoken": "^8.3.0", 23 | "moment": "^2.22.2", 24 | "mongodb": "^3.1.6", 25 | "nodemailer": "^4.6.7", 26 | "pug": "^2.0.3", 27 | "randomstring": "^1.1.5", 28 | "redos": "^1.0.1", 29 | "validator": "^10.4.0", 30 | "yargs": "^12.0.1" 31 | }, 32 | "devDependencies": { 33 | "chai": "^4.1.2", 34 | "chai-as-promised": "^7.1.1", 35 | "mocha": "^5.2.0", 36 | "mongodb-memory-server": "^1.9.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/test/db-util.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const util = require('../lib/db-util'); 3 | 4 | describe('Util', () => { 5 | it('should encode document', () => { 6 | const schema = { 7 | foo: 'bar', 8 | $ref: 'abc', 9 | nested: { 10 | $ref: 'qux', 11 | }, 12 | array: [{ 13 | $ref: 'def', 14 | }] 15 | }; 16 | const encoded = util.encodeDocument(schema); 17 | expect(encoded).to.deep.equal({ 18 | foo: 'bar', 19 | '\uFF04ref': 'abc', 20 | nested: { 21 | '\uFF04ref': 'qux', 22 | }, 23 | array: [{ 24 | '\uFF04ref': 'def', 25 | }] 26 | }); 27 | const decoded = util.decodeDocument(encoded); 28 | expect(decoded).to.deep.equal(schema); 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /server/web/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | link(rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css") 5 | link(rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css") 6 | style. 7 | body { 8 | background-color: #eee; 9 | padding-top: 25px; 10 | } 11 | .btn { 12 | min-width: 150px; 13 | } 14 | .form { 15 | background-color: #fff; 16 | } 17 | script( 18 | src="https://code.jquery.com/jquery-3.3.1.min.js" 19 | integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" 20 | crossorigin="anonymous") 21 | script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.bundle.min.js") 22 | block head 23 | 24 | body 25 | .container 26 | block body 27 | -------------------------------------------------------------------------------- /server/web/views/reset_password.pug: -------------------------------------------------------------------------------- 1 | extends layout.pug 2 | block head 3 | script. 4 | var CODE = !{JSON.stringify(code || '')}; 5 | 6 | function showError(msg) { 7 | $('.alert.alert-danger').text(msg).css('opacity', 1); 8 | } 9 | 10 | function hideError() { 11 | $('.alert.alert-danger').css('opacity', 0); 12 | } 13 | 14 | function showLoading() { 15 | $('button.btn-success').attr('disabled', true); 16 | $('button.btn-success span').hide(); 17 | $('button.btn-success i.fa').show(); 18 | } 19 | 20 | function hideLoading() { 21 | $('button.btn-success').attr('disabled', false); 22 | $('button.btn-success span').show(); 23 | $('button.btn-success i.fa').hide(); 24 | } 25 | 26 | function resetPassword() { 27 | hideError(); 28 | var data = {code: CODE, newPassword: $('input[name="password"]').val()}; 29 | var confirmPassword = $('input[name="confirmPassword"]').val(); 30 | if (data.newPassword !== confirmPassword) { 31 | showError("Passwords don't match"); 32 | return false; 33 | } 34 | showLoading(); 35 | $.ajax({ 36 | type: 'POST', 37 | url: '/users/reset_password', 38 | headers: {'Content-Type': 'application/json'}, 39 | data: JSON.stringify(data), 40 | success: function(data) { 41 | hideLoading(); 42 | $('.card').replaceWith('
Your password has been successfully reset.
') 43 | }, 44 | error: function(req, textStatus, error) { 45 | hideLoading(); 46 | var message = textStatus; 47 | try { 48 | message = JSON.parse(req.responseText).message || message; 49 | } catch (e) {} 50 | showError(message); 51 | }, 52 | }) 53 | return false; 54 | } 55 | 56 | block body 57 | .row 58 | .col-12.col-md-6.offset-md-3 59 | .alert.alert-danger(style="opacity: 0") 60 | span   61 | .card 62 | .card-body 63 | h2.card-title Reset your password 64 | form(onsubmit="resetPassword(); return false") 65 | .form-group 66 | label New Password 67 | input.form-control(type="password" name="password") 68 | .form-group 69 | label Confirm Password 70 | input.form-control(type="password" name="confirmPassword") 71 | .form-group 72 | button.btn.btn-success(type="submit") 73 | i.fa.fa-spin.fa-refresh(style="display: none") 74 | span Reset Password 75 | -------------------------------------------------------------------------------- /web/docs/LucyBot.yml: -------------------------------------------------------------------------------- 1 | favicon: assets/img/Icon.png 2 | 3 | routes: 4 | /: 5 | ui: documentation 6 | title: OneDB 7 | meta: 8 | title: OneDB Documentation 9 | description: Documentation for OneDB, an open source, decentralized backend-as-a-service 10 | keywords: 11 | - OneDB 12 | - documentation 13 | - open source 14 | - federated 15 | - decentralized 16 | - backend 17 | - frontend 18 | - backend as a service 19 | - BaaS 20 | navigation: 21 | - title: Introduction 22 | markdownFile: markdown/Overview.md 23 | - title: Create an App 24 | expand: true 25 | autoselect: true 26 | children: 27 | - title: Quickstart 28 | markdownFile: markdown/Quickstart.md 29 | - title: End-to-end Example 30 | markdownFile: markdown/End_to_End.md 31 | - markdownFile: markdown/Client_API.md 32 | - markdownFile: markdown/Data_Schemas.md 33 | - title: Authorization 34 | markdownFile: markdown/Authorization.md 35 | - title: Multiple Instances 36 | markdownFile: markdown/Multiple_Instances.md 37 | - title: Host an Instance 38 | markdownFile: markdown/Host_an_Instance.md 39 | - title: HTTP API 40 | markdownFile: markdown/HTTP.md 41 | -------------------------------------------------------------------------------- /web/docs/assets/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/docs/assets/img/Icon.png -------------------------------------------------------------------------------- /web/docs/assets/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/docs/assets/img/Logo.png -------------------------------------------------------------------------------- /web/docs/assets/img/quickstart_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/docs/assets/img/quickstart_screenshot.png -------------------------------------------------------------------------------- /web/docs/markdown/Overview.md: -------------------------------------------------------------------------------- 1 | # OneDB 2 | 3 |
4 | ![OneDB Logo](assets/img/Logo.svg) 5 | 6 |

Connect your frontend to the cloud. For free, forever.

7 |
8 | 9 | OneDB is an open source decentralized backend - it handles user authentication, data storage, and validation, 10 | so **you can focus on creating a beautiful user interface**. 11 | 12 | Anyone can host a OneDB instance, and end-users can decide where they want to store their data. 13 | This means **end-users have complete control and ownership** over their data. And by utilizing 14 | the existing network of OneDB instances, you can **deploy your app for free**, forever. 15 | 16 | We provide a OneDB instance at `one-db.datafire.io` which is free for developers. End-users can 17 | store up to 10MB of data before getting charged. 18 | 19 | For troubleshooting, comments, and ideas, [reach out to us on GitHub](https://github.com/DataFire/OneDB) 20 | 21 | ## Ready to get started? 22 | 23 | Check out [the Quickstart page](/Create_an_App/Quickstart) to learn how to create a minimal OneDB app, 24 | or head to [the End-to-end example](/Create_an_App/End_to_end_Example) for a more in-depth look at OneDB. 25 | -------------------------------------------------------------------------------- /web/docs/styles/styles.css: -------------------------------------------------------------------------------- 1 | .main-container { 2 | padding-top: 50px; 3 | } 4 | @media(min-width:768px) { 5 | .side-menu { 6 | position: fixed; 7 | top: 50px; 8 | left: 15px; 9 | width: inherit; 10 | max-height: 90%; 11 | overflow-y: scroll; 12 | } 13 | .side-menu-col + .col-xs-12 { 14 | padding-left: 30px; 15 | } 16 | } 17 | 18 | .docs-contents { 19 | font-size: 16px; 20 | color: #555; 21 | } 22 | 23 | .docs-contents h1, 24 | .docs-contents h2, 25 | .docs-children h4 { 26 | margin-top: 60px; 27 | } 28 | .docs-contents h2:first-of-type { 29 | margin-top: 30px; 30 | } 31 | 32 | .docs-contents h1:first-of-type { 33 | display: none; 34 | } 35 | 36 | side-menu .btn { 37 | border-color: #ddd; 38 | } 39 | 40 | nav .navbar-brand { 41 | padding-top: 3px; 42 | } 43 | 44 | nav img { 45 | height: 36px; 46 | padding-top: 10px; 47 | float: left; 48 | margin-right: 10px; 49 | } 50 | 51 | thead { 52 | display: none; 53 | } 54 | 55 | pre { 56 | margin-bottom: 15px; 57 | } 58 | -------------------------------------------------------------------------------- /web/docs/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 10 | 16 | 30 | -------------------------------------------------------------------------------- /web/homepage/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/Icon.png -------------------------------------------------------------------------------- /web/homepage/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/Logo.png -------------------------------------------------------------------------------- /web/homepage/img/team/andrew.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/andrew.jpg -------------------------------------------------------------------------------- /web/homepage/img/team/bob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/bob.png -------------------------------------------------------------------------------- /web/homepage/img/team/bobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/bobby.jpg -------------------------------------------------------------------------------- /web/homepage/img/team/joe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/joe.jpg -------------------------------------------------------------------------------- /web/homepage/img/team/keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/keep -------------------------------------------------------------------------------- /web/homepage/img/team/throop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/homepage/img/team/throop.jpg -------------------------------------------------------------------------------- /web/img/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/img/Icon.png -------------------------------------------------------------------------------- /web/img/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataFire/OneDB/18ac8a50ebd6345e348045cda791813b705aba30/web/img/Logo.png --------------------------------------------------------------------------------