├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── Procfile ├── README.md ├── config ├── config.json-dist ├── defs.js └── upstart_bip.conf ├── migrations ├── 0.2.44 │ └── index.js ├── 0.2.50 │ └── index.js ├── 0.2.79 │ └── index.js ├── 0.3.0 │ └── index.js ├── 0.3.20 │ └── index.js ├── 0.3.24 │ └── index.js ├── 0.3.39 │ └── index.js ├── 0.3.40 │ └── index.js ├── 0.3.44 │ └── index.js ├── 0.4.16 │ └── index.js ├── 0.4.19 │ └── index.js └── 0.4.4 │ └── index.js ├── package.json ├── src ├── bootstrap.js ├── lib │ ├── bastion.js │ ├── dao-mongo.js │ ├── dao.js │ ├── helper.js │ ├── rabbit.js │ └── step.js ├── models │ ├── account.js │ ├── account_activity.js │ ├── account_auth.js │ ├── account_log.js │ ├── account_option.js │ ├── bip.js │ ├── bip_log.js │ ├── bip_share.js │ ├── channel.js │ ├── channel_log.js │ ├── domain.js │ ├── migration.js │ ├── prototype.js │ ├── stats_account.js │ ├── stats_account_network.js │ ├── stats_global.js │ └── transform_default.js ├── modules │ ├── auth │ │ ├── http_basic.js │ │ ├── ldap.js │ │ ├── models │ │ │ └── account_info.js │ │ └── native.js │ └── cdn │ │ ├── LICENSE │ │ ├── README.md │ │ ├── cdn.js │ │ ├── config.json │ │ ├── fs.js │ │ ├── index.js │ │ ├── package.json │ │ └── utils.js ├── router.js ├── server.js └── worker.js ├── tests ├── lib │ └── helper.js ├── managers │ ├── dao-mongo.js │ └── dao.js ├── migrations │ └── 0.3.0.js └── models │ ├── bip.js │ ├── channel.js │ └── domain.js └── tools ├── archive_logs.js ├── avsnarf.js ├── bip-expire.js ├── bip-trigger.js ├── cdnfactory.js ├── create_user.js ├── crypt_get.js ├── gencert.sh ├── generate-hub-stats.js ├── git-setup.sh ├── heapdump.js ├── init-boilerplate.sh ├── login_set.js ├── oauth_refresh.js ├── pause_bip.js ├── pod-install.js ├── post-merge ├── rm_user.js ├── set_user_level.js ├── setup.js ├── test_jwt_token.js ├── token_get.js ├── token_regen.js ├── token_set.js ├── triage.js └── update-transforms.js /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ 2 | node_modules 3 | config/default.json 4 | config/staging.json 5 | config/production.json 6 | config/haraka 7 | config/credentials 8 | data 9 | config/runtime.json 10 | npm-debug.log 11 | sftp_config.json 12 | .DS_Store 13 | src/modules/stats 14 | src/modules/permissions 15 | src/modules/provisioning 16 | src/modules/mozu 17 | logs 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ 2 | node_modules 3 | config/default.json 4 | config/staging.json 5 | config/production.json 6 | config/haraka 7 | data 8 | config/runtime.json 9 | npm-debug.log 10 | src/modules/stats 11 | src/modules/mozu 12 | src/modules/permissions 13 | src/modules/provisioning 14 | logs 15 | config/runtime* 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Your contributions are essential to keeping bip.io fit and healthy. With tens of thousands of API's on the web ripe for integration, there's plenty of challenges and opportunity for you to be a valued community partner and for bip.io to make it worth your while. We want to keep it as easy as possible to contribute changes that 4 | get things working in your environment. There are a few guidelines that we 5 | need contributors to follow so that we can have a chance of keeping on 6 | top of thing and are all speaking the same language. 7 | 8 | 9 | ## Getting Started 10 | 11 | * Make sure you have a [GitHub account](https://github.com/signup/free) 12 | * Submit a ticket for your issue, assuming one does not already exist on the [repo issue tracker](https://github.com/bipio-server/bipio/issues). 13 | * Clearly describe the issue including steps to reproduce when it is a bug. 14 | * Make sure you fill in the earliest version that you know has the issue. 15 | * Fork the repository on GitHub 16 | 17 | ## Making Changes 18 | 19 | * Create a topic branch from where you want to base your work. 20 | * This is usually the master branch. 21 | * Only target release branches if you are certain your fix must be on that 22 | branch. 23 | * To quickly create a topic branch based on master; `git checkout -b 24 | fix/master/my_contribution master`. Please avoid working directly on the 25 | `master` branch. 26 | * Make commits of logical units. 27 | * Check for unnecessary whitespace with `git diff --check` before committing. 28 | * Make sure your commit messages are in the proper format. 29 | 30 | ```` 31 | fixes #1 32 | 33 | Make the example in CONTRIBUTING imperative and concrete 34 | 35 | Without this patch applied the example commit message in the CONTRIBUTING 36 | document is not a concrete example. This is a problem because the 37 | contributor is left to imagine what the commit message should look like 38 | based on a description rather than an example. This patch fixes the 39 | problem by making the example concrete and imperative. 40 | 41 | The first line is a real life imperative statement with a ticket number 42 | from our issue tracker. The body describes the behavior without the patch, 43 | why this is a problem, and how the patch fixes the problem when applied. 44 | ```` 45 | 46 | ## Submitting Changes 47 | 48 | * Push your changes to a topic branch in your fork of the repository. 49 | * Submit a pull request to the bipio repository in the bipio-server organization. 50 | * Update your Github issue to mark that you have submitted code and are ready for it to be reviewed, be sure to reference the issue number in your forked commit message (Status: Ready for Merge). 51 | 52 | ## Migrations 53 | 54 | [Migrations](http://en.wikipedia.org/wiki/Software_modernization) is the mechanism bip.io uses to upgrade itself when its software version changes. To create a migration, create the file `migrations/<% pkg.version%>/index.js`. 55 | 56 | [Example Migration](https://github.com/bipio-server/bipio/blob/master/migrations/0.2.50/index.js) 57 | 58 | ## Submitting Changes to Pods 59 | 60 | Changes to Pods (ie: bip-pod-? repositories in the Github bipio-server organization) should follow the same steps as above, however issues should be logged and tracked for each individual pod repository. 61 | 62 | ## Submitting New Pods for endorsement 63 | 64 | * Create a Pod under your own GitHub account named `bip-pod-{pod name}` 65 | * Submit a new issue in the [repo issue tracker](https://github.com/bipio-server/bipio/issues), detailing the new pod, some potential use cases (for testing), and a link to your repository 66 | * Your Pod repo will be installed and peer reviewed for scalability and security, and rolled into the [Official Pods List](https://github.com/bipio-server/bipio/wiki/Pod-List). If its generally useful, it may even ship with the server itself. 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # TESTS = tests/*.js tests/managers/*.js tests/models/*.js 2 | # TESTS = tests/*.js tests/managers/*.js 3 | # TESTS = tests/models/channel.js 4 | TESTS = tests/migrations/0.3.0.js 5 | REPORTER = dot 6 | 7 | install: 8 | ./tools/git-setup.sh 9 | @SYSTEM_TZ=`/usr/bin/env date +%Z` ./tools/setup.js 10 | 11 | test-install: 12 | @NODE_ENV=test ./tools/setup.js 13 | 14 | clean: 15 | rm ./config/*.json 16 | 17 | # node-inspector :: 18 | # --debug 19 | # --debug-brk 20 | test: 21 | @NODE_ENV=test ./node_modules/.bin/mocha \ 22 | --reporter $(REPORTER) \ 23 | --timeout 15000 \ 24 | $(TESTS) 25 | 26 | test-cov: lib-cov 27 | @CONNECT_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html 28 | 29 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node-supervisor src/server.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **NOTICE:** This repository has been **DEPRECATED**. Do not use. 2 | 3 |
4 | ========= 5 | 6 | Welcome to the [bip.io](https://bip.io) API Server (Snow 0.4) 7 | 8 | bip.io is Billion Instructions Per I/O - For People and Robots. 9 | 10 | Imagine you can send a single standard payload and have a limitless host of API's orchestrate at your command. 11 | 12 | That's what bip.io does. 13 | 14 | [![NPM](https://nodei.co/npm/bipio.png?downloads=true)](https://nodei.co/npm/bipio/) 15 | 16 | Follow @bipioapp on Twitter for regular news and updates. 17 | 18 | ---- 19 | 20 | bip.io is a nodejs based web automation framework that runs 'bips'. A Bip is a web automation agent. A Bip can connect many different web services and perform useful work. It can act on your behalf or perform at your command. 21 | 22 | Bips can take actions in sequence or in parallel, and chain web services together as you like. They can be put to work via Web Hooks, Email or Trigger when something happens. 23 | 24 | bip.io can be installed alongside your existing open source app or prototype for out-of-band message transformation, feed aggregation, queuing, social network fanout or whatever you like, even on your [Raspberry Pi](http://www.raspberrypi.org/). 25 | 26 | This server software is a RESTful JSON API supporting account level namespacing and multiple domains ([fqdn](http://en.wikipedia.org/wiki/Fully_qualified_domain_name)) per account. Clients authenticate over HTTP Basic. 27 | 28 | bip.io is dynamic, flexible, fast, modular, opinionless and GPLv3 open source. 29 | 30 | Find out more in [the wiki](https://github.com/bipio-server/bipio/wiki). 31 | 32 |
33 | 34 | 35 | ### Pods 36 | 37 | Pods are the standalone service containers bip.io uses to encapsulate and standardize the world's API's. [Supported Services](https://github.com/bipio-server/bipio/wiki/Pod-List) are growing fast, and open source. 38 | 39 | This server ships with a few handy '[Pods](https://github.com/bipio-server/bipio/wiki/Pods)' which you can use right away - Email, Text/HTML/Markdown Templating, Flow Control, Syndication, Web Hooks and Time. 40 | 41 | Extra Pods can be found in the [master repository](https://github.com/bipio-server). 42 | 43 | To install a pod : 44 | 45 | npm install bip-pod-{pod-name} 46 | ./tools/pod-install.js -a {pod-name} 47 | 48 | And follow the instructions. 49 | 50 | Feel free to [craft your own](https://github.com/bipio-server/bipio/wiki/Pods#creating-pods). 51 | 52 | ## Requirements 53 | 54 | - [Node.js >= 0.10.15](http://nodejs.org) **API and graph resolver** 55 | - [MongoDB Server v2.6](http://www.mongodb.org) **data store** 56 | - [RabbitMQ](http://www.rabbitmq.com) **message broker** 57 | 58 | SMTP Bips are available out of the box with a Haraka plugin. Configs under [bipio-contrib/haraka](https://github.com/bipio-server/bipio-contrib). 59 | 60 | - [Haraka](https://github.com/baudehlo/Haraka) 61 | 62 | ## Installation 63 | 64 | ### docker (offical) 65 | 66 | [Find It Here](https://github.com/bipio-server/bipio-docker) 67 | 68 | #### npm (global) 69 | 70 | sudo npm install -g bipio 71 | bipio 72 | 73 | #### npm (local) 74 | 75 | sudo npm install bipio 76 | cd node_modules 77 | npm start 78 | 79 | #### git 80 | 81 | git clone git@github.com:bipio-server/bipio.git 82 | cd bipio 83 | npm install 84 | node . (or `npm start`) 85 | 86 | ### Visual Tools 87 | 88 | The bip.io server is a light weight headless API server and ships without a User Interface (UI). The official UI can be found on the [bip.io](https://bip.io) hosted platform. It's completely free, will run on all desktops and most tablets. 89 | 90 | [![ScreenShot](https://bip.io/static/img/docs/vimeo_overlay.png)](https://vimeo.com/147186752) 91 | 92 | To learn about the UI, the [community knowledgebase](https://bip.uservoice.com/knowledgebase) is the best place to start. 93 | 94 | Although bip.io is a hosted cloud platform, you can still use it to manage your own bip.io server with a feature called ['Mounts'](https://bip.uservoice.com/knowledgebase/articles/764829-where-is-the-user-interface-for-my-open-source-bip) 95 | 96 | Sign in to [bip.io](https://bip.io) to mount your local install from your browser under My Account > Mounts > Create Mount. 97 | 98 | ![Server Mount](https://bip.io/static/img/docs/server_mount.png) 99 | 100 | #### Mounting Security Notes 101 | 102 | Be sure to answer **yes** to the SSL question during setup to install a self signed SSL certificate. 103 | 104 | `Enable SSL? This will let you mount this server from the https://bip.io dashboard :` 105 | 106 | This will avoid any browser security restrictions when mounting your server via the hosted website. You *must* visit your bipio server in a browser first and accept the self signed certificate, or the mount may not work eg : `https://localhost:5000/status` 107 | 108 | The UI is a thin client which is loaded entirely into your browser. Once loaded you can reach any bipio server your browser can connect to such as from behind any firewall, over VPN or IP tunnel etc. 109 | 110 | ## Technical Notes 111 | 112 | When setting bip.io up for the first time, the install process will enter interactive mode, saving the generated config to `config/default.json`. 113 | 114 | The location of the config file can be overrideen using the `NODE_CONFIG_DIR` environment variable. 115 | 116 | export NODE_CONFIG_DIR= 117 | 118 | Be sure to have a MongoDB server and Rabbit broker ready and available before install. Otherwise, follow the prompts during the install process to get a basically sane server running that you can play with. 119 | 120 | For Ubuntu users, a sample upstart script is supplied in `config/upstart_bip.conf` which should be copied to 121 | `/etc/init` and reconfigured to suit your environment. 122 | 123 | If you have a more complex deployment environment and the packaged sparse config doesn't suit, don't worry! Set the environment variable `BIPIO_SPARSE_CONFIG` to the path of your preferred config file, and it will use that instead. 124 | 125 | For a non-interactive setup (ie: make install without any user interaction) - set environment variable `HEADLESS=true` 126 | 127 | bip.io does not provide any load balancing beyond [node-cluster](http://nodejs.org/api/cluster.html). It can provide SSL termination but this is unsuitable for a production environment. If you need SSL termination this should be delegated to the forward proxy of your choice such as Nginx, Apache, HAProxy etc. 128 | 129 | ## Developing and Contributing 130 | 131 | A healthy contributor community is great for everyone! Take a look at the [Contribution Document](https://github.com/bipio-server/bipio/blob/master/CONTRIBUTING.md) to see how to get your changes merged in. 132 | 133 | ## Support 134 | 135 | Please log issues to the [repository issue tracker](https://github.com/bipio-server/bipio/issues) on GitHub. 136 | 137 | ## License 138 | 139 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 140 | 141 | http://www.apache.org/licenses/LICENSE-2.0 142 | 143 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 144 | -------------------------------------------------------------------------------- /config/config.json-dist: -------------------------------------------------------------------------------- 1 | { 2 | "site_name" : "bip.io", 3 | "domain": "localhost", 4 | "domain_public" : "localhost:5000", 5 | "website_public": "http://localhost", 6 | "cdn_public": "http://localhost/static", 7 | "oembed_host": "http://localhost", 8 | "site_emitter": "https://bip.io", 9 | "proto_public": "http://", 10 | "crons" : { 11 | "stat" : "0 0 * * * *", 12 | "trigger" : "0 */15 * * * *", 13 | "expire" : "0 0 * * * *", 14 | "transforms_compact": "0 0 */8 * * *", 15 | "transforms_fetch": "0 0 */12 * * *" 16 | }, 17 | "timezone" : "Asia/Tokyo", 18 | "server": { 19 | "port": 5000, 20 | "host" : "127.0.0.1", 21 | "sessionSecret" : "", 22 | "forks": 1, 23 | "smtp_bips" : false, 24 | "logLevel" : "info", 25 | "ssl" : { 26 | "key" : "", 27 | "cert" : "" 28 | } 29 | }, 30 | "mailer": { 31 | "host": "localhost", 32 | "port": 25 33 | }, 34 | "dbMongo": { 35 | "connect" : "localhost/bipio" 36 | }, 37 | "rabbit": { 38 | "host" : "localhost", 39 | "vhost" : "", 40 | "auth": { 41 | "username": "guest", 42 | "password": "guest" 43 | }, 44 | "exchanges": { 45 | "bastion_generic": { 46 | "queue_default": { 47 | "name": "queue_bastion", 48 | "config": { 49 | "durable": true, 50 | "autoDelete": false 51 | } 52 | }, 53 | "route_default": "default", 54 | "cfg": { 55 | "type": "direct", 56 | "durable": true, 57 | "autoDelete": false, 58 | "confirm" : true 59 | } 60 | }, 61 | "bastion_jobs": { 62 | "queue_default": { 63 | "name": "queue_jobs", 64 | "config": { 65 | "durable": true, 66 | "autoDelete": false 67 | } 68 | }, 69 | "route_default": "default", 70 | "cfg": { 71 | "type": "direct", 72 | "durable": true, 73 | "autoDelete": false, 74 | "confirm" : true 75 | } 76 | }, 77 | "bastion_ctl": { 78 | "queue_default": { 79 | "name": "queue_bastion_ctl", 80 | "config": { 81 | "durable": true, 82 | "autoDelete": true 83 | } 84 | }, 85 | "route_default": "default", 86 | "cfg": { 87 | "type": "fanout", 88 | "durable": false, 89 | "autoDelete": false, 90 | "confirm" : true 91 | } 92 | } 93 | }, 94 | "defaultExchange": "public_inbound" 95 | }, 96 | "k": {}, 97 | "jwtKey" : "", 98 | "modules" : { 99 | "auth": { 100 | "strategy" : "native" 101 | }, 102 | "cdn" : { 103 | "strategy" : "fs", 104 | "config" : { 105 | "data_dir" : "data" 106 | } 107 | } 108 | }, 109 | "pods": { 110 | "email" : { 111 | "mailer": { 112 | "host": "localhost" 113 | } 114 | }, 115 | "templater" : {}, 116 | "flow" : {}, 117 | "syndication" : {}, 118 | "http" : {}, 119 | "time" : {}, 120 | "math" : {}, 121 | "html" : {}, 122 | "crypto" : {} 123 | }, 124 | "transforms" : { 125 | "fetch" : true, 126 | "syncFrom" : "https://api.bip.io" 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /config/defs.js: -------------------------------------------------------------------------------- 1 | var defs = { 2 | 3 | CONTENTTYPE_RSS : 'application/rss+xml', 4 | CONTENTTYPE_TEXT : 'text/plain', 5 | CONTENTTYPE_XML : 'text/xml', 6 | CONTENTTYPE_HTML : 'text/html', 7 | CONTENTTYPE_JSON : 'application/json', 8 | CONTENTTYPE_BINARY : 'application/octet-stream', 9 | CONTENTTYPE_CSV : 'application/csv', 10 | CONTENTTYPE_TSV : 'application/tsv', 11 | 12 | RESPONSE_OK : 'OK', 13 | RESPONSE_INPROCESS : 'IN_PROCESS', 14 | RESPONSE_ERROR : 'ERROR', 15 | 16 | DEFAULT_CACHE_EXPIRE_SECS : 10*60, 17 | 18 | ERR_CONSTRAINT : 'errConstraint', 19 | 20 | SIG_RESTART : 'bounce', 21 | 22 | JOB_USER_NOTIFICATION : 'userNotify', 23 | 24 | JOB_USER_STAT : 'userStat', 25 | JOB_USER_STAT_IN_MB : 'userStatInMB', 26 | JOB_USER_STAT_OUT_MB : 'userStatOutMB', 27 | 28 | // JOB_ATTACH_REFERER_ICON : 'attachBipRefererIcon', 29 | 30 | JOB_BIP_TRIGGER : 'bipTrigger', 31 | 32 | JOB_BIP_ACTIVITY : 'bipActivity', 33 | 34 | JOB_BIP_SET_DEFAULTS : 'transformDefaults', 35 | 36 | JOB_SET_DEFAULT_SPACE : 'spaceDefault', 37 | 38 | JOB_HEAP_DUMP : 'heapDump' 39 | }; 40 | 41 | module.exports = defs; -------------------------------------------------------------------------------- /config/upstart_bip.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copy this file to /etc/init/bip.conf and fix paths where needed to point 3 | # to your local installation 4 | # 5 | # then to start and stop : 6 | # start bip 7 | # stop bip 8 | # 9 | description "bip Startup Monitor" 10 | author "michael pearson" 11 | 12 | start on startup 13 | stop on shutdown 14 | console log 15 | respawn 16 | 17 | env APP_PATH="/path/to/bipio/src" 18 | env LOGFILE="/path/to/bipio/logs/server.log" 19 | env PIDFILE="/var/run/bip.pid" 20 | env USER=bdeploy 21 | env NODE_ENV="production" 22 | 23 | script 24 | export HOME="/path/to/bipio/" 25 | 26 | echo $$ > $PIDFILE 27 | chdir $HOME 28 | exec sudo -u $USER NODE_ENV=development /usr/bin/node $APP_PATH/server.js >> $LOGFILE 2>&1 29 | end script 30 | 31 | pre-start script 32 | # Date format same as (new Date()).toISOString() for consistency 33 | echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Starting" >> $LOGFILE 34 | end script 35 | 36 | pre-stop script 37 | rm $PIDFILE 38 | echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Stopping" >> $LOGFILE 39 | end script 40 | -------------------------------------------------------------------------------- /migrations/0.2.44/index.js: -------------------------------------------------------------------------------- 1 | // migration for https://github.com/bipio-server/bipio/pull/14 2 | var fs = require('fs'), 3 | Migration = { 4 | run : function(app, configPath, next) { 5 | var config = JSON.parse(fs.readFileSync(configPath)), 6 | repl = new RegExp('^/'), 7 | rootPath = GLOBAL.SERVER_ROOT + '/../', 8 | delta = false; 9 | 10 | if (0 === config.cdn.indexOf('/') && fs.existsSync(rootPath + config.cdn)) { 11 | console.info('Re-writing CDN config path'); 12 | config.cdn = config.cdn.replace(repl, ''); 13 | delta = true; 14 | } 15 | 16 | if (0 === config.datadir.indexOf('/') && fs.existsSync(rootPath + config.datadir)) { 17 | console.info('Re-writing Data Directory config path'); 18 | config.datadir = config.datadir.replace(repl, ''); 19 | delta = true; 20 | } 21 | 22 | if (delta) { 23 | fs.writeFile(configPath , JSON.stringify(config, null, 2), function(err) { 24 | if (err) { 25 | next(err, 'error'); 26 | } else { 27 | console.info("\nConfig written to : " + configPath + "\n"); 28 | next(); 29 | } 30 | }); 31 | } else { 32 | next('Nothing To Do'); 33 | } 34 | } 35 | } 36 | 37 | module.exports = Migration; -------------------------------------------------------------------------------- /migrations/0.2.50/index.js: -------------------------------------------------------------------------------- 1 | // Adds JWT Signing key 2 | var fs = require('fs'), 3 | crypto = require('crypto'), 4 | Migration = { 5 | run : function(app, configPath, next) { 6 | var config = JSON.parse(fs.readFileSync(configPath)); 7 | 8 | crypto.randomBytes(48, function(ex, buf) { 9 | config.jwtKey = buf.toString('hex'); 10 | fs.writeFile(configPath , JSON.stringify(config, null, 2), function(err) { 11 | if (err) { 12 | next(err, 'error'); 13 | } else { 14 | console.info("\nConfig written to : " + configPath + "\n"); 15 | next(); 16 | } 17 | }); 18 | }); 19 | } 20 | } 21 | 22 | module.exports = Migration; -------------------------------------------------------------------------------- /migrations/0.2.79/index.js: -------------------------------------------------------------------------------- 1 | // migration for https://github.com/bipio-server/bipio/pull/14 2 | var fs = require('fs'), 3 | Migration = { 4 | run : function(app, configPath, next) { 5 | var config = JSON.parse(fs.readFileSync(configPath)), 6 | rootPath = GLOBAL.SERVER_ROOT + '/../', 7 | delta = false; 8 | 9 | if (!config.modules) { 10 | config.modules = {}; 11 | delta = true; 12 | } 13 | 14 | if (!config.modules.auth && config.auth) { 15 | config.modules.auth = {}; 16 | config.modules.auth.strategy = config.auth.type; 17 | config.modules.auth.config = app._.clone(config.auth.config); 18 | delete config.auth; 19 | delta = true; 20 | } 21 | 22 | if (delta) { 23 | fs.writeFileSync(configPath , JSON.stringify(config, null, 2)); 24 | console.info("\nConfig (for 0.2.79) syncronously written to : " + configPath + "\n"); 25 | next(); 26 | } else { 27 | next('Nothing To Do'); 28 | } 29 | } 30 | } 31 | 32 | module.exports = Migration; 33 | -------------------------------------------------------------------------------- /migrations/0.3.0/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | Q = require('q'), 4 | minTime = new Date().getTime() - (30 * 24 * 60 * 60 * 1000), 5 | syndicationMigration = Q.defer(), 6 | soundcloudMigration = Q.defer(), 7 | promises = [ syndicationMigration.promise, soundcloudMigration.promise ], 8 | 9 | Migration = { 10 | run : function(app, configPath, next) { 11 | 12 | var config = JSON.parse(fs.readFileSync(configPath)); 13 | // var config = jf.readFileSync(configPath); 14 | 15 | 16 | var rootPath = GLOBAL.SERVER_ROOT + '/../'; 17 | var delta = false; 18 | 19 | 20 | config.cdn_public = config.cdn_public.replace(/\/img\/cdn/, ''); 21 | 22 | if (!config.modules.hasOwnProperty("cdn")) { 23 | config.modules.cdn = { 24 | "strategy" : "fs", 25 | "config" : { 26 | "data_dir" : config.datadir 27 | } 28 | }; 29 | console.log("**NOTICE** `cdn_public` URL has changed, please update any of your site cdn symlinks to point to : `" + config.modules.cdn.config.data_dir + "`"); 30 | delta = true; 31 | } 32 | 33 | if (config.hasOwnProperty("datadir")) { 34 | delete config.datadir; 35 | delta = true; 36 | } 37 | if (config.hasOwnProperty("cdn")) { 38 | delete config.cdn; 39 | delta = true; 40 | } 41 | 42 | // Handle database migration 43 | 44 | if (config.pods.hasOwnProperty("syndication")) { 45 | 46 | app.dao.list('pod_syndication_track_subscribe', null, 100, 1, 'recent', { created: { $gt: minTime } }, function(err, oldModelName, results) { 47 | 48 | if (err) { 49 | syndicationMigration.reject(new Error(err)); 50 | } 51 | else if (results.num_pages === 0) { 52 | console.log("...Syndication Migration finished."); 53 | syndicationMigration.resolve(); 54 | } 55 | else { 56 | var syndPromises = []; 57 | console.log("Migrating Syndication subscription data from the last 30 days (if any)..."); 58 | 59 | for (var index=1; index<=results.num_pages; index++) { 60 | 61 | (function(pageNum, syndicationMigration, isLastPage) { 62 | app.dao.list('pod_syndication_track_subscribe', null, 100, pageNum, 'recent', { created: { $gt: minTime } }, function(err, oldModelName, result) { 63 | for (var i=0; i= results.length) { 26 | next('Updated ' + count + ' Shares'); 27 | } 28 | 29 | }); 30 | })(results[i], i); 31 | } 32 | } else { 33 | next('Nothing To Do'); 34 | } 35 | }); 36 | } 37 | } 38 | 39 | module.exports = Migration; -------------------------------------------------------------------------------- /migrations/0.4.16/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | Q = require('q'), 3 | Migration = { 4 | run : function(app, configPath, next) { 5 | var dao = app.dao, 6 | modelName = 'bip_share'; 7 | 8 | dao.findFilter( 9 | modelName, 10 | {}, 11 | function(err, results) { 12 | var UIDMap = {},r; 13 | 14 | if (err) { 15 | next(err); 16 | } else if (!results) { 17 | next('Nothing To Do'); 18 | } else { 19 | for (var i = 0; i < results.length; i++) { 20 | r = results[i]; 21 | 22 | if (!UIDMap[r.owner_id]) { 23 | UIDMap[r.owner_id] = []; 24 | } 25 | 26 | UIDMap[r.owner_id].push(r.id); 27 | } 28 | 29 | dao.findFilter( 30 | 'account', 31 | { 32 | 'id' : { 33 | $in : Object.keys(UIDMap) 34 | } 35 | }, 36 | function(err, results) { 37 | if (err) { 38 | next(err); 39 | } else if (!results) { 40 | next('Shares Do Not Map To Local Users') 41 | } else { 42 | var owner; 43 | for (var i = 0; i < results.length; i++) { 44 | owner = results[i]; 45 | dao.updateColumn( 46 | 'bip_share', 47 | { 48 | id : { 49 | '$in' : UIDMap[owner.id] 50 | } 51 | }, 52 | { 53 | user_name : owner.username 54 | }, 55 | function(err) { 56 | if (err) { 57 | next(err); 58 | } else { 59 | if (i >= results.length - 1) { 60 | next(); 61 | } 62 | } 63 | } 64 | ); 65 | } 66 | } 67 | } 68 | ); 69 | } 70 | } 71 | ); 72 | } 73 | } 74 | 75 | module.exports = Migration; -------------------------------------------------------------------------------- /migrations/0.4.19/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | Q = require('q'), 3 | Migration = { 4 | run : function(app, configPath, next) { 5 | var dao = app.dao; 6 | 7 | dao.updateProperties( 8 | 'bip_share', 9 | {}, 10 | { 11 | installs : 0 12 | }, 13 | function(err) { 14 | next(err || 'Done') 15 | } 16 | ); 17 | } 18 | } 19 | 20 | module.exports = Migration; -------------------------------------------------------------------------------- /migrations/0.4.4/index.js: -------------------------------------------------------------------------------- 1 | // ENT-444 2 | var fs = require('fs'), 3 | Q = require('q'), 4 | Migration = { 5 | run : function(app, configPath, next) { 6 | var dao = app.dao, 7 | modelName = 'account', 8 | adminDefer = Q.defer(); 9 | userDefer = Q.defer(), 10 | promises = [ adminDefer.promise, userDefer.promise ]; 11 | 12 | Q.all(promises).then( 13 | function() { 14 | next(); 15 | }, 16 | function(err) { 17 | next(err) 18 | } 19 | ); 20 | 21 | // set is_admin to user level 'admin' 22 | dao.updateColumn( 23 | modelName, 24 | { 25 | is_admin : true 26 | }, 27 | { 28 | account_level : 'admin' 29 | }, 30 | function(err, results) { 31 | console.log(arguments); 32 | if (err) { 33 | adminDefer.reject(err); 34 | } else { 35 | adminDefer.resolve(); 36 | } 37 | } 38 | ); 39 | 40 | // set !is_admin to user level 'user' 41 | dao.updateColumn( 42 | modelName, 43 | { 44 | is_admin : false 45 | }, 46 | { 47 | account_level : 'user' 48 | }, 49 | function(err, results) { 50 | console.log(arguments); 51 | if (err) { 52 | userDefer.reject(err); 53 | } else { 54 | userDefer.resolve(); 55 | } 56 | } 57 | ); 58 | } 59 | } 60 | 61 | module.exports = Migration; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Michael Pearson", 3 | "name": "bipio", 4 | "version": "0.5.0", 5 | "scripts": { 6 | "start": "node src/server.js >> logs/server.log &", 7 | "test": "make test", 8 | "install": "make install" 9 | }, 10 | "main": "src/server.js", 11 | "license": "Apache-2.0", 12 | "bin": { 13 | "bipio": "./src/server.js", 14 | "bipio-setup": "./tools/setup.js", 15 | "bipio-token-regen": "./tools/token_regen.js", 16 | "bipio-pod-install": "./tools/pod-install.js", 17 | "bipio-trigger": "./tools/bip-trigger.js", 18 | "bipio-expire": "./tools/bip-expire.js", 19 | "bipio-generate-hub-stats": "./tools/generate-hub-stats.js" 20 | }, 21 | "description": "API and graph resolver for the bipio content pipeline.", 22 | "homepage": "https://bip.io", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/bipio-server/bipio" 26 | }, 27 | "engines": { 28 | "node": ">=v0.10.2" 29 | }, 30 | "dependencies": { 31 | "JSONPath": "^0.10.0", 32 | "amqp": ">=0.1.3", 33 | "async": "0.1.18", 34 | "base-converter": "1.1.2", 35 | "bcrypt": ">=0.5.0", 36 | "bip-pod": ">=0.4.14", 37 | "bip-pod-crypto": ">=0.0.1", 38 | "bip-pod-email": ">=0.0.1", 39 | "bip-pod-flow": ">=0.0.1", 40 | "bip-pod-html": ">=0.0.1", 41 | "bip-pod-http": ">=0.0.1", 42 | "bip-pod-math": ">=0.0.1", 43 | "bip-pod-syndication": ">=0.4.5", 44 | "bip-pod-templater": ">=0.0.1", 45 | "bip-pod-time": ">=0.0.1", 46 | "body-parser": "~1.13.2", 47 | "clone": ">=0.1.0", 48 | "commander": ">=2.0.0", 49 | "config": "0.4.36", 50 | "connect": "2.30.2", 51 | "connect-mongo": "0.8.2", 52 | "cookie-parser": "^1.0.1", 53 | "cron": ">=1.0.1", 54 | "dateformat": "1.0.7-1.2.3", 55 | "datejs": ">=0.0.2", 56 | "errorhandler": "^1.1.1", 57 | "express": ">=4.0.0", 58 | "express-session": "^1.0.4", 59 | "extend": "^2.0.0", 60 | "favitest": "^1.0.3", 61 | "html-md": "^3.0.2", 62 | "htmlencode": ">=0.0.1", 63 | "htmlparser2": ">=3.4.0", 64 | "imagemagick": ">=0.1.3", 65 | "inquirer": ">=0.2.4", 66 | "ipaddr.js": ">=0.1.2", 67 | "json-middleware": "^1.0.2", 68 | "jsonwebtoken": ">=1.0.0", 69 | "ldapjs": "^0.7.1", 70 | "lodash": "^3.2.0", 71 | "marked": "^0.3.2", 72 | "method-override": "^1.0.0", 73 | "mime": "^1.2.11", 74 | "mkdirp": ">=0.3.5", 75 | "moment": "^2.8.4", 76 | "moment-timezone": ">=0.3.0", 77 | "mongoose": "4.2.3", 78 | "multer": "0.1.8", 79 | "node-fs": "^0.1.7", 80 | "node-uuid": "1.3.3", 81 | "passport": ">=0.1.15", 82 | "pkgcloud": ">=1.1.0", 83 | "posix-getopt": "1.1.0", 84 | "q": "^1.0.1", 85 | "request": ">=2.2.9", 86 | "rimraf": ">=2.2.6", 87 | "rrecur": ">=2.0.0", 88 | "sleep": ">=1.2.0", 89 | "sprintf": ">=0.1.1", 90 | "ssl-root-cas": "^1.1.7", 91 | "sugar": "^1.4.1", 92 | "sync-exec": "0.6.2", 93 | "temp": ">=0.5.0", 94 | "time": ">=0.9.2", 95 | "tldtools": ">=0.0.6", 96 | "underscore": ">=1.5.2", 97 | "validator": "1.5.1", 98 | "winston": "1.0.0" 99 | }, 100 | "devDependencies": { 101 | "should": "*", 102 | "mocha": "*", 103 | "dox": "*" 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/lib/rabbit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * AMQP Transport wrapper 24 | * 25 | */ 26 | var amqp = require('amqp'), 27 | app = GLOBAL.app; 28 | 29 | function Rabbit(cfg, next) { 30 | var self = this; 31 | this.exchanges = []; 32 | this.queues = []; 33 | this.cfg = cfg; 34 | var opt = { 35 | host: cfg.host, 36 | login: cfg.auth.username, 37 | password : cfg.auth.password, 38 | noDelay : true 39 | }; 40 | 41 | if ('' !== cfg.vhost) { 42 | opt.vhost = cfg.vhost; 43 | } 44 | 45 | this.amqpConn = amqp.createConnection(opt, 46 | { 47 | defaultExchangeName: cfg.defaultExchange 48 | } 49 | ); 50 | 51 | this.amqpConn.connectionStatus = null; 52 | 53 | // Setup our preconfigured exchanges, queues and routes 54 | this.amqpConn.on( 55 | 'ready', 56 | function() { 57 | self.amqpConn.connectionStatus = 'connected'; 58 | for (xName in cfg.exchanges) { 59 | self.exchanges[xName] = self.amqpConn.exchange( 60 | xName, 61 | cfg.exchanges[xName].cfg, 62 | function() { 63 | var exchange = this; 64 | app.logmessage('RABBIT:X:' + this.name + ':UP'); 65 | var xStruct = cfg.exchanges[this.name]; 66 | 67 | // create default queue for this exchange 68 | self.queues[xStruct.queue_default.name] = self.amqpConn.queue( 69 | xStruct.queue_default.name, 70 | xStruct.queue_default.config, 71 | function() { 72 | this.bind(exchange, xStruct.route_default); 73 | app.logmessage('RABBIT:Q:' + (undefined !== next ? 'PUBSUB' : 'PUB') + this.name + ':UP' ); 74 | if (next) { 75 | next(this.name); 76 | } 77 | }); 78 | }); 79 | } 80 | }); 81 | 82 | this.amqpConn.on('connect', function() { 83 | app.logmessage('RABBIT:Connected'); 84 | }); 85 | 86 | this.amqpConn.on('error', function(err) { 87 | self.amqpConn.connectionStatus = null; 88 | app.logmessage('RABBIT:' + err, 'error'); 89 | }); 90 | } 91 | 92 | Rabbit.prototype.produce = function(xName, route, payload, cb) { 93 | this.exchanges[xName].publish(route, JSON.stringify(payload), {}, cb); 94 | } 95 | 96 | Rabbit.prototype.producePublic = function(payload, cb) { 97 | this.produce('bastion_generic', 'default', payload, cb); 98 | } 99 | 100 | Rabbit.prototype.produceJob = function(payload, cb) { 101 | this.produce('bastion_jobs', 'default', payload, cb); 102 | } 103 | 104 | Rabbit.prototype.getQueueByName = function(queueName) { 105 | return this.queues[queueName]; 106 | } 107 | 108 | Rabbit.prototype.disconnect = function() { 109 | this.amqpConn.disconnect(); 110 | this.amqpConn.connectionStatus = null; 111 | app.logmessage('RABBIT:DISCONNECTED'); 112 | } 113 | 114 | module.exports = Rabbit; 115 | -------------------------------------------------------------------------------- /src/lib/step.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Tim Caswell 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | // Inspired by http://github.com/willconant/flow-js, but reimplemented and 26 | // modified to fit my taste and the node.JS error handling system. 27 | function Step() { 28 | var steps = Array.prototype.slice.call(arguments), 29 | pending, counter, results, lock; 30 | 31 | // Define the main callback that's given as `this` to the steps. 32 | function next() { 33 | counter = pending = 0; 34 | 35 | // Check if there are no steps left 36 | if (steps.length === 0) { 37 | // Throw uncaught errors 38 | if (arguments[0]) { 39 | throw arguments[0]; 40 | } 41 | return; 42 | } 43 | 44 | // Get the next step to execute 45 | var fn = steps.shift(); 46 | results = []; 47 | 48 | // Run the step in a try..catch block so exceptions don't get out of hand. 49 | try { 50 | lock = true; 51 | var result = fn.apply(next, arguments); 52 | } catch (e) { 53 | // Pass any exceptions on through the next callback 54 | next(e); 55 | } 56 | 57 | if (counter > 0 && pending == 0) { 58 | // If parallel() was called, and all parallel branches executed 59 | // synchronously, go on to the next step immediately. 60 | next.apply(null, results); 61 | } else if (result !== undefined) { 62 | // If a synchronous return is used, pass it to the callback 63 | next(undefined, result); 64 | } 65 | lock = false; 66 | } 67 | 68 | // Add a special callback generator `this.parallel()` that groups stuff. 69 | next.parallel = function () { 70 | var index = 1 + counter++; 71 | pending++; 72 | 73 | return function () { 74 | pending--; 75 | // Compress the error from any result to the first argument 76 | if (arguments[0]) { 77 | results[0] = arguments[0]; 78 | } 79 | // Send the other results as arguments 80 | results[index] = arguments[1]; 81 | if (!lock && pending === 0) { 82 | // When all parallel branches done, call the callback 83 | next.apply(null, results); 84 | } 85 | }; 86 | }; 87 | 88 | // Generates a callback generator for grouped results 89 | next.group = function () { 90 | var localCallback = next.parallel(); 91 | var counter = 0; 92 | var pending = 0; 93 | var result = []; 94 | var error = undefined; 95 | 96 | function check() { 97 | if (pending === 0) { 98 | // When group is done, call the callback 99 | localCallback(error, result); 100 | } 101 | } 102 | process.nextTick(check); // Ensures that check is called at least once 103 | 104 | // Generates a callback for the group 105 | return function () { 106 | var index = counter++; 107 | pending++; 108 | return function () { 109 | pending--; 110 | // Compress the error from any result to the first argument 111 | if (arguments[0]) { 112 | error = arguments[0]; 113 | } 114 | // Send the other results as arguments 115 | result[index] = arguments[1]; 116 | if (!lock) { check(); } 117 | }; 118 | }; 119 | }; 120 | 121 | // Start the engine an pass nothing to the first step. 122 | next(); 123 | } 124 | 125 | // Tack on leading and tailing steps for input and output and return 126 | // the whole thing as a function. Basically turns step calls into function 127 | // factories. 128 | Step.fn = function StepFn() { 129 | var steps = Array.prototype.slice.call(arguments); 130 | return function () { 131 | var args = Array.prototype.slice.call(arguments); 132 | 133 | // Insert a first step that primes the data stream 134 | var toRun = [function () { 135 | this.apply(null, args); 136 | }].concat(steps); 137 | 138 | // If the last arg is a function add it as a last step 139 | if (typeof args[args.length-1] === 'function') { 140 | toRun.push(args.pop()); 141 | } 142 | 143 | 144 | Step.apply(null, toRun); 145 | } 146 | } 147 | 148 | 149 | // Hook into commonJS module systems 150 | if (typeof module !== 'undefined' && "exports" in module) { 151 | module.exports = Step; 152 | } -------------------------------------------------------------------------------- /src/models/account.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | var BipModel = require('./prototype.js').BipModel; 21 | 22 | var Account = Object.create(BipModel); 23 | 24 | GLOBAL.DEFS.ACCOUNT_LEVEL = { 25 | USER : 'user', 26 | ADMIN : 'admin' 27 | } 28 | 29 | Account.id = ''; 30 | Account.name = ''; 31 | Account.username = ''; 32 | Account.email_account = ''; 33 | Account.account_level = GLOBAL.DEFS.ACCOUNT_LEVEL.USER; 34 | 35 | Account.compoundKeyConstraints = { 36 | username : 1, 37 | email_account : 1 38 | }; 39 | 40 | Account.entityName = 'account'; 41 | Account.entitySchema = { 42 | id: { 43 | type: String, 44 | renderable: true, 45 | writable: false 46 | }, 47 | 48 | username: { 49 | type: String, 50 | renderable: true, 51 | writable: false 52 | }, 53 | 54 | name: { 55 | type: String, 56 | renderable: true, 57 | writable: true 58 | }, 59 | 60 | email_account: { 61 | type: String, 62 | renderable: true, 63 | writable: false 64 | }, 65 | 66 | created : { 67 | type: Number, 68 | renderable: true, 69 | writable: false 70 | }, 71 | 72 | last_session : { 73 | type: Number, 74 | renderable: true, 75 | writable: false 76 | }, 77 | 78 | account_level : { 79 | type : String, 80 | renderable : true, 81 | writable : false, 82 | default : GLOBAL.DEFS.ACCOUNT_LEVEL.USER, 83 | validate : [ 84 | { 85 | validator : function(val, next) { 86 | next( 87 | -1 !== app._.values(GLOBAL.DEFS.ACCOUNT_LEVEL).indexOf(val) 88 | ); 89 | }, 90 | msg : 'Invalid Account Level' 91 | } 92 | ] 93 | } 94 | }; 95 | /* 96 | // lazy loader 97 | Account.collections = {}; 98 | 99 | Account._load = function(collection, next) { 100 | var self = this; 101 | 102 | if (!this.collections[collection]) { 103 | this.dao.findFilter( 104 | collection, 105 | { 106 | owner_id : this.account.id 107 | }, 108 | function(err, result) { 109 | if (err) { 110 | next(err); 111 | } else { 112 | self.collections[collection] = result; 113 | } 114 | } 115 | ); 116 | 117 | } else { 118 | next(false, this.collections[collection]); 119 | } 120 | }; 121 | 122 | Account.getSetting = function(name, next) { 123 | this._load('account_option', function(err, settings) { 124 | if (settings && settings.length) { 125 | next(err, settings[0][name]) 126 | 127 | } else { 128 | next(err); 129 | } 130 | }); 131 | }; 132 | 133 | Account.getSettings = function(next) { 134 | this._load('account_option', function(err, settings) { 135 | if (settings && settings.length) { 136 | next(err, settings[0]) 137 | 138 | } else { 139 | next(err); 140 | } 141 | }); 142 | }; 143 | 144 | Account.getName : function() { 145 | return this.name; 146 | }; 147 | 148 | Account.getUsername : function() { 149 | return this.username; 150 | }; 151 | 152 | Account.getDomains = function(next) { 153 | this._load('domain', next); 154 | }; 155 | 156 | Account.getChannels = function(next) { 157 | this._load('channel', next); 158 | }; 159 | 160 | Account.getTimezone = function(next) { 161 | return this.getSetting('timezone', next); 162 | }; 163 | 164 | // 165 | Account.getDefaultDomain = function(next) { 166 | var self = this; 167 | if (this.defaultDomain) { 168 | next(false, this.defaultDomain); 169 | } else { 170 | this.getDomains(function(err, domains) { 171 | var defaultDomain; 172 | if (err) { 173 | next(err); 174 | } else { 175 | for (var i = 0; i < domains.length; i++) { 176 | if ('vanity' === domains[i].type) { 177 | self.defaultDomain = domains[i]; 178 | } 179 | } 180 | } 181 | }); 182 | } 183 | }; 184 | 185 | // 186 | Account.getDefaultDomainStr = function(next) { 187 | this.getDefaultDomain( 188 | function(err, domain) { 189 | if (err) { 190 | next(err); 191 | } else { 192 | next(false, GLOBAL.CFG.proto_public + domain.name 193 | } 194 | } 195 | ); 196 | } 197 | */ 198 | 199 | module.exports.Account = Account; 200 | -------------------------------------------------------------------------------- /src/models/account_activity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * Rolling account activity 24 | * 25 | */ 26 | var AccountActivityModel = require('./prototype.js').BipModel, 27 | AccountActivity = Object.create(AccountActivityModel); 28 | 29 | AccountActivity.entityName = 'stats_account'; 30 | AccountActivity.entitySchema = { 31 | id: { // day 32 | type: String, 33 | renderable: true, 34 | writable: false 35 | }, 36 | users_new : { 37 | type: String, 38 | renderable: true, 39 | writable: true 40 | }, 41 | users_signin : { 42 | type: String, 43 | renderable: true, 44 | writable: true 45 | }, 46 | 47 | // by bip type 48 | new_bip_breakdown : { 49 | type : Object, 50 | renderable: true, 51 | writable: true 52 | }, 53 | new_bips_total: { 54 | type: Integer, 55 | renderable: true, 56 | writable: true 57 | }, 58 | 59 | // by channel action 60 | new_channel_breakdown : { 61 | type : Object, 62 | renderable: true, 63 | writable: true 64 | }, 65 | new_channels_total: { 66 | type: Integer, 67 | renderable: true, 68 | writable: true 69 | }, 70 | 71 | channels_unverified: { 72 | type: Integer, 73 | renderable: true, 74 | writable: true 75 | }, 76 | 77 | 78 | new_domains_total: { 79 | type: Integer, 80 | renderable: true, 81 | writable: true 82 | }, 83 | 84 | delivered_bip_inbound: { 85 | type: Integer, 86 | renderable: true, 87 | writable: true 88 | }, 89 | delivered_channel_outbound : { 90 | type: Array, 91 | renderable: true, 92 | writable: true 93 | }, 94 | 95 | traffic_inbound: { 96 | type: Integer, 97 | renderable: true, 98 | writable: true 99 | }, 100 | traffic_outbound: { 101 | type: Integer, 102 | renderable: true, 103 | writable: true 104 | }, 105 | 106 | demographic_users_male: { 107 | type: Integer, 108 | renderable: true, 109 | writable: true 110 | }, 111 | 112 | demographic_users_female: { 113 | type: Integer, 114 | renderable: true, 115 | writable: true 116 | }, 117 | 118 | demographic_users_unknown: { 119 | type: Integer, 120 | renderable: true, 121 | writable: true 122 | } 123 | }; 124 | 125 | module.exports.AccountActivity = AccountActivity; 126 | -------------------------------------------------------------------------------- /src/models/account_auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | * 20 | 21 | */ 22 | var bcrypt = require('bcrypt'), 23 | crypto = require('crypto'), 24 | BipModel = require('./prototype.js').BipModel; 25 | 26 | var AccountAuth = Object.create(BipModel); 27 | 28 | function strCryptSync(str) { 29 | return bcrypt.hashSync(str, bcrypt.genSaltSync(10)); 30 | } 31 | 32 | function strCryptCmpSync(taintedClear, localHash) { 33 | return bcrypt.compareSync(taintedClear, localHash); 34 | } 35 | 36 | function AESCrypt(value) { 37 | var key, keyVersion, 38 | iv = crypto.randomBytes(32).toString('hex').substr(0, 16); 39 | // get latest key 40 | for (keyVersion in CFG.k) { 41 | key = CFG.k[keyVersion]; 42 | } 43 | 44 | var cipher = crypto.createCipheriv('aes-256-cbc', key, iv), 45 | crypted = cipher.update(value, 'ascii', 'base64') + cipher.final('base64'); 46 | cryptEncoded = new Buffer(keyVersion + iv + crypted).toString('base64'); 47 | 48 | return cryptEncoded; 49 | } 50 | 51 | function AESDecrypt(cryptedStr, autoPadding) { 52 | var crypted = new Buffer(cryptedStr, 'base64').toString('utf-8'); 53 | var keyVersion = crypted.substr(0, 1), 54 | iv = crypted.substr(1, 16), 55 | key = CFG.k[keyVersion], 56 | cypher = crypted.substr(17); 57 | 58 | var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); 59 | 60 | if (!autoPadding) { 61 | autoPadding = false; 62 | } 63 | decipher.setAutoPadding(autoPadding); 64 | 65 | var decrypted = (decipher.update(cypher, 'base64', 'ascii') + decipher.final('ascii')); 66 | return decrypted; 67 | } 68 | 69 | function pwHash(pwValue) { 70 | var crypted; 71 | // tokens use AES 72 | if (this.type !== 'login_primary') { 73 | crypted = AESCrypt(pwValue); 74 | // or seeded crypt 75 | } else { 76 | crypted = strCryptSync(pwValue); 77 | } 78 | return crypted; 79 | } 80 | 81 | function cryptSave(value) { 82 | if (value) { 83 | var crypted = value; 84 | 85 | // passwords get 86 | if (this.type == 'login_primary' || this.type == 'login_sub') { 87 | crypted = strCryptSync(value); 88 | //app.logmessage('Trying to write login primary to account_auth [' + this.id + ']', 'error'); 89 | //throw new Error('Bad Type'); 90 | } else if (this.type !== 'token_invite') { 91 | crypted = AESCrypt(value); 92 | } 93 | 94 | return crypted; 95 | } else { 96 | return value; 97 | } 98 | } 99 | 100 | function _encStr(s, toUnicode) { 101 | var json = JSON.stringify(s); 102 | return toUnicode ? json : json.replace(/[\u007f-\uffff]/g, 103 | function(c) { 104 | return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); 105 | } 106 | ); 107 | } 108 | 109 | function cryptSaveObj(value) { 110 | if (value) { 111 | //var strVal = (new Buffer(JSON.stringify(value), 'utf-8' )).toString('ascii') 112 | //return cryptSave(JSON.stringify(strVal)); 113 | var strVal = _encStr(value, false) 114 | return cryptSave(JSON.stringify(strVal)); 115 | } else { 116 | return value; 117 | } 118 | } 119 | 120 | AccountAuth.id = ''; 121 | AccountAuth.username = ''; 122 | AccountAuth.owner_id = ''; 123 | // enum 'login_primary', 'login_sub', 'token', 'token_invite', 'oauth', 'oauth_app', 'api_token' 124 | AccountAuth.type = ''; 125 | AccountAuth.password = ''; 126 | 127 | AccountAuth.oauth_provider = ''; // pod/provider name, where type = 'oauth' 128 | AccountAuth.oauth_refresh = ''; // AES refresh token, where type = 'oauth' 129 | AccountAuth.oauth_profile = ''; // AES serialized profile, where type = 'oauth' 130 | 131 | AccountAuth.entityName = 'account_auth'; 132 | AccountAuth.entitySchema = { 133 | id: { 134 | type: String, 135 | index: true, 136 | renderable: true, 137 | writable: false 138 | }, 139 | type: { 140 | type: String, 141 | index: true, 142 | renderable: true, 143 | writable: false 144 | }, 145 | password: { 146 | type: String, 147 | renderable: false, 148 | writable: false, 149 | set : cryptSave 150 | }, 151 | username: { 152 | type: String, 153 | renderable: false, 154 | writable: false, 155 | set : cryptSave 156 | }, 157 | key: { 158 | type: String, 159 | renderable: false, 160 | writable: false, 161 | set : cryptSave 162 | }, 163 | owner_id : { 164 | type: String, 165 | index: true, 166 | renderable: true, 167 | writable: false 168 | }, 169 | auth_provider: { 170 | type: String, 171 | renderable: true, 172 | writable: false 173 | }, 174 | oauth_provider: { 175 | type: String, 176 | renderable: true, 177 | writable: false 178 | }, 179 | oauth_refresh: { 180 | type: String, 181 | renderable: true, 182 | writable: false, 183 | set : cryptSave 184 | }, 185 | oauth_token_expire : { 186 | type : Number, 187 | renderable : false, 188 | writable : false, 189 | set : function(value) { 190 | if (value) { 191 | return (new Date()).getTime() + (value * 1000) 192 | } else { 193 | return value; 194 | } 195 | } 196 | }, 197 | oauth_profile: { 198 | type: Object, 199 | renderable: true, 200 | writable: false, 201 | set : cryptSaveObj 202 | } 203 | }; 204 | 205 | AccountAuth.hash = function(value) { 206 | return pwHash(value); 207 | } 208 | 209 | AccountAuth.cmpPassword = function(passwordTainted) { 210 | var password = this.getPassword().replace(/^\s+|\s+$/g, ""); 211 | 212 | // compare hash 213 | /* disabled 214 | if (this.type == 'login_primary') { 215 | return bcrypt.compareSync(passwordTainted, password); 216 | */ 217 | // AES 218 | if (this.type == 'token') { 219 | return passwordTainted == password; 220 | } 221 | return false; 222 | }; 223 | 224 | // gets the password, if it's async then try to decrypt 225 | AccountAuth.getPassword = function() { 226 | // AES 227 | if (this.type == 'token') { 228 | return AESDecrypt(this.password).substr(0,32); 229 | } else { 230 | // return this.password; 231 | if (this.password) { 232 | return AESDecrypt(this.password, true); 233 | } else { 234 | return this.password; 235 | } 236 | } 237 | }; 238 | 239 | AccountAuth.getUsername = function() { 240 | if (this.username) { 241 | return AESDecrypt(this.username, true); 242 | } else { 243 | return; 244 | } 245 | }; 246 | 247 | AccountAuth.getKey = function() { 248 | if (this.key) { 249 | return AESDecrypt(this.key, true); 250 | } else { 251 | return; 252 | } 253 | }; 254 | 255 | AccountAuth.getOAuthRefresh = function() { 256 | if (this.oauth_refresh) { 257 | return AESDecrypt(this.oauth_refresh, true); 258 | } else { 259 | return null; 260 | } 261 | } 262 | 263 | // gets oauth profile, handles legacy (json string) 264 | // and new object translation 265 | AccountAuth.getOauthProfile = function() { 266 | var profile = AESDecrypt(this.oauth_profile, true); 267 | if (app.helper.isObject(profile)) { 268 | return profile; 269 | } else { 270 | try { 271 | return JSON.parse(profile); 272 | } catch (e) { 273 | return 'Profile Not Available'; 274 | } 275 | } 276 | } 277 | 278 | module.exports.AccountAuth = AccountAuth; -------------------------------------------------------------------------------- /src/models/account_log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * Rolling account activity 24 | * 25 | */ 26 | var AccountLogModel = require('./prototype.js').BipModel, 27 | AccountLog = Object.create(AccountLogModel); 28 | 29 | AccountLog.codes = { 30 | 31 | 'channel_verify_email' : 'Email Verification Sent', 32 | 'channel_verify_email_yes' : 'Email Verified', 33 | 'channel_verify_email_no' : 'Email Opted Out', 34 | 35 | 'channel_create' : 'Channel Created', 36 | 'channel_deliver' : 'Message Delivered to Channel', 37 | 38 | 'bip_create' : 'Bip Created', 39 | 'bip_recieve' : 'Message received by Bip', 40 | 'bip_expired' : 'Bip Expired (Paused)' 41 | } 42 | 43 | AccountLog.entityName = 'account_log'; 44 | AccountLog.entitySchema = { 45 | id: { // log 46 | type: String, 47 | renderable: true, 48 | writable: false 49 | }, 50 | owner_id : { 51 | type: String, 52 | renderable: false, 53 | writable: false 54 | }, 55 | code : { 56 | type: String, 57 | renderable: true, 58 | writable: false 59 | }, 60 | description : { 61 | type: String, 62 | renderable: true, 63 | writable: false 64 | }, 65 | content : { 66 | type: String, 67 | renderable: true, 68 | writable: false 69 | }, 70 | 'public' : { 71 | type: String, 72 | renderable: false, 73 | writable: false 74 | }, 75 | created : { 76 | type: Number, 77 | renderable: true, 78 | writable: false 79 | } 80 | }; 81 | 82 | function setDescription(code) { 83 | this.description = AccountLog.codes[code]; 84 | } 85 | 86 | Bip.entitySetters = { 87 | 'code' : setDescription 88 | }; 89 | 90 | module.exports.AccountLog = AccountLog; 91 | -------------------------------------------------------------------------------- /src/models/bip_log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * Bip Activity + Error Log 24 | * 25 | */ 26 | var BipLogModel = require('./prototype.js').BipModel, 27 | BipLog = Object.create(BipLogModel); 28 | 29 | BipLog.codes = { 30 | 'bip_create' : 'Created', 31 | 'bip_deleted_auto' : 'Expired (Deleted)', 32 | 'bip_deleted_manual' : 'Deleted', 33 | 'bip_recieve' : 'Message Received', 34 | 'bip_paused_auto' : 'Expired (Paused)', 35 | 'bip_paused_manual' : 'Manually Paused', 36 | 'bip_share' : 'Config Shared', 37 | 'bip_unshare' : 'Config Un-Shared', 38 | 'bip_invoke' : 'Invoked', 39 | 'bip_channnel_error' : 'Channel Error' 40 | } 41 | 42 | BipLog.entityName = 'bip_log'; 43 | BipLog.entitySchema = { 44 | id: { // log 45 | type: String, 46 | renderable: true, 47 | writable: false 48 | }, 49 | owner_id : { 50 | type: String, 51 | renderable: false, 52 | index: true, 53 | writable: false 54 | }, 55 | bip_id : { 56 | type: String, 57 | renderable: false, 58 | index: true, 59 | writable: false 60 | }, 61 | transaction_id : { 62 | type: String, 63 | renderable: true, 64 | writable: false 65 | }, 66 | code : { 67 | type: String, 68 | renderable: true, 69 | writable: false, 70 | set : function(code) { 71 | if (BipLog.codes[code]) { 72 | this.message = BipLog.codes[code]; 73 | } 74 | return code; 75 | } 76 | }, 77 | source : { // action source 78 | type: String, 79 | renderable: true, 80 | writable: false 81 | }, 82 | message : { 83 | type: String, 84 | renderable: true, 85 | writable: false 86 | }, 87 | data : { 88 | type: Object, 89 | renderable: false, 90 | writable: false 91 | }, 92 | created : { 93 | type: Number, 94 | renderable: true, 95 | writable: false 96 | } 97 | }; 98 | 99 | module.exports.BipLog = BipLog; 100 | -------------------------------------------------------------------------------- /src/models/bip_share.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * Shared Bip hubs 24 | * 25 | */ 26 | var BipShare = Object.create(require('./prototype.js').BipModel); 27 | 28 | function escapeDot(val) { 29 | return val.replace(/\./g, '\u0001'); 30 | } 31 | 32 | function unEscapeDot(val) { 33 | return val.replace(/\u0001/g, '.'); 34 | } 35 | 36 | BipShare.entityName = 'bip_share'; 37 | BipShare.entitySchema = { 38 | id: { // day 39 | type: String, 40 | renderable: true, 41 | writable: false 42 | }, 43 | bip_id : { 44 | type: String, 45 | renderable: false, 46 | writable: false 47 | }, 48 | 49 | type : { 50 | type: String, 51 | renderable: true, 52 | writable: true 53 | }, 54 | 55 | name : { 56 | type: String, 57 | renderable: true, 58 | writable: true 59 | }, 60 | 61 | slug : { 62 | type: String, 63 | renderable: true, 64 | writable: true 65 | }, 66 | 67 | note : { 68 | type: String, 69 | renderable: true, 70 | writable: true 71 | }, 72 | 73 | icon : { 74 | type: String, 75 | renderable: true, 76 | writable: true 77 | }, 78 | 79 | config : { 80 | type: Object, 81 | renderable: true, 82 | writable: true 83 | }, 84 | 85 | owner_id : { 86 | type: String, 87 | renderable: true, 88 | writable: false 89 | }, 90 | owner_name : { 91 | type: String, 92 | renderable: true, 93 | writable: false 94 | }, 95 | user_name : { 96 | type: String, 97 | renderable: true, 98 | writable: false 99 | }, 100 | manifest : { 101 | type: Array, 102 | renderable: true, 103 | writable: false 104 | }, 105 | manifest_hash : { 106 | type: String, 107 | renderable: true, 108 | writable: false 109 | }, 110 | hub : { 111 | type: Object, 112 | renderable: true, 113 | writable: false, 114 | set : function(hub) { 115 | var newSrc, newCid; 116 | 117 | // normalize 118 | for (var src in hub) { 119 | 120 | newSrc = escapeDot(src); 121 | hub[newSrc] = hub[src]; 122 | 123 | if (newSrc !== src) { 124 | delete hub[src]; 125 | } 126 | 127 | if ( hub.hasOwnProperty(newSrc) ) { 128 | for (var cid in hub[newSrc].transforms) { 129 | 130 | newCid = escapeDot(cid); 131 | hub[newSrc].transforms[newCid] = hub[newSrc].transforms[cid]; 132 | if (newCid !== cid) { 133 | delete hub[newSrc].transforms[cid]; 134 | } 135 | 136 | } 137 | } 138 | } 139 | 140 | // parse 141 | for (var src in hub) { 142 | if (hub.hasOwnProperty(src)) { 143 | for (var cid in hub[src].transforms) { 144 | if (hub[src].transforms.hasOwnProperty(cid)) { 145 | for (var k in hub[src].transforms[cid]) { 146 | hub[src].transforms[cid][k] = hub[src].transforms[cid][k].trim(); 147 | } 148 | } 149 | } 150 | } 151 | } 152 | 153 | return hub; 154 | }, 155 | customGetter : function(hub) { 156 | var newSrc, newCid; 157 | 158 | // normalize 159 | for (var src in hub) { 160 | 161 | newSrc = unEscapeDot(src); 162 | hub[newSrc] = hub[src]; 163 | 164 | if (newSrc !== src) { 165 | delete hub[src]; 166 | } 167 | 168 | if ( hub.hasOwnProperty(newSrc) ) { 169 | for (var cid in hub[newSrc].transforms) { 170 | 171 | newCid = unEscapeDot(cid); 172 | hub[newSrc].transforms[newCid] = hub[newSrc].transforms[cid]; 173 | if (newCid !== cid) { 174 | delete hub[newSrc].transforms[cid]; 175 | } 176 | 177 | } 178 | } 179 | } 180 | 181 | return hub; 182 | }, 183 | }, 184 | schedule: { 185 | type: Object, 186 | renderable: true, 187 | writable: true, 188 | default : {} 189 | }, 190 | exports : { // user timezone 191 | type : Object, 192 | renderable : true, 193 | writable : true, 194 | get : function(val) { 195 | return val ? JSON.parse(val) : val; 196 | }, 197 | set : function(val) { 198 | return val ? JSON.stringify(val) : val; 199 | } 200 | }, 201 | search : { 202 | type : String, 203 | renderable : false, 204 | writable : false, 205 | }, 206 | votes: { 207 | type: Number, 208 | renderable: true, 209 | writable: false, 210 | "default" : 0 211 | }, 212 | installs: { 213 | type: Number, 214 | renderable: true, 215 | writable: false, 216 | "default" : 0 217 | }, 218 | created : { 219 | type: Number, 220 | renderable: true, 221 | writable: false 222 | }, 223 | updated : { 224 | type: Number, 225 | renderable: true, 226 | writable: false 227 | } 228 | }; 229 | 230 | BipShare.links = function( accountInfo ) { 231 | var links = []; 232 | 233 | links.push({ 234 | name : 'oembed', 235 | title : 'oEmbed', 236 | description : 'Retrieves The oEmbed For This Share', 237 | contentType : DEFS.CONTENTTYPE_JSON, 238 | _href : this._dao.getBaseUrl() + '/rpc/oembed/?url=' + this.href() 239 | }); 240 | 241 | return links; 242 | } 243 | 244 | // 245 | BipShare.preSave = function(accountInfo, next) { 246 | // rebuild search terms 247 | var note = this.note || '', 248 | name = this.name || '', 249 | manifest = this.manifest ? this.manifest.join('" "') : '', 250 | searchStr = '', 251 | pod, 252 | tokens; 253 | 254 | searchStr += (note ? ('"' + note + '"') : '') + '"' + name + '" "' + manifest + '"'; 255 | 256 | if (this.manifest) { 257 | for (var i = 0; i < this.manifest.length; i++) { 258 | tokens = this.manifest[i].split('.'); 259 | pod = this._dao.pod(tokens[0]); 260 | if (pod) { 261 | searchStr += ' "' + pod.getAction(tokens[1]).title + '"'; 262 | } 263 | } 264 | } 265 | 266 | this.search = searchStr; 267 | 268 | next(false, this); 269 | }; 270 | 271 | BipShare.compoundKeyConstraints = { 272 | owner_id : 1, 273 | slug: 1 274 | }; 275 | 276 | module.exports.BipShare = BipShare; 277 | -------------------------------------------------------------------------------- /src/models/channel_log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * Channel Activity + Error Log 24 | * 25 | */ 26 | var ChannelLogModel = require('./prototype.js').BipModel, 27 | ChannelLog = Object.create(ChannelLogModel); 28 | 29 | ChannelLog.codes = { 30 | 'channel_create' : 'Created', 31 | 'channel_error' : 'Error' 32 | } 33 | 34 | ChannelLog.entityName = 'channel_log'; 35 | ChannelLog.entitySchema = { 36 | id: { // log 37 | type: String, 38 | renderable: true, 39 | writable: false 40 | }, 41 | owner_id : { 42 | type: String, 43 | renderable: false, 44 | index: true, 45 | writable: false 46 | }, 47 | channel_id : { 48 | type: String, 49 | renderable: false, 50 | index: true, 51 | writable: false 52 | }, 53 | transaction_id : { 54 | type: String, 55 | renderable: true, 56 | writable: false 57 | }, 58 | code : { 59 | type: String, 60 | renderable: true, 61 | writable: false, 62 | set : function(code) { 63 | if (ChannelLog.codes[code]) { 64 | this.message = ChannelLog.codes[code]; 65 | } 66 | return code; 67 | } 68 | }, 69 | bip_id : { // transaction source 70 | type: String, 71 | renderable: true, 72 | writable: false 73 | }, 74 | message : { 75 | type: String, 76 | renderable: true, 77 | writable: false 78 | }, 79 | created : { 80 | type: Number, 81 | renderable: true, 82 | writable: false 83 | } 84 | }; 85 | 86 | module.exports.ChannelLog = ChannelLog; 87 | -------------------------------------------------------------------------------- /src/models/domain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | var BipModel = require('./prototype.js').BipModel, 21 | Domain = Object.create(BipModel), 22 | dns = require('dns'), 23 | step = require('../lib/step'); 24 | 25 | Domain.uniqueKeys = ['name']; 26 | Domain.entityName = 'domain'; 27 | Domain.entitySchema = { 28 | id: { 29 | type: String, 30 | index : true, 31 | renderable: true, 32 | writable: false 33 | }, 34 | owner_id : { 35 | type: String, 36 | index : true, 37 | renderable: false, 38 | writable: false 39 | }, 40 | type: { 41 | type: String, 42 | renderable: false, 43 | writable: false, 44 | "default" : "custom" 45 | }, 46 | _available: { 47 | type: Boolean, 48 | renderable: true, 49 | writable: false, 50 | "default" : false 51 | }, 52 | name: { 53 | type: String, 54 | renderable: true, 55 | index : true, 56 | writable: true, 57 | validate : [ 58 | { 59 | validator : function(val, next) { 60 | if (process.env.NODE_ENV !== 'production') { 61 | next(true); 62 | return; 63 | } 64 | 65 | var ok = /[\w\d]+\.[a-zA-Z]{2,}$/.test(val); 66 | if (ok) { 67 | 68 | var isLocal = Domain.isLocal(val); 69 | if (this.type == 'custom') { 70 | ok = !isLocal; 71 | } else { 72 | // lock vanity domains as unwritable 73 | ok = (!isLocal && this.type != 'vanity'); 74 | } 75 | } 76 | next(ok); 77 | }, 78 | msg : "Can not overwrite a protected Domain" 79 | }, 80 | ] 81 | }, 82 | renderer : { 83 | type : Object, 84 | renderable : true, 85 | writable : true, 86 | validate : [ 87 | { 88 | validator : function(val, next) { 89 | self._dao.validateRPC( 90 | val, 91 | this.getAccountInfo(), 92 | function(err, ok) { 93 | next(!err && ok); 94 | } 95 | ); 96 | }, 97 | msg : 'Renderer RPC Not Found' 98 | } 99 | ] 100 | } 101 | }; 102 | 103 | // is a local domain 104 | Domain.isLocal = function(domain) { 105 | var local = GLOBAL.CFG.domain_public.split(':').shift().replace(/\./g, '\\.'), 106 | reg = new RegExp(local + '$'); 107 | return reg.test(domain); 108 | } 109 | 110 | /** 111 | * 112 | * 113 | */ 114 | Domain.setAvailable = function(available, next) { 115 | var self = this; 116 | self._available = available; 117 | 118 | if (self.id) { 119 | this._dao.updateColumn( 120 | 'domain', 121 | self.id, 122 | { 123 | '_available' : available 124 | }, 125 | function(err, result) { 126 | if (err) { 127 | console.log(err); 128 | } 129 | next(err, 'domain', self, 200); 130 | } 131 | ); 132 | } else { 133 | next(false, 'domain', self, 200); 134 | } 135 | }; 136 | 137 | Domain.verify = function(accountInfo, next) { 138 | var self = this, dao = this._dao; 139 | if (/.?localhost$/.test(self.name)) { 140 | self.setAvailable(true, next); 141 | return; 142 | } 143 | 144 | step( 145 | function domainVerify() { 146 | dns.resolveMx( 147 | self.name, 148 | this.parallel() 149 | ); 150 | 151 | dns.resolveCname( 152 | self.name, 153 | this.parallel() 154 | ); 155 | }, 156 | function collateResults(err, MXAddr, CNAMEAddr) { 157 | self._available = false; 158 | if (!MXAddr && !CNAMEAddr && null != err) { 159 | if (err.errno == 'ENOTFOUND' || err.errno == 'ENODATA') { 160 | next(err, 'domain', self, 202); 161 | } else { 162 | next(err, 'domain', self, 500); 163 | } 164 | 165 | } else { 166 | 167 | accountInfo.getDefaultDomain(function(err, domain) { 168 | 169 | var ok = false; 170 | 171 | if (MXAddr && MXAddr.length > 0) { 172 | for (var i = 0; i < MXAddr.length; i++) { 173 | if (MXAddr[i].exchange && MXAddr[i].exchange == domain.name) { 174 | ok = true; 175 | } 176 | } 177 | } 178 | 179 | if (CNAMEAddr && CNAMEAddr.length > 0) { 180 | if (app.helper.inArray(CNAMEAddr, domain.name)) { 181 | ok = true; 182 | } 183 | } 184 | 185 | if (ok) { 186 | self.setAvailable(true, next); 187 | } else { 188 | // accepted - domain exists but is not bound to local application 189 | next(err, 'domain', self, 202); 190 | } 191 | 192 | }); 193 | } 194 | 195 | } 196 | ); 197 | } 198 | 199 | // domains behave a little differently, they can have postponed availability 200 | // after creation as we verify the domain is properly configured 201 | Domain.postSave = function(accountInfo, next) { 202 | var self = this, dao = this._dao; 203 | if (!this.isLocal(this.name)) { 204 | this.verify(accountInfo, next); 205 | } else { 206 | next(false, 'domain', self, 200); 207 | } 208 | } 209 | 210 | Domain.repr = function(accountInfo, next) { 211 | return this.name; 212 | } 213 | 214 | Domain.entitySetters = { 215 | 'name' : function(name) { 216 | return name.toLowerCase(); 217 | 218 | } 219 | }; 220 | 221 | Domain.compoundKeyConstraints = { 222 | name : 1 223 | }; 224 | 225 | module.exports.Domain = Domain; 226 | -------------------------------------------------------------------------------- /src/models/migration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | var BipModel = require('./prototype.js').BipModel; 21 | var Migration = Object.create(BipModel); 22 | 23 | Migration.entityName = 'migration'; 24 | Migration.entitySchema = { 25 | id: { 26 | type: String, 27 | renderable: false, 28 | writable: false 29 | }, 30 | zone : { 31 | type : String, 32 | renderable : false, 33 | writable : false, 34 | "default" : "system" 35 | }, 36 | version : { 37 | type: String, 38 | renderable: false, 39 | writable: false 40 | }, 41 | versionInt : { 42 | type: Number, 43 | renderable: false, 44 | writable: false 45 | } 46 | }; 47 | 48 | module.exports.Migration = Migration; 49 | -------------------------------------------------------------------------------- /src/models/prototype.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * BipModel is our local representation of a persistent model 24 | * 25 | */ 26 | var clone = require('clone'), 27 | lodash = require('lodash'), 28 | helper = require('../lib/helper'); 29 | 30 | var BipModel = { 31 | entityIndex: 'id', 32 | entityExpiration: 60*5, 33 | entityCreated: 'created', 34 | 35 | entitySetters: {}, 36 | 37 | _accountInfo : null, 38 | 39 | compoundKeyConstraints: undefined, 40 | 41 | // list of unique keys 42 | uniqueKeys: [], 43 | 44 | helper : helper, 45 | _dao : undefined, 46 | 47 | serverInfo : undefined, 48 | 49 | bindServerMeta: function(serverInfo) { 50 | this.serverInfo = serverInfo; 51 | }, 52 | 53 | href: function() { 54 | return this._dao.getBaseUrl() + '/rest/' + this.entityName + '/' + this.getIdValue(); 55 | }, 56 | 57 | // static prototype constructor 58 | staticInit: function(dao) { 59 | 60 | this._dao = dao; 61 | 62 | this.staticChildInit(); 63 | }, 64 | 65 | // @todo create a proper inheritence chain 66 | staticChildInit : function() { 67 | 68 | }, 69 | 70 | // instance constructor 71 | init: function(accountInfo) { 72 | this._accountInfo = accountInfo; 73 | return this; 74 | }, 75 | 76 | renderablePropsArray : undefined, 77 | writablePropsArray : undefined, 78 | 79 | getDao: function() { 80 | return this._dao; 81 | }, 82 | 83 | getAccountInfo: function() { 84 | return this._accountInfo; 85 | }, 86 | 87 | getCompoundKeyConstraints: function() { 88 | return this.compoundKeyConstraints; 89 | }, 90 | 91 | repr: function(accountInfo) { 92 | return ''; 93 | }, 94 | 95 | links : function() { 96 | return []; 97 | }, 98 | 99 | setValue: function(key, value) { 100 | this.key = value; 101 | }, 102 | 103 | /** 104 | * populates this object with src 105 | * 106 | * trusts tainted sources 107 | */ 108 | populate: function(src, accountInfo) { 109 | // copy from source into this model, override 110 | lodash.assign(this, src); 111 | this.decorate(accountInfo); 112 | }, 113 | 114 | /* 115 | * adds attribute decorators 116 | */ 117 | decorate: function(accountInfo) { 118 | if (undefined != accountInfo && this.id) { 119 | this._repr = this.repr(accountInfo); 120 | this._links = this.links(accountInfo); 121 | } 122 | 123 | if (this.getEntityIndex() && this[this.getEntityIndex()]) { 124 | this._href = this.href(); 125 | } 126 | }, 127 | 128 | toObj : function() { 129 | 130 | var obj = { 131 | _repr : this._repr, 132 | _href : this._href, 133 | _links : this._links 134 | }, 135 | self = this; 136 | 137 | _.each(this.entitySchema, function(value, key) { 138 | obj[key] = lodash.cloneDeep(self[key]); 139 | }); 140 | 141 | return obj; 142 | }, 143 | 144 | toMongoModel: function(mongoModel) { 145 | var model = helper.copyProperties(this, mongoModel, true); 146 | var self = this; 147 | model.getAccountInfo = function() { 148 | return self.getAccountInfo(); 149 | } 150 | 151 | model.getDao = function() { 152 | return self.getDao(); 153 | } 154 | 155 | return model; 156 | }, 157 | 158 | // called after successfully saving the object 159 | postSave: function(accountInfo, cb) { 160 | cb(false, this.getEntityName(), this); 161 | }, 162 | 163 | // called prior to save 164 | preSave: function(accountInfo, next) { 165 | next(false, this); 166 | }, 167 | 168 | preRemove : function(id, accountInfo, cb) { 169 | cb(false, this.getEntityName(), this) 170 | }, 171 | 172 | prePatch : function(patch, accountInfo, cb) { 173 | cb(false, this.getEntityName(), patch); 174 | }, 175 | 176 | getIdValue: function() { 177 | return this.id; 178 | }, 179 | 180 | getValue: function(prop) { 181 | return (this.hasOwnProperty(prop)) ? this[prop] : undefined; 182 | }, 183 | 184 | setId: function(newId) { 185 | this.id = newId; 186 | }, 187 | 188 | getEntityName: function() { 189 | return this.entityName 190 | }, 191 | 192 | getEntityIndex: function() { 193 | return this.entityIndex; 194 | }, 195 | 196 | getEntityExpiration: function() { 197 | return this.entityExpiration; 198 | }, 199 | 200 | getEntityCreated: function() { 201 | return this.entityCreated; 202 | }, 203 | 204 | getEntitySchema: function() { 205 | return this.entitySchema; 206 | }, 207 | 208 | isReadable: function(attr) { 209 | var ok = false, 210 | schema = this.getEntitySchema(); 211 | 212 | switch (attr) { 213 | case '_repr' : 214 | case '_href' : 215 | case '_links' : 216 | case 'status' : 217 | case 'message' : 218 | case 'code' : 219 | case 'errors' : 220 | ok = true; 221 | break; 222 | 223 | default : 224 | ok = schema[attr] && schema[attr].renderable; 225 | } 226 | 227 | return ok; 228 | }, 229 | 230 | isWritable: function(attr) { 231 | var schema = this.getEntitySchema(); 232 | return schema[attr] && schema[attr].writable; 233 | }, 234 | 235 | 236 | getClass: function() { 237 | return this; 238 | }, 239 | 240 | getValidators : function(attr) { 241 | var validators, 242 | schema = this.getEntitySchema(); 243 | 244 | if (schema[attr] && schema[attr].validate) { 245 | validators = schema[attr].validate; 246 | } 247 | return validators; 248 | }, 249 | 250 | testProperty : function(prop) { 251 | return this.getEntitySchema().hasOwnProperty(prop); 252 | } 253 | } 254 | 255 | BipModel.validators = { 256 | 'notempty' : function(val, next) { 257 | next(undefined != val && val != '' && null != val); 258 | }, 259 | 'len_64' : function(val, next) { 260 | next(!val || val.length <= 64); 261 | }, 262 | 'len_32' : function(val, next) { 263 | next(!val || val.length <= 32); 264 | }, 265 | 'max_32' : function(val, next) { 266 | next(!val || val.length <= 32); 267 | }, 268 | 'max_64' : function(val, next) { 269 | next(!val || val.length <= 64); 270 | }, 271 | 'max_text' : function(val, next) { 272 | next(!val || val.length <= 1024); 273 | }, 274 | 'bool_int' : function(val, next) { 275 | next(val == 0 || val == 1); 276 | }, 277 | 'bool_any' : function(val, next) { 278 | var bools = [ 279 | 1, 280 | 0, 281 | '1', 282 | '0', 283 | true, 284 | false, 285 | 'true', 286 | 'false' 287 | ]; 288 | next(-1 !== bools.indexOf(val)); 289 | }, 290 | // 291 | 'accountModelDomain' : function(val, next) { 292 | var filter = { 293 | id : this.domain_id, 294 | owner_id : this.owner_id 295 | }; 296 | 297 | this.getDao().find('domain', filter, function(err, result) { 298 | next(!err); 299 | }); 300 | }, 301 | // validates email format and whether the domian looks to be valid 302 | 'email' : function(val, next) { 303 | var validFormat = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i.test(val); 304 | var domainTokens = tldtools.extract('mailto:' + val); 305 | next(validFormat && domainTokens.inspect.useful() ); 306 | } 307 | } 308 | 309 | module.exports.BipModel = BipModel; 310 | -------------------------------------------------------------------------------- /src/models/stats_account.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | var StatsAccountModel = require('./prototype.js').BipModel, 21 | StatsAccount = Object.create(StatsAccountModel); 22 | 23 | StatsAccount.entityName = 'stats_account'; 24 | StatsAccount.entitySchema = { 25 | id: { 26 | type: String, 27 | renderable: false, 28 | writable: false 29 | }, 30 | day: { // day 31 | type: String, 32 | renderable: false, 33 | writable: false 34 | }, 35 | owner_id : { 36 | type: String, 37 | renderable: false, 38 | writable: false 39 | }, 40 | bips_total: { 41 | type: Number, 42 | renderable: true, 43 | writable: true, 44 | "default" : 0 45 | }, 46 | share_total: { 47 | type: Number, 48 | renderable: true, 49 | writable: true, 50 | "default" : 0 51 | }, 52 | channels_total: { 53 | type: Number, 54 | renderable: true, 55 | writable: true, 56 | "default" : 0 57 | }, 58 | delivered_bip_inbound: { 59 | type: Number, 60 | renderable: true, 61 | writable: true, 62 | "default" : 0 63 | }, 64 | delivered_channel_outbound : { 65 | type: Number, 66 | renderable: true, 67 | writable: true, 68 | "default" : 0 69 | }, 70 | traffic_inbound_mb: { 71 | type: Number, 72 | renderable: true, 73 | writable: true, 74 | "default" : 0 75 | }, 76 | traffic_outbound_mb: { 77 | type: Number, 78 | renderable: true, 79 | writable: true, 80 | "default" : 0 81 | } 82 | }; 83 | 84 | StatsAccount.compoundKeyConstraints = { 85 | owner_id : 1, 86 | day : 1, 87 | id : 1 88 | }; 89 | 90 | module.exports.StatsAccount = StatsAccount; 91 | -------------------------------------------------------------------------------- /src/models/stats_account_network.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | var StatsAccountNetworkNetworkModel = require('./prototype.js').BipModel, 21 | StatsAccountNetwork = Object.create(StatsAccountNetworkNetworkModel); 22 | 23 | StatsAccountNetwork.entityName = 'stats_account_network'; 24 | StatsAccountNetwork.entitySchema = { 25 | id: { 26 | type: String, 27 | renderable: false, 28 | writable: false 29 | }, 30 | day: { // day 31 | type: Number, 32 | renderable: true, 33 | writable: false 34 | }, 35 | owner_id : { 36 | type: String, 37 | renderable: false, 38 | writable: false 39 | }, 40 | data: { 41 | type: Object, 42 | renderable: true, 43 | writable: false 44 | }, 45 | updated_at: { 46 | type: Number, 47 | renderable: false, 48 | writable: false 49 | } 50 | }; 51 | 52 | StatsAccountNetwork.compoundKeyConstraints = { 53 | owner_id : 1, 54 | day : 1 55 | }; 56 | 57 | module.exports.StatsAccountNetwork = StatsAccountNetwork; 58 | -------------------------------------------------------------------------------- /src/models/stats_global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | * 20 | 21 | */ 22 | /** 23 | * 24 | * Provides general system statistics, by day 25 | * 26 | */ 27 | var StatsGlobalModel = require('./prototype.js').BipModel, 28 | StatsGlobal = Object.create(StatsGlobalModel); 29 | 30 | StatsGlobal.entityName = 'stats_account'; 31 | StatsGlobal.entitySchema = { 32 | id: { // day 33 | type: String, 34 | renderable: true, 35 | writable: false 36 | }, 37 | users_new : { 38 | type: String, 39 | renderable: true, 40 | writable: true 41 | }, 42 | users_signin : { 43 | type: String, 44 | renderable: true, 45 | writable: true 46 | }, 47 | 48 | // by bip type 49 | new_bip_breakdown : { 50 | type : Object, 51 | renderable: true, 52 | writable: true 53 | }, 54 | new_bips_total: { 55 | type: Integer, 56 | renderable: true, 57 | writable: true 58 | }, 59 | 60 | // by channel action 61 | new_channel_breakdown : { 62 | type : Object, 63 | renderable: true, 64 | writable: true 65 | }, 66 | new_channels_total: { 67 | type: Integer, 68 | renderable: true, 69 | writable: true 70 | }, 71 | 72 | channels_unverified: { 73 | type: Integer, 74 | renderable: true, 75 | writable: true 76 | }, 77 | 78 | 79 | new_domains_total: { 80 | type: Integer, 81 | renderable: true, 82 | writable: true 83 | }, 84 | 85 | delivered_bip_inbound: { 86 | type: Integer, 87 | renderable: true, 88 | writable: true 89 | }, 90 | delivered_channel_outbound : { 91 | type: Array, 92 | renderable: true, 93 | writable: true 94 | }, 95 | 96 | traffic_inbound: { 97 | type: Integer, 98 | renderable: true, 99 | writable: true 100 | }, 101 | traffic_outbound: { 102 | type: Integer, 103 | renderable: true, 104 | writable: true 105 | }, 106 | 107 | demographic_users_male: { 108 | type: Integer, 109 | renderable: true, 110 | writable: true 111 | }, 112 | 113 | demographic_users_female: { 114 | type: Integer, 115 | renderable: true, 116 | writable: true 117 | }, 118 | 119 | demographic_users_unknown: { 120 | type: Integer, 121 | renderable: true, 122 | writable: true 123 | } 124 | }; 125 | 126 | module.exports.StatsGlobal = StatsGlobal; -------------------------------------------------------------------------------- /src/models/transform_default.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * The Bipio API Server 4 | * 5 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | /** 22 | * 23 | * TranslateDefault is responsible for surfacing transforms between two channels 24 | * based on the last saved transform for the channels by the client, or otherwise 25 | * from the system. 26 | * 27 | */ 28 | var BipModel = require('./prototype.js').BipModel, 29 | TransformDefault = Object.create(BipModel); 30 | 31 | TransformDefault.compoundKeyConstraints = { 32 | owner_id : 1, 33 | from_channel : 1, 34 | to_channel : 1 35 | }; 36 | TransformDefault.entityName = 'transform_default'; 37 | TransformDefault.entitySchema = { 38 | id: { 39 | type: String, 40 | index: true, 41 | renderable: false, 42 | writable: false 43 | }, 44 | owner_id : { // owner_id 'system' = system determined map 45 | type: String, 46 | index: true, 47 | renderable: false, 48 | writable: false 49 | }, 50 | from_channel: { // channel action (pod.action) 51 | type: String, 52 | index: true, 53 | renderable: true, 54 | writable: false 55 | }, 56 | to_channel: { // channel action (pod.action) 57 | type: String, 58 | index: true, 59 | renderable: true, 60 | writable: false 61 | }, 62 | transform : { // serialized transform 63 | type: Object, 64 | renderable: true, 65 | writable: false 66 | }, 67 | created : { 68 | type: Number, 69 | renderable: true, 70 | writable: false 71 | } 72 | }; 73 | 74 | module.exports.TransformDefault = TransformDefault; 75 | -------------------------------------------------------------------------------- /src/modules/auth/http_basic.js: -------------------------------------------------------------------------------- 1 | 2 | var proto = require('./native'); 3 | 4 | function HTTPBasic(options) { 5 | this.options = options; 6 | } 7 | 8 | HTTPBasic.prototype = proto.prototype; 9 | 10 | /* 11 | * Remote HTTP Basic Auth 12 | */ 13 | HTTPBasic.prototype.test = function(username, password, opts, next) { 14 | var self = this, 15 | dao = this.dao, 16 | filter = {}, 17 | options = this.options; 18 | 19 | if ('admin' === username || opts.asOwner) { 20 | this.__proto__._test.apply(this, arguments); 21 | 22 | } else if (!username || !password) { 23 | next(self.MSG_NOT_AUTHORIZED, null); 24 | 25 | } else { 26 | 27 | this.__proto__._test.call(this, username, password, opts, function(err, result) { 28 | if (err) { 29 | // try to auth and sync off ldap auth service 30 | request.get( 31 | { 32 | "url" : options.url, 33 | "auth" : { 34 | "user" : username, 35 | "pass" : password, 36 | "sendImmediately" : true 37 | } 38 | }, 39 | function(err, res, body) { 40 | if (err) { 41 | next(err); 42 | } else if (200 !== res.statusCode) { 43 | next('Not Authorized'); 44 | } else { 45 | dao.find( 46 | 'account', 47 | { 48 | username : username 49 | }, 50 | function(err, acctResult) { 51 | if (!err && (null != acctResult)) { 52 | 53 | var filter = { 54 | 'owner_id' : acctResult.id, 55 | 'type' : 'token' 56 | } 57 | 58 | dao.find('account_auth', filter, function(isErr, authResult) { 59 | var resultModel = null; 60 | if (!isErr && null != authResult) { 61 | 62 | self.acctBind(acctResult, authResult, options, function(err, accountInfo) { 63 | if (err) { 64 | next(err); 65 | } else { 66 | try { 67 | accountInfo._remoteBody = JSON.parse(body); 68 | } catch (e) { 69 | accountInfo._remoteBody = body; 70 | } 71 | next(false, accountInfo); 72 | } 73 | }); 74 | 75 | } else { 76 | next(self.MSG_NOT_AUTHORIZED, resultModel); 77 | } 78 | }); 79 | 80 | // if user auths off and option set, auto create 81 | // local account 82 | } else if (!acctResult && options.auto_sync && options.auto_sync.mail_field) { 83 | var emailAddress; 84 | 85 | // if no email address found, create a local dummy 86 | if ('none' === options.auto_sync.mail_field) { 87 | emailAddress = 'noreply@' + username + '.' + CFG.domain; 88 | } else { 89 | emailAddress = app.helper.jsonPath(body, options.auto_sync.mail_field); 90 | } 91 | 92 | if (emailAddress) { 93 | 94 | dao.createUser(username, emailAddress, null, function(err, authResult) { 95 | 96 | authResult.username = username; 97 | authResult.name = username 98 | authResult.account_level = GLOBAL.DEFS.ACCOUNT_LEVEL.USER; 99 | 100 | self.acctBind(authResult, authResult, options, function(err, accountInfo) { 101 | if (err) { 102 | next(err); 103 | 104 | } else { 105 | try { 106 | accountInfo._remoteBody = JSON.parse(body); 107 | } catch (e) { 108 | accountInfo._remoteBody = body; 109 | } 110 | next(false, accountInfo); 111 | } 112 | }); 113 | 114 | }); 115 | 116 | } else { 117 | next(self.MSG_NOT_AUTHORIZED, null); 118 | } 119 | } else { 120 | app.logmessage('No Email field found to sync for ' + username + ', skipping auth', 'error'); 121 | next(self.MSG_NOT_AUTHORIZED, null); 122 | } 123 | } 124 | ); 125 | } 126 | } 127 | ); 128 | } else { 129 | next(err, result); 130 | } 131 | }); 132 | 133 | } 134 | } 135 | 136 | module.exports = HTTPBasic; -------------------------------------------------------------------------------- /src/modules/auth/ldap.js: -------------------------------------------------------------------------------- 1 | 2 | var proto = require('./native'), 3 | ldap = require('ldapjs'); 4 | 5 | function LDAP(options) { 6 | this.options = options; 7 | 8 | this._ldapClient = ldap.createClient(options.server); 9 | } 10 | 11 | LDAP.prototype = proto.prototype; 12 | 13 | /* 14 | * Remote HTTP Basic Auth 15 | */ 16 | LDAP.prototype.test = function(username, password, opts, next) { 17 | var self = this, 18 | dao = this.dao, 19 | filter = {}, 20 | options = this.options; 21 | 22 | if ('admin' === username || opts.asOwner) { 23 | this.__proto__._test.apply(this, arguments); 24 | 25 | } else if (!username || !password) { 26 | next(self.MSG_NOT_AUTHORIZED, null); 27 | 28 | } else { 29 | 30 | var client = this._ldapClient, 31 | base = options.base, 32 | search = app._.clone(options.search), 33 | bind = true; 34 | 35 | if (search.filter) { 36 | search.filter = search.filter.replace(/{{username}}/, username); 37 | } 38 | 39 | client.search(base, search, function(err, res) { 40 | if (err) { 41 | next(err); 42 | } else { 43 | 44 | var foundMatch = false, 45 | notFoundMsg = 'Not Found'; 46 | 47 | res.on('end', function() { 48 | if (!foundMatch) { 49 | next(notFoundMsg, null); 50 | } 51 | }); 52 | 53 | res.on('searchEntry', function(entry) { 54 | foundMatch = true; 55 | 56 | client.compare(entry.dn, 'userPassword', password, function(err, pass ) { 57 | 58 | if (err) { 59 | next(err); 60 | 61 | } else if (!pass) { 62 | next('Not Authorized') 63 | 64 | } else { 65 | 66 | // auth continue 67 | dao.find( 68 | 'account', 69 | { 70 | username : username 71 | }, 72 | function(err, acctResult) { 73 | if (!err && (null != acctResult)) { 74 | 75 | var filter = { 76 | 'owner_id' : acctResult.id, 77 | 'type' : 'token' 78 | } 79 | 80 | dao.find('account_auth', filter, function(isErr, authResult) { 81 | var resultModel = null; 82 | if (!isErr && null != authResult) { 83 | 84 | self.acctBind(acctResult, authResult, options, function(err, accountInfo) { 85 | next(false, accountInfo); 86 | }); 87 | 88 | } else { 89 | next(self.MSG_NOT_AUTHORIZED, resultModel); 90 | } 91 | }); 92 | 93 | // if user auths off and option set, auto create 94 | // local account 95 | } else if (!acctResult && options.auto_sync && options.auto_sync.mail_field) { 96 | var emailAddress; 97 | 98 | // if no email address found, create a local dummy 99 | if ('none' === options.auto_sync.mail_field) { 100 | emailAddress = 'noreply@' + username + '.' + CFG.domain; 101 | } else { 102 | for (var i = 0; i < entry.attributes.length; i++) { 103 | if (options.auto_sync.mail_field === entry.attributes[i].type) { 104 | emailAddress = entry.attributes[i].vals.pop(); 105 | break; 106 | } 107 | } 108 | } 109 | 110 | if (emailAddress) { 111 | 112 | dao.createUser(username, emailAddress, null, function(err, authResult) { 113 | authResult.username = username; 114 | authResult.name = username 115 | authResult.account_level = GLOBAL.DEFS.ACCOUNT_LEVEL.USER; 116 | 117 | self.acctBind(authResult, authResult, options, function(err, accountInfo) { 118 | next(false, accountInfo); 119 | }); 120 | 121 | }); 122 | 123 | } else { 124 | next(self.MSG_NOT_AUTHORIZED, null); 125 | } 126 | } else { 127 | app.logmessage('No Email field found to sync for ' + username + ', skipping auth', 'error'); 128 | next(self.MSG_NOT_AUTHORIZED, null); 129 | } 130 | } 131 | ); 132 | } 133 | }); 134 | }); 135 | } 136 | }); 137 | } 138 | } 139 | 140 | module.exports = LDAP; -------------------------------------------------------------------------------- /src/modules/auth/models/account_info.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | */ 5 | function AccountInfo(account, dao) { 6 | this.user = account; 7 | this.dao = dao; 8 | 9 | this.collections = {}; 10 | this.activeDomain = null; 11 | } 12 | 13 | AccountInfo.prototype = { 14 | 15 | _load : function(collection, next) { 16 | var self = this; 17 | 18 | if (!this.collections[collection]) { 19 | this.dao.findFilter( 20 | collection, 21 | { 22 | owner_id : this.user.id 23 | }, 24 | function(err, result) { 25 | self.collections[collection] = result; 26 | if (next) { 27 | next(err, result); 28 | } 29 | 30 | return self.collections[collection]; 31 | } 32 | ); 33 | } else { 34 | 35 | if (next) { 36 | next(false, this.collections[collection]); 37 | } 38 | 39 | return this.collections[collection]; 40 | } 41 | }, 42 | 43 | getSettings : function(next) { 44 | var self = this; 45 | return this._load('account_option', function(err, settings) { 46 | 47 | if (app.helper.isArray(settings)) { 48 | self.settings = settings[0]; 49 | 50 | } else { 51 | self.settings = settings; 52 | } 53 | 54 | next(err, self.settings.toJSON ? self.settings.toJSON() : self.settings); 55 | }); 56 | }, 57 | 58 | getSetting : function(name, next) { 59 | this.getSettings(function(err, settings) { 60 | next(err, settings[name]); 61 | }); 62 | }, 63 | 64 | getDomains : function(next) { 65 | return this._load('domain', next); 66 | }, 67 | 68 | getDomain : function(domainId, next) { 69 | this.getDomains(function(err, domains) { 70 | next(err, _.where(domains, { id : domainId }) ); 71 | }); 72 | }, 73 | 74 | testDomain : function(domainId, next) { 75 | this.getDomains(function(err, domains) { 76 | next(err, !!_.where(domains, { id : domainId}).length ); 77 | }); 78 | }, 79 | 80 | getChannels : function(next) { 81 | this._load('channel', next); 82 | }, 83 | 84 | getChannel : function(cid, next) { 85 | this.getChannels(function(err, channels) { 86 | next(err, _.findWhere(channels, { id : cid}) ); 87 | }); 88 | }, 89 | 90 | testChannel : function(cid, next) { 91 | this.getChannels(function(err, channels) { 92 | next(err, !!_.where(channels, { id : cid}).length ); 93 | }); 94 | }, 95 | 96 | getId : function() { 97 | return this.user.id; 98 | }, 99 | 100 | getName : function() { 101 | return this.user.name; 102 | }, 103 | 104 | getUserName : function() { 105 | return this.user.username; 106 | }, 107 | 108 | getActiveDomain : function() { 109 | return this.activeDomain; 110 | }, 111 | 112 | getDefaultDomain: function(next) { 113 | this.getDomains( 114 | function(err, domains) { 115 | next(err, _.findWhere(domains, { type : 'vanity'} ) ); 116 | } 117 | ); 118 | }, 119 | 120 | getDefaultDomainStr : function(next) { 121 | this.getDefaultDomain(function(err, domain) { 122 | next(err, CFG.proto_public + domain.name) 123 | }); 124 | }, 125 | 126 | setActiveDomain : function(domainId, next) { 127 | var self = this; 128 | if (domainId) { 129 | this.getDomains(function(err, domains) { 130 | self.activeDomain = _.findWhere(domains, { id : domainId } ); 131 | next(); 132 | }); 133 | } else { 134 | this.getDefaultDomain(function(err, domain) { 135 | self.activeDomain = domain; 136 | next(); 137 | }); 138 | } 139 | }, 140 | 141 | getTimezone : function(next) { 142 | this.getSetting('timezone', next); 143 | } 144 | }; 145 | 146 | module.exports = AccountInfo; -------------------------------------------------------------------------------- /src/modules/auth/native.js: -------------------------------------------------------------------------------- 1 | var AccountInfo = require('./models/account_info.js'); 2 | 3 | const MSG_NOT_AUTHORIZED = 'Not Authorized'; 4 | 5 | /* 6 | * Account Authorization Module prototype 7 | * 8 | */ 9 | function AuthModule(options) { 10 | this.options = options; 11 | } 12 | 13 | AuthModule.prototype = { 14 | MSG_NOT_AUTHORIZED : MSG_NOT_AUTHORIZED, 15 | setDAO : function(dao) { 16 | this.dao = dao; 17 | } 18 | }; 19 | 20 | AuthModule.prototype.getAccountStruct = function(authModel, next) { 21 | // session usable abstract model of the account 22 | var account = new AccountInfo({ 23 | id : authModel.owner_id, 24 | name : authModel.name, 25 | username : authModel.username, 26 | account_level: authModel.account_level, 27 | plan_until : authModel.plan_until, 28 | settings: { 29 | api_token: null 30 | } 31 | }, this.dao); 32 | 33 | // always load domain records for the account 34 | account.getDomains(function() { 35 | next(false, account); 36 | }); 37 | 38 | } 39 | 40 | AuthModule.prototype.accountFactory = function(props) { 41 | return new AccountInfo(props); 42 | } 43 | 44 | /** 45 | * 46 | * Creates an AccountStruct from a username 47 | */ 48 | AuthModule.prototype.getAccountStructByUsername = function(username, next) { 49 | var self = this; 50 | 51 | this.dao.find('account', { username : username }, function(err, result) { 52 | if (err || !result) { 53 | next(err ? err : "Not Found"); 54 | } else { 55 | result.owner_id = result.id; 56 | self.getAccountStruct(result, next); 57 | } 58 | }); 59 | } 60 | 61 | AuthModule.prototype.getAccountStructById = function(id, next) { 62 | var self = this; 63 | 64 | this.dao.find('account', { id : id }, function(err, result) { 65 | if (err || !result) { 66 | next(err ? err : "Not Found"); 67 | } else { 68 | result.owner_id = result.id; 69 | self.getAccountStruct(result, next); 70 | } 71 | }); 72 | } 73 | 74 | AuthModule.prototype.acctBind = function(account, accountAuth, options, next) { 75 | var masquerade = options.masquerade, 76 | activeDomainId = options.domainId, 77 | authModel = this.dao.modelFactory('account_auth', accountAuth); 78 | 79 | if (masquerade && 'admin' === account.account_level) { 80 | this.getAccountStructByUsername(masquerade, next); 81 | 82 | } else { 83 | accountAuth.username = account.username; 84 | accountAuth.name = account.name; 85 | 86 | // this should be injectable by the permissions module somehow 87 | accountAuth.account_level = account.account_level; 88 | accountAuth.plan_until = account.get('plan_until'); 89 | 90 | this.getAccountStruct(accountAuth, function(err, accountInfo) { 91 | 92 | accountInfo.user.username = account.username; 93 | 94 | accountInfo.setActiveDomain(activeDomainId, function() { 95 | next(false, accountInfo); 96 | }); 97 | 98 | }); 99 | } 100 | } 101 | 102 | // --------- PUBLIC INTERFACES 103 | 104 | /** 105 | * 106 | * Tests authorization 107 | * 108 | * @param string username 109 | * @param string password 110 | * @param string type () 111 | */ 112 | AuthModule.prototype._test = function(username, password, options, next) { 113 | var dao = this.dao, 114 | self = this, 115 | filter = {}; 116 | 117 | if (options.asOwner) { 118 | filter.id = username; 119 | } else { 120 | filter.username = username; 121 | } 122 | 123 | dao.find( 124 | 'account', 125 | filter, 126 | function(err, acctResult) { 127 | if (!err && (null != acctResult)) { 128 | var filter = { 129 | 'owner_id' : acctResult.id, 130 | 'type' : 'token' 131 | } 132 | 133 | dao.find('account_auth', filter, function(isErr, authResult) { 134 | 135 | var resultModel = null; 136 | if (!isErr && null != authResult) { 137 | var authModel = dao.modelFactory('account_auth', authResult); 138 | if (options.acctBind || authModel.cmpPassword(password)) { 139 | self.acctBind(acctResult, authResult, options, next); 140 | 141 | } else { 142 | next(MSG_NOT_AUTHORIZED); 143 | } 144 | } else { 145 | next(MSG_NOT_AUTHORIZED); 146 | } 147 | }); 148 | } else { 149 | next(MSG_NOT_AUTHORIZED); 150 | } 151 | } 152 | ); 153 | } 154 | 155 | AuthModule.prototype.test = function(username, password, options, next) { 156 | this.__proto__._test.apply(this, arguments); 157 | } 158 | 159 | 160 | /** 161 | * Creates AccountInfo context for a domain 162 | * 163 | * @param string domain domain name 164 | * @param function next callback(error, accountResult) 165 | */ 166 | AuthModule.prototype.domainAuth = function(domainName, next) { 167 | var self = this; 168 | 169 | this.dao.find('domain', { 170 | 'name' : domainName 171 | }, function(err, domain) { 172 | if (err) { 173 | next(err); 174 | } else { 175 | if (domain) { 176 | self._test( 177 | domain.owner_id, 178 | '', 179 | { 180 | asOwner : true, 181 | acctBind : true, 182 | domainId : domain.id 183 | }, 184 | function(err, accountInfo) { 185 | accountInfo.activeDomain = domain; 186 | 187 | next(err, accountInfo); 188 | } 189 | ); 190 | } else { 191 | next('Not Found'); 192 | } 193 | } 194 | }); 195 | } 196 | 197 | module.exports = AuthModule; 198 | -------------------------------------------------------------------------------- /src/modules/cdn/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/modules/cdn/README.md: -------------------------------------------------------------------------------- 1 | fs-cdn 2 | ====== 3 | 4 | A streaming adapter between fs and pkgcloud 5 | 6 | fs-cdn allows you to mirror basic native `fs` and `pkgcloud` functionality. It abstracts the interface into an intuitive set of methods for basic file operations on either your local filesystem and a storage provider supported by `pkgcloud`. 7 | 8 | Getting Started 9 | --------------- 10 | 11 | There are two ways to install fs-cdn. 12 | 13 | npm install fs-cdn 14 | 15 | or 16 | 17 | git clone git@github.com:wotio/fs-cdn.git 18 | cd fs-cdn 19 | npm install 20 | 21 | Next, create a file called `config.json` in your project. It should have a structure that conforms to the pkgcloud config object, which varies per provider. Below is an example of a config using Rackspace Cloudfiles: 22 | 23 | { 24 | "data_dir" : "/path/to/data/dir" // required - the relative root directory, all paths will be relative to this 25 | "provider" : "rackspace", 26 | "username" : "", // provider-specific, see pkgcloud's storage docs for supported providers and their respective config requirements 27 | "apiKey" : "", // provider-specific 28 | "region" : "", // optional 29 | "container" : "" // optional, but without it you must specify the container per file operation 30 | } 31 | 32 | A sample script using `fs-cdn` would thus look something like this: 33 | 34 | ```Javascript 35 | var Fs_Cdn = require('fs-cdn'), 36 | remoteConfig = require(''), 37 | localConfig = require(''); 38 | 39 | var fs_cdn_remote = new Fs_Cdn(remoteConfig), 40 | fs_cdn_local = new Fs_Cdn(localConfig); 41 | ``` 42 | 43 | Available Methods 44 | ----------------- 45 | 46 | ### **save(*dest*, *source*, *options*, *callback*)** 47 | Pipes ReadStream *source* to *dest*. If a string filepath is passed instead of a ReadStream, `save` creates a ReadStream from it. 48 | 49 | ***dest***: {string} Full path of destination file 50 | ***source***: {object or string} ReadStream or full path of source file 51 | ***options***: {object} Options object 52 | ***callback***: {function} Callback 53 | 54 | ```Javascript 55 | var options = { 56 | persist: @boolean, // If true, stores file in /perm subfolder. If false, stores files in /tmp 57 | append: @boolean, // If true, appends the source to the dest file, instead of overwriting 58 | compress: @boolean, // If true, compresses the file using gzip. 59 | write: @boolean // If true, looks for an instance of Buffer as argument[1] and writes it to the file. 60 | } 61 | 62 | fs_cdn_local.save('path/to/dest/filename.txt', <'path/to/src/filename.txt' or fs.createReadStream('path/to/src/filename.txt')>, options, function(err, result) { 63 | 64 | // result will be a file object like this: 65 | // { 66 | // size : @int, 67 | // localpath : @string, 68 | // name : @string, 69 | // type : @string, 70 | // encoding : 'binary' 71 | // } 72 | 73 | }); 74 | 75 | fs_cdn_remote.save('path/to/dest/filename.txt', <'path/to/src/filename.txt' or fs.createReadStream('path/to/src/filename.txt')>, options, function(err, result) { 76 | 77 | // result will be a file object like this: 78 | // { 79 | // size : @int, 80 | // localpath : @string, 81 | // name : @string, 82 | // type : @string, 83 | // container: @string, 84 | // encoding : 'binary' 85 | // } 86 | 87 | }); 88 | ``` 89 | 90 | ### **get(*source*, *callback*)** 91 | Gets *source* and a readStream pointing to it. If a string filepath is passed instead of a File object, `get` normalizes it into a File object (after checking for its existence). 92 | 93 | ***source***: {object or string} Full path of source file or File object 94 | ***callback***: {function} Callback 95 | 96 | ```Javascript 97 | fs_cdn_local.get(<'path/to/src/filename.txt' or { localpath : 'path/to/src/filename.txt', name: 'filename.txt', encoding: 'binary' }>, function(err, result, readStream) { 98 | 99 | // result will be a file object like this: 100 | // { 101 | // size : @int, 102 | // localpath : @string, 103 | // name : @string, 104 | // type : @string, 105 | // container: @string, 106 | // encoding : 'binary' 107 | // } 108 | // 109 | // readStream will be an instance of fs.readStream from the filesystem 110 | 111 | }); 112 | 113 | fs_cdn_remote.get(<'path/to/src/filename.txt' or { localpath : 'path/to/src/filename.txt', name: 'filename.txt', encoding: 'binary' }>, function(err, result, readStream) { 114 | 115 | // result will be a file object like this: 116 | // { 117 | // size : @int, 118 | // localpath : @string, 119 | // name : @string, 120 | // type : @string, 121 | // container: @string, 122 | // encoding : 'binary' 123 | // } 124 | // 125 | // readStream will be an instance of fs.readStream from the CDN storage provider 126 | 127 | }); 128 | ``` 129 | 130 | ### **list(*dir*, *callback*)** -> 131 | Lists all files in *dir* or in remote container 132 | 133 | ***dir***: {string} directory path (local) or container name (remote, optional if container was specified in config) 134 | ***callback***: {function} Callback 135 | 136 | ```Javascript 137 | fs_cdn_local.list('path/to/dst/dir', function(err, result) { 138 | // handle results 139 | }); 140 | ``` 141 | 142 | ```Javascript 143 | fs_cdn_remote.list(function(err, result) { 144 | // handle results 145 | }); 146 | ``` 147 | 148 | In the callback, `result` will be an array of the files in the container or folder. 149 | 150 | ### **find(*file*, *callback*)** -> 151 | Finds the specified file. 152 | 153 | ***file***: {string} directory path (local) or file name (remote) 154 | ***callback***: {function} Callback 155 | 156 | ```Javascript 157 | fs_cdn_local.find('path/to/local/filename.txt', function(err, result) { 158 | // handle results 159 | }); 160 | ``` 161 | 162 | In the callback, `results` object will be an instance of [fs.Stats](http://nodejs.org/api/fs.html#fs_class_fs_stats) 163 | 164 | ```Javascript 165 | fs_cdn_remote.find('filename.txt', function(err, result) { 166 | // handle results 167 | }); 168 | ``` 169 | 170 | In the callback, `result` object will be a pkgcloud [File Model](https://github.com/pkgcloud/pkgcloud/blob/master/docs/providers/rackspace/storage.md#file-model). 171 | 172 | ### **remove(*file*, *callback*)** -> 173 | Removes the specified file. 174 | 175 | ***file***: {string} directory path (local) or file name (remote) 176 | ***options***: {object} Options object 177 | ***callback***: {function} Callback 178 | 179 | ```Javascript 180 | fs_cdn_local.remove('path/to/local/filename.txt', function(err, result) { 181 | // handle results 182 | }); 183 | ``` 184 | 185 | ```Javascript 186 | fs_cdn_remote.remove('filename.txt', function(err, result) { 187 | // handle results 188 | }); 189 | ``` -------------------------------------------------------------------------------- /src/modules/cdn/cdn.js: -------------------------------------------------------------------------------- 1 | 2 | var client = null, 3 | fs = require('fs'), 4 | FsProto = require('./fs.js'), 5 | pkgcloud = require('pkgcloud'), 6 | util = require('util'); 7 | 8 | function CDNProto(options) { 9 | if (options.hasOwnProperty("provider")) { 10 | client = pkgcloud.storage.createClient(options); 11 | this.opts = options; 12 | } 13 | else { 14 | console.warn("Credentials not found. Initializing in local fs mode...") 15 | } 16 | }; 17 | 18 | util.inherits(CDNProto, FsProto); 19 | 20 | CDNProto.prototype = { 21 | 22 | /* 23 | * Saves a file from a string or readStream, syncs to CDN, and returns file object 24 | * 25 | * dest: string or object 26 | * source: string or readStream 27 | * options: object [optional] 28 | * next: function(string err, object file) 29 | */ 30 | 31 | save: function() { 32 | var self = this, 33 | dest = arguments[0], 34 | source = arguments[1], 35 | options = (arguments[2] ? arguments[2] : null), 36 | destPath = ((typeof dest === 'object') ? dest.localpath : dest), 37 | next = arguments[arguments.length-1], 38 | clientOpts = { 39 | container: ((options && options.container) ? options.container : self.opts.container), 40 | remote: destPath 41 | }, 42 | readStream = ((typeof source === 'string') ? fs.createReadStream(source) : source), 43 | writeStream = client.upload(clientOpts), 44 | local = new FsProto({data_dir: this.opts.data_dir}); 45 | 46 | local.save(dest, source, options, function(err, file) { 47 | if (err) next(err); 48 | 49 | writeStream.on('error', next); 50 | writeStream.on('success', function() { 51 | file.container = clientOpts.container; 52 | next(null, file); 53 | }) 54 | }); 55 | 56 | readStream.pipe(writeStream); 57 | }, 58 | 59 | /* 60 | * Gets a file from a fileStruct, if id doesn't exist, check the CDN and return a new fileStruct and readStream from CDN 61 | * 62 | * source: string or object 63 | * next: function(string err, object file, readStream) 64 | */ 65 | 66 | get: function() { 67 | var self = this, 68 | source = arguments[0], 69 | next = arguments[arguments.length-1], 70 | srcPath = ((typeof source === 'object') ? source.localpath : source), 71 | clientOpts = { 72 | container: (source.container ? source.container : self.opts.container), 73 | remote: srcPath 74 | }, 75 | local = new FsProto({data_dir: this.opts.data_dir}); 76 | 77 | if (typeof srcPath !== 'string') next("fs_cdn.get() requires either a valid path string or a File object with property 'localpath'."); 78 | 79 | fs.stat(srcPath, function(err, result) { 80 | if (err) { 81 | var readStream = client.download(clientOpts) 82 | local.save(srcPath, readStream, function(err, file) { 83 | if (err) next(err); 84 | file.container = clientOpts.container; 85 | next(null, result, readStream); 86 | }); 87 | } 88 | else { 89 | readStream = fs.createReadStream(srcPath); 90 | 91 | self.utils.normalize(srcPath, function(err, file) { 92 | file.container = clientOpts.container; 93 | next(err, file, readStream); 94 | }); 95 | } 96 | }); 97 | }, 98 | 99 | /* 100 | * Lists all files in directory path 101 | * 102 | * file: string or object 103 | * next: function(string err, array files) 104 | */ 105 | 106 | list: function() { 107 | 108 | var file = arguments[0], 109 | filePath = ((typeof file === 'object') ? file.localpath : file), 110 | next = arguments[arguments.length-1], 111 | local = new FsProto({data_dir: this.opts.data_dir}); 112 | 113 | local.list(file, next); 114 | }, 115 | 116 | /* 117 | * Removes file from filesystem and CDN 118 | * 119 | * file: string or object 120 | * next: function(string err, boolean success) 121 | */ 122 | 123 | remove: function() { 124 | var self = this; 125 | if (client && arguments[0] && typeof arguments[0] === 'string' && typeof arguments[arguments.length-1] === 'function') { 126 | var container = (arguments[1] && typeof arguments[1] === 'string' ? arguments[1] : self.opts.container); 127 | client.removeFile(container, arguments[0], arguments[arguments.length-1]); 128 | } 129 | }, 130 | 131 | utils: require('./utils') 132 | 133 | }; 134 | 135 | module.exports = CDNProto; 136 | -------------------------------------------------------------------------------- /src/modules/cdn/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider" : "", 3 | "data_dir" : "", 4 | "username" : "", 5 | "apiKey" : "", 6 | "container" : "" 7 | } 8 | -------------------------------------------------------------------------------- /src/modules/cdn/fs.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | imagemagick = require('imagemagick'), 3 | multer = require('multer'), 4 | mime = require('mime'), 5 | path = require('path'), 6 | regex = /[^\\/]+\.[^\\/]+$/, 7 | Stream = require('stream') 8 | zlib = require('zlib'); 9 | 10 | function FsProto(options) { 11 | this.dataDir = path.resolve((0 === options.data_dir.indexOf('/') ? options.data_dir : options.basePath + '/../' + options.data_dir)); 12 | this.tmpDir = options.data_dir + '/tmp'; 13 | this.permDir = options.data_dir + '/perm'; 14 | }; 15 | 16 | FsProto.prototype = { 17 | 18 | /* 19 | * Saves a file from a string or readStream, and returns file object 20 | * 21 | * dest: string or object 22 | * source: string or readStream 23 | * options: object [optional] 24 | * next: function(string err, object file) 25 | */ 26 | 27 | save: function() { 28 | 29 | var self = this, 30 | dest = arguments[0], 31 | source = arguments[1], 32 | buffer = ((arguments[1] instanceof Buffer) ? arguments[1] : null) 33 | options = (arguments[2] ? arguments[2] : null), 34 | next = arguments[arguments.length-1], 35 | compress = options && options.compress, 36 | append = options && options.append, 37 | header = options && options.header, 38 | write = options && options.write, 39 | encoding = ((options && options.encoding) ? options.encoding : null), 40 | destPath = ((typeof dest === 'object') ? dest.localpath : dest), 41 | rootDir = ((options && options.persist) ? self.permDir : self.tmpDir), 42 | writeOptions = {}; 43 | 44 | if (compress) { 45 | destPath += '.zip'; 46 | } 47 | 48 | if (append) { 49 | writeOptions.flags = 'a'; 50 | } 51 | 52 | if (buffer) { 53 | var readStream = null 54 | } 55 | else { 56 | var readStream = ((typeof source === 'string') ? fs.createReadStream(source) : source); 57 | readStream.pause(); 58 | } 59 | 60 | self.utils.parse_path(destPath, rootDir, function(err, path) { 61 | 62 | if (err) { 63 | next(err); 64 | } 65 | 66 | (function(next) { 67 | var writeStream = fs.createWriteStream(path, writeOptions); 68 | 69 | if (header) { 70 | var headerStream = new Stream(); 71 | headerStream.on('data', function(data) { 72 | writeStream.write(data); 73 | }); 74 | 75 | headerStream.emit('data', options.header); 76 | } 77 | 78 | writeStream.on('error', next); 79 | writeStream.on('finish', function(err) { 80 | 81 | if (err) next(err); 82 | else self.utils.normalize(path, next); 83 | }); 84 | 85 | if (compress) { 86 | var gzip = zlib.createGzip(); 87 | readStream.pipe(gzip).pipe(writeStream); 88 | readStream.resume(); 89 | } 90 | else if (readStream) { 91 | readStream.pipe(writeStream); 92 | readStream.resume(); 93 | } 94 | 95 | if (buffer) { 96 | writeStream.write(encoding ? buffer : buffer.toString(), encoding, function() { 97 | writeStream.end(); 98 | }); 99 | } 100 | })(next); 101 | 102 | }); 103 | }, 104 | 105 | 106 | /* 107 | * Saves an avatar file 108 | * 109 | * fileName: string 110 | * source: string. image location | url 111 | * dstPath: string 112 | * next: function(string err, string dstFile) 113 | */ 114 | saveAvatar: function() { 115 | var self = this, 116 | fileName = arguments[0], 117 | source = arguments[1], 118 | dstPath = arguments[2], 119 | next = arguments[3]; 120 | 121 | var dstFile = dstPath + fileName + '.png'; 122 | 123 | self.save(dstFile, source, { persist : true }, function(err, file) { 124 | if (err) { 125 | next(err); 126 | } else { 127 | var options = { 128 | srcPath : file.localpath, 129 | dstPath : file.localpath.replace(/\.[a-z]{0,4}$/i, '.png'), 130 | format: 'png', 131 | height: 125, 132 | width: 125 133 | }; 134 | 135 | imagemagick.convert([options.srcPath, '-resize', '125x125', options.dstPath], function(err, result) { 136 | if (err) { 137 | next(err); 138 | } else { 139 | next(err, dstFile); 140 | } 141 | }); 142 | } 143 | }); 144 | 145 | }, 146 | 147 | 148 | /* 149 | * Gets a file from a fileStruct and returns a new fileStruct and readStream 150 | * 151 | * source: string or object 152 | * next: function(string err, object file, readStream) 153 | */ 154 | 155 | get: function() { 156 | 157 | var self = this, 158 | source = arguments[0], 159 | options = (arguments[1] ? arguments[1] : null), 160 | rootDir = ((options && options.persist) ? self.permDir : self.tmpDir), 161 | next = arguments[arguments.length-1], 162 | srcPath = ((typeof source === 'object') ? source.localpath : rootDir + source), 163 | readStream = fs.createReadStream(srcPath); 164 | 165 | self.utils.normalize(srcPath, function(err, struct) { 166 | if (source.name) { 167 | struct.name = source.name; 168 | } 169 | 170 | next(err, struct, readStream); 171 | }); 172 | }, 173 | 174 | /* 175 | * Lists all files in directory path 176 | * 177 | * file: string or object 178 | * next: function(string err, array files) 179 | */ 180 | 181 | list: function() { 182 | 183 | var file = arguments[0], 184 | filePath = ((typeof file === 'object') ? file.localpath : file), 185 | next = arguments[arguments.length-1]; 186 | 187 | if (filePath.match(regex)) next("list() cannot be called on files, only directories."); 188 | 189 | fs.exists(filePath, function(exists) { 190 | if (exists) { 191 | fs.readdir(filePath, function (err, files) { 192 | if (err) next(err); 193 | 194 | var results = []; 195 | 196 | files.map(function (file) { 197 | return path.join(filePath, file); 198 | }).filter(function (file) { 199 | return fs.statSync(file).isFile(); 200 | }).forEach(function (file) { 201 | (self.utils.normalize(file.name, function(err, fileStruct) { 202 | results.push(fileStruct) 203 | }))(results, filePath) 204 | }); 205 | 206 | next(null, results) 207 | }); 208 | } 209 | else { 210 | next("Directory does not exist."); 211 | } 212 | }); 213 | }, 214 | 215 | /* 216 | * Removes file from filesystem 217 | * 218 | * file: string or object 219 | * options: object 220 | * next: function(string err, boolean success) 221 | */ 222 | 223 | remove: function() { 224 | 225 | var file = arguments[0], 226 | filePath = ((typeof file === 'object') ? file.localpath : file), 227 | options = (arguments[1] ? arguments[1] : null), 228 | rootDir = ((options && options.persist) ? self.permDir : self.tmpDir), 229 | next = arguments[arguments.length-1]; 230 | 231 | fs.exists(rootDir + filePath, function(exists) { 232 | if (exists) fs.unlink(filePath, next); 233 | else next("File does not exist."); 234 | }); 235 | }, 236 | 237 | /* 238 | * Finds file in filesystem 239 | * 240 | * file: string or object 241 | * next: function(string err, object file) 242 | */ 243 | 244 | find: function() { 245 | 246 | var file = arguments[0], 247 | self = this, 248 | filePath = ((typeof file === 'object') ? file.localpath : file), 249 | next = arguments[arguments.length-1]; 250 | 251 | fs.exists(filePath, function(exists) { 252 | if (exists) self.utils.normalize(filePath, next); 253 | else next("File does not exist."); 254 | }); 255 | }, 256 | 257 | utils: require('./utils') 258 | 259 | }; 260 | 261 | module.exports = FsProto; 262 | -------------------------------------------------------------------------------- /src/modules/cdn/index.js: -------------------------------------------------------------------------------- 1 | var CDNProto = require('./src/cdn.js'); 2 | var FsProto = require('./src/fs.js'); 3 | 4 | function Fs_Cdn(options) { 5 | if (options && options.hasOwnProperty("provider") && options.hasOwnProperty("data_dir")) { 6 | this.prototype = CDNProto.prototype; 7 | return new CDNProto(options); 8 | } 9 | else if (options && options.hasOwnProperty("data_dir")) { 10 | this.prototype = FsProto.prototype; 11 | return new FsProto(options); 12 | } 13 | else throw new Error("Required options not specified in config.") 14 | }; 15 | 16 | module.exports = Fs_Cdn; -------------------------------------------------------------------------------- /src/modules/cdn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fs-cdn", 3 | "version": "0.0.3", 4 | "description": "An adapter between fs and pkgcloud", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --timeout 10000" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/wotio/fs-cdn" 12 | }, 13 | "author": "Alfonso Gober ", 14 | "license": "Apache", 15 | "bugs": { 16 | "url": "https://github.com/wotio/fs-cdn/issues" 17 | }, 18 | "homepage": "https://github.com/wotio/fs-cdn", 19 | "devDependencies": { 20 | "mocha": "latest", 21 | "should": "^4.0.4" 22 | }, 23 | "dependencies": { 24 | "async": "^0.9.0", 25 | "mime": "^1.2.11", 26 | "multer": "^0.1.6", 27 | "pkgcloud": "latest", 28 | "mkdirp": ">=0.3.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/modules/cdn/utils.js: -------------------------------------------------------------------------------- 1 | var mkdirp = require('mkdirp'), 2 | multer = require('multer'), 3 | path = require('path'); 4 | 5 | module.exports = { 6 | HTTPFormHandler : function() { 7 | return multer({ 8 | dest : this.tmpDir, 9 | onFileUploadComplete : function(file) { 10 | file.localpath = file.path; 11 | file.name = file.originalname; 12 | file.type = file.mimetype; 13 | } 14 | }); 15 | }, 16 | 17 | parse_path: function(relPath, root, next) { 18 | var relPathRegexp = /\.\./g, 19 | mode = null, // @todo permissions mask? 20 | localPath = path.resolve( (root + '/' + relPath).replace(relPathRegexp, '')), 21 | basePath = path.dirname(localPath); 22 | 23 | mkdirp(basePath, mode, function(err) { 24 | next(err, localPath); 25 | }); 26 | }, 27 | 28 | gzip_compress: function() { 29 | console.log("Hi"); 30 | }, 31 | 32 | get_filename_from_path: function(path) { 33 | return path.split('\\').pop().split('/').pop(); 34 | }, 35 | 36 | normalize: function(filePath, next) { 37 | var self = this; 38 | 39 | fs.stat(filePath, function(err, stats) { 40 | if (err) { 41 | next(err); 42 | } else { 43 | next(null, { 44 | size : stats.size, 45 | localpath : filePath, 46 | name : self.get_filename_from_path(filePath), 47 | type : mime.lookup(filePath), 48 | encoding : 'binary' 49 | }); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/worker.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * Dedicated worker 23 | */ 24 | process.env.WORKER = true; 25 | 26 | var bootstrap = require(__dirname + '/bootstrap'), 27 | app = bootstrap.app, 28 | helper = require('./lib/helper'), 29 | cluster = require('cluster'); 30 | 31 | // export app everywhere 32 | module.exports.app = app; 33 | 34 | // 35 | // ------ START CLUSTER 36 | // 37 | 38 | app.logmessage('BIPIO_WORKER:STARTED:' + new Date()); 39 | 40 | if (cluster.isMaster) { 41 | // when user hasn't explicitly configured a cluster size, use 1 process per cpu 42 | var forks = GLOBAL.CFG.server.forks ? GLOBAL.CFG.server.forks : require('os').cpus().length; 43 | app.logmessage('Node v' + process.versions.node); 44 | app.logmessage('Starting ' + forks + ' fork(s)'); 45 | 46 | for (var i = 0; i < forks; i++) { 47 | cluster.fork(); 48 | } 49 | 50 | cluster.on('disconnect', function(worker) { 51 | app.logmessage('Worker:' + worker.workerID + ':Disconnect'); 52 | cluster.fork(); 53 | }); 54 | 55 | } else { 56 | 57 | workerId = cluster.worker.workerID; 58 | 59 | helper.tldtools.init( 60 | function() { 61 | app.logmessage('TLD:UP'); 62 | }, 63 | function(body) { 64 | app.logmessage('TLD:Cache fail - ' + body, 'error') 65 | } 66 | ); 67 | } 68 | 69 | app.logmessage('WAITING...'); 70 | -------------------------------------------------------------------------------- /tests/lib/helper.js: -------------------------------------------------------------------------------- 1 | process.HEADLESS = true 2 | 3 | var bootstrap = require(__dirname + '/../../src/bootstrap'), 4 | assert = require('assert'), 5 | should = require('should'); 6 | 7 | describe('helper', function() { 8 | it('can get the bipio favicon', function(done) { 9 | app.helper.resolveFavicon('https://bip.io/docs/pods', function(err, url, suffix, hashedIco) { 10 | url.should.equal('https://bip.io/favicon.ico') 11 | done(); 12 | }); 13 | 14 | }); 15 | 16 | // arbitrary - this could change at any time :D :D 17 | it('can get icon from link in page', function(done) { 18 | app.helper.resolveFavicon('https://evernote.com/', function(err, url, suffix, hashedIco) { 19 | url.should.equal('https://evernote.com/media/img/favicon.ico') 20 | done(); 21 | }); 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /tests/managers/dao-mongo.js: -------------------------------------------------------------------------------- 1 | var bootstrap = require(__dirname + '/../../src/bootstrap'), 2 | dao = bootstrap.app.dao, 3 | assert = require('assert'), 4 | should = require('should'), 5 | uuid = require('node-uuid'), 6 | accountInfo; 7 | 8 | describe('bootstrap', function() { 9 | it('can bootstrap', function(done) { 10 | this.timeout(0); 11 | dao.on('ready', function() { 12 | done(); 13 | }); 14 | }); 15 | 16 | it('can copy a collection', function(done) { 17 | dao.copyTo('bips', 'pod_syndication_dups', function() { 18 | console.log(arguments); 19 | done(); 20 | }) 21 | }); 22 | }); -------------------------------------------------------------------------------- /tests/managers/dao.js: -------------------------------------------------------------------------------- 1 | var bootstrap = require(__dirname + '/../../src/bootstrap'), 2 | dao = bootstrap.app.dao, 3 | assert = require('assert'), 4 | should = require('should'), 5 | uuid = require('node-uuid'), 6 | accountInfo; 7 | 8 | describe('bootstrap', function() { 9 | it('can bootstrap', function(done) { 10 | this.timeout(0); 11 | dao.on('ready', function() { 12 | done(); 13 | }); 14 | }); 15 | 16 | it('has queues', function(done) { 17 | this.timeout(0); 18 | bootstrap.app.bastion.on('readyQueue', function(queueName) { 19 | if ('queue_jobs' === queueName) { 20 | done(); 21 | } 22 | }); 23 | }); 24 | 25 | it('can compile a token authenticated accountInfo object', function(done) { 26 | dao.checkAuth( 27 | GLOBAL.CFG.testing_user.username, 28 | GLOBAL.CFG.testing_user.password, 29 | 'token', 30 | function(err, acct) { 31 | if (err) { 32 | done(err); 33 | } else { 34 | acct.should.be.ok; 35 | acct.user.username.should.equal(GLOBAL.CFG.testing_user.username); 36 | 37 | // assert expected object structure and interface 38 | acct.should.have.ownProperty('user'); 39 | acct.user.should.have.ownProperty('id'); 40 | acct.user.settings.should.be.a('object'); 41 | 42 | acct.user.domains.should.be.a('object'); 43 | acct.user.domains.set.should.be.a('function'); 44 | acct.user.domains.get.should.be.a('function'); 45 | acct.user.domains.test.should.be.a('function'); 46 | 47 | acct.user.channels.should.be.a('object'); 48 | acct.user.channels.set.should.be.a('function'); 49 | acct.user.channels.get.should.be.a('function'); 50 | acct.user.channels.test.should.be.a('function'); 51 | 52 | Object.keys(acct.user.domains).should.not.be.empty; 53 | 54 | accountInfo = acct; 55 | done(); 56 | } 57 | } 58 | ); 59 | }); 60 | 61 | it('can compile a domain accountInfo object', function(done) { 62 | dao.domainAuth('admin.localhost', true, 63 | function(err, acct) { 64 | if (err) { 65 | err.should.not.be.ok; 66 | done(); 67 | } else { 68 | 69 | console.log(acct); 70 | 71 | acct.should.be.ok; 72 | acct.user.username.should.equal(GLOBAL.CFG.testing_user.username); 73 | 74 | // assert expected object structure and interface 75 | acct.should.have.ownProperty('user'); 76 | acct.user.should.have.ownProperty('id'); 77 | acct.user.settings.should.be.a('object'); 78 | 79 | acct.user.domains.should.be.a('object'); 80 | acct.user.domains.set.should.be.a('function'); 81 | acct.user.domains.get.should.be.a('function'); 82 | acct.user.domains.test.should.be.a('function'); 83 | 84 | acct.user.channels.should.be.a('object'); 85 | acct.user.channels.set.should.be.a('function'); 86 | acct.user.channels.get.should.be.a('function'); 87 | acct.user.channels.test.should.be.a('function'); 88 | 89 | Object.keys(acct.user.domains).should.not.be.empty; 90 | 91 | accountInfo = acct; 92 | done(); 93 | } 94 | } 95 | ); 96 | }); 97 | 98 | /* 99 | it('can compile a domain/authenticated accountInfo object', function(done) { 100 | dao.checkAuth( 101 | GLOBAL.CFG.testing_user.username, 102 | GLOBAL.CFG.testing_user.password, 103 | 'token', 104 | function(err, acct) { 105 | if (err) { 106 | done(err); 107 | } else { 108 | acct.should.be.ok; 109 | acct.user.username.should.equal(GLOBAL.CFG.testing_user.username); 110 | 111 | // assert expected object structure and interface 112 | acct.should.have.ownProperty('user'); 113 | acct.user.should.have.ownProperty('id'); 114 | acct.user.settings.should.be.a('object'); 115 | 116 | acct.user.domains.should.be.a('object'); 117 | acct.user.domains.set.should.be.a('function'); 118 | acct.user.domains.get.should.be.a('function'); 119 | acct.user.domains.test.should.be.a('function'); 120 | 121 | acct.user.channels.should.be.a('object'); 122 | acct.user.channels.set.should.be.a('function'); 123 | acct.user.channels.get.should.be.a('function'); 124 | acct.user.channels.test.should.be.a('function'); 125 | 126 | Object.keys(acct.user.domains).should.not.be.empty; 127 | 128 | accountInfo = acct; 129 | done(); 130 | } 131 | } 132 | ); 133 | }); 134 | */ 135 | }); 136 | 137 | 138 | /* 139 | describe('bips', function() { 140 | it('can save a bip', function(done) { 141 | this.timeout(0); 142 | 143 | var bip = { 144 | type : 'http', 145 | hub : { 146 | source : { 147 | // edges : [ cid ] 148 | edges : [ '88013839-3932-4083-ae67-fb02f3f32b92' ] 149 | 150 | } 151 | } 152 | }, 153 | model = dao.modelFactory('bip', bip, accountInfo, true); 154 | dao.create(model, function(err, result) { 155 | console.log(err); 156 | console.log(result); 157 | done(!err && result); 158 | }, accountInfo); 159 | }); 160 | }); 161 | */ 162 | describe('retrieve a pod', function() { 163 | it('contains expected attributes', function(done) { 164 | dao.pod('facebook').should.have.ownProperty('_description'); 165 | dao.pod('facebook').should.have.ownProperty('_description'); 166 | done(); 167 | }); 168 | }); -------------------------------------------------------------------------------- /tests/migrations/0.3.0.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | app = require(__dirname + '/../../src/bootstrap').app, 3 | targetConfig = path.resolve( process.env.NODE_CONFIG_DIR || path.join(__dirname, '/../../config/'), 'test.json'), 4 | migration = require(__dirname + '/../../migrations/0.3.0'), 5 | assert = require('assert'), 6 | should = require('should'); 7 | 8 | describe('Migration', function() { 9 | describe('Successfully migrates to v0.3.0', function() { 10 | if (!process.env.NODE_ENV === 'test') throw new Error("NODE_ENV must be set to `test` to run migration tests! Please run `export NODE_ENV=test` and then `make install`. During install, set the name of your db to something OTHER THAN `bipio` (so as not to interfere with any existing installations). Then re-run this migration test."); 11 | it('Setup', function(done){ 12 | var config = JSON.parse(fs.readFileSync(targetConfig)); 13 | if (!config.hasOwnProperty("cdn")) config.cdn = "test/cdn/dir"; 14 | if (!config.hasOwnProperty("datadir")) config.datadir = "test/data/dir"; 15 | fs.writeFile(targetConfig, JSON.stringify(config, null, 2), done); 16 | }); 17 | 18 | it('Creates 500 pod_syndication_track_subscribe entries with current timestamps', function(done) { 19 | for (var i=1; i<=500; i++) { 20 | (function(isLast) { 21 | app.dao.create(app.dao.modelFactory('pod_syndication_track_subscribe', { 22 | owner_id : Math.random().toString(36).slice(2), 23 | channel_id : Math.random().toString(36).slice(2), 24 | created: new Date().getTime(), 25 | guid : Math.random().toString(36).slice(2), 26 | bip_id : Math.random().toString(36).slice(2), 27 | last_update : new Date().getTime() 28 | }), 29 | function(err, modelName, retModel, code) { 30 | if (err) throw new Error(err); 31 | if (isLast) done(); 32 | }); 33 | })(i === 500); 34 | } 35 | }); 36 | 37 | it('Creates 500 pod_syndication_track_subscribe entries with 90-day-old timestamps', function(done) { 38 | for (var i=1; i<=500; i++) { 39 | (function(isLast) { 40 | app.dao.create(app.dao.modelFactory('pod_syndication_track_subscribe', { 41 | owner_id : Math.random().toString(36).slice(2), 42 | channel_id : Math.random().toString(36).slice(2), 43 | created: new Date().getTime() - (90 * 24 * 60 * 60 * 1000), 44 | guid : Math.random().toString(36).slice(2), 45 | bip_id : Math.random().toString(36).slice(2), 46 | last_update : new Date().getTime() 47 | }), 48 | function(err) { 49 | if (err) throw new Error(err); 50 | if (isLast) done(); 51 | }); 52 | })(i === 500); 53 | } 54 | }); 55 | 56 | it('Creates 500 pod_soundcloud_track_favorite entries with current timestamps', function(done) { 57 | for (var i=1; i<=500; i++) { 58 | (function(isLast) { 59 | app.dao.create(app.dao.modelFactory('pod_soundcloud_track_favorite', { 60 | owner_id : Math.random().toString(36).slice(2), 61 | channel_id : Math.random().toString(36).slice(2), 62 | created: new Date().getTime(), 63 | track_id : Math.random().toString(36).slice(2), 64 | bip_id : Math.random().toString(36).slice(2), 65 | last_update : new Date().getTime() 66 | }), 67 | function(err) { 68 | if (err) throw new Error(err); 69 | if (isLast) done(); 70 | }); 71 | })(i === 500); 72 | } 73 | }); 74 | 75 | it('Creates 500 pod_soundcloud_track_favorite entries with 90-day-old timestamps', function(done) { 76 | for (var i=1; i<=500; i++) { 77 | (function(isLast) { 78 | app.dao.create(app.dao.modelFactory('pod_soundcloud_track_favorite', { 79 | owner_id : Math.random().toString(36).slice(2), 80 | channel_id : Math.random().toString(36).slice(2), 81 | created: new Date().getTime() - (90 * 24 * 60 * 60 * 1000), 82 | track_id : Math.random().toString(36).slice(2), 83 | bip_id : Math.random().toString(36).slice(2), 84 | last_update : new Date().getTime() 85 | }), 86 | function(err) { 87 | if (err) throw new Error(err); 88 | if (isLast) done(); 89 | }); 90 | })(i === 500); 91 | } 92 | }); 93 | 94 | it('Runs the migration', function(done) { 95 | migration.run(app, targetConfig, function(err) { 96 | if (err) throw new Error(err); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('Checks for the existence of 500 pod_syndication_dup entries', function(done) { 102 | app.dao.findFilter('pod_syndication_dup', {}, function(err, result) { 103 | if (err) throw new Error(err); 104 | should.exist(result); 105 | result.should.have.length(500) 106 | done(); 107 | }); 108 | }); 109 | 110 | it('Checks for the existence of 500 pod_soundcloud_dup entries', function(done) { 111 | app.dao.findFilter('pod_soundcloud_dup', {}, function(err, result) { 112 | if (err) throw new Error(err); 113 | should.exist(result); 114 | result.should.have.length(500) 115 | done(); 116 | }); 117 | }); 118 | 119 | it('Checks test.json for removal of `cdn` and `datadir` properties', function() { 120 | var newConfig = JSON.parse(fs.readFileSync(targetConfig)); 121 | newConfig.should.not.have.property("cdn"); 122 | newConfig.should.not.have.property("datadir"); 123 | }); 124 | 125 | it('Cleans up test data', function(done) { 126 | app.dao.removeFilter('pod_syndication_dup', {}, function(err, result) { 127 | if (err) console.log(err); 128 | app.dao.removeFilter('pod_soundcloud_dup', {}, function(err, result) { 129 | if (err) console.log(err); 130 | app.dao.removeFilter('pod_syndication_track_subscribe', {}, function(err, result) { 131 | if (err) console.log(err); 132 | app.dao.removeFilter('pod_soundcloud_track_favorite', {}, function(err, result) { 133 | if (err) console.log(err); 134 | done(); 135 | }); 136 | }); 137 | }); 138 | }); 139 | }); 140 | }); 141 | }); -------------------------------------------------------------------------------- /tests/models/domain.js: -------------------------------------------------------------------------------- 1 | var bootstrap = require(__dirname + '/../../src/bootstrap'), 2 | dao = bootstrap.app.dao, 3 | assert = require('assert'), 4 | should = require('should'), 5 | uuid = require('node-uuid'), 6 | accountInfo, 7 | domainPublic = GLOBAL.CFG.domain_public.split(':').shift(); 8 | 9 | describe('bootstrap', function() { 10 | it('can compile an accountInfo object', function(done) { 11 | dao.checkAuth( 12 | GLOBAL.CFG.testing_user.username, 13 | GLOBAL.CFG.testing_user.password, 14 | 'token', 15 | function(err, acct) { 16 | if (err) { 17 | done(err); 18 | } else { 19 | acct.should.be.ok; 20 | acct.user.username.should.equal(GLOBAL.CFG.testing_user.username); 21 | 22 | // assert expected object structure and interface 23 | acct.should.have.ownProperty('user'); 24 | acct.user.should.have.ownProperty('id'); 25 | acct.user.settings.should.be.a('object'); 26 | 27 | acct.user.domains.should.be.a('object'); 28 | acct.user.domains.set.should.be.a('function'); 29 | acct.user.domains.get.should.be.a('function'); 30 | acct.user.domains.test.should.be.a('function'); 31 | 32 | acct.user.channels.should.be.a('object'); 33 | acct.user.channels.set.should.be.a('function'); 34 | acct.user.channels.get.should.be.a('function'); 35 | acct.user.channels.test.should.be.a('function'); 36 | 37 | Object.keys(acct.user.domains).should.not.be.empty; 38 | 39 | accountInfo = acct; 40 | done(); 41 | } 42 | } 43 | ); 44 | }); 45 | }); 46 | 47 | describe('domains', function() { 48 | /* 49 | it('can save a localhost domain', function(done) { 50 | var domainStruct = { 51 | name : uuid.v4() + '.localhost', 52 | type : 'custom' 53 | }, 54 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 55 | dao.create(model, function(err, modelName, domain) { 56 | modelName.should.equal('domain'); 57 | 58 | domain.should.have.ownProperty('id'); 59 | domain.should.have.ownProperty('owner_id'); 60 | domain.should.have.ownProperty('type'); 61 | domain.should.have.ownProperty('name'); 62 | domain.should.have.ownProperty('_available'); 63 | 64 | domain.owner_id.should.equal(accountInfo.user.id); 65 | domain.name.should.equal(domainStruct.name); 66 | domain.type.should.equal('custom'); 67 | 68 | domain._available.should.be.true; 69 | done(); 70 | }, accountInfo); 71 | });*/ 72 | 73 | it('can save a non-local domain as unverified', function(done) { 74 | var domainStruct = { 75 | name : uuid.v4() + '.localhost.net' 76 | }, 77 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 78 | dao.create(model, function(err, modelName, domain) { 79 | modelName.should.equal('domain'); 80 | 81 | domain.should.have.ownProperty('id'); 82 | domain.should.have.ownProperty('owner_id'); 83 | domain.should.have.ownProperty('type'); 84 | domain.should.have.ownProperty('name'); 85 | domain.should.have.ownProperty('_available'); 86 | 87 | domain.owner_id.should.equal(accountInfo.user.id); 88 | domain.name.should.equal(domainStruct.name); 89 | domain.type.should.equal('custom'); 90 | 91 | domain._available.should.be.false; 92 | 93 | done(); 94 | }, accountInfo); 95 | }); 96 | 97 | it('can not save *.local domains of type vanity', function(done) { 98 | var domainStruct = { 99 | name : uuid.v4() + '.' + domainPublic, 100 | type : 'vanity' 101 | }, 102 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 103 | dao.create(model, function(err, modelName, result, status) { 104 | result.should.have.property('status'); 105 | result.should.have.property('message'); 106 | result.status.should.equal(400); 107 | 108 | result.message.should.equal('ValidationError'); 109 | result.errors.should.have.property('name'); 110 | done(); 111 | }); 112 | }); 113 | 114 | it('can detect local domains', function(done) { 115 | var domainStruct = { 116 | name : domainPublic + uuid.v4() 117 | } 118 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 119 | model.isLocal(domainStruct.name).should.equal(false); 120 | model.isLocal('testing.' + domainPublic).should.equal(true); 121 | done(); 122 | }); 123 | 124 | it('verfies localhost', function(done) { 125 | var domainStruct = { 126 | name : 'localhost' 127 | } 128 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 129 | model.verify(accountInfo, function(err) { 130 | err.should.be.false; 131 | model._available.should.be.true; 132 | 133 | done(); 134 | }); 135 | }); 136 | 137 | 138 | it('verfies a correctly bound domain', function(done) { 139 | var domainStruct = { 140 | name : 'testing.bip.io' 141 | } 142 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 143 | model.verify( 144 | { 145 | user : accountInfo.user, 146 | getDefaultDomain : function() { 147 | return { 148 | name : 'bip.io' 149 | } 150 | } 151 | }, 152 | function(err) { 153 | err.should.be.false; 154 | model._available.should.be.true; 155 | 156 | done(); 157 | } 158 | ); 159 | }); 160 | 161 | it('should fail verifying an unknown domain', function(done) { 162 | var domainStruct = { 163 | name : uuid.v4() + '.com.net.eu' 164 | } 165 | model = dao.modelFactory('domain', domainStruct, accountInfo, true); 166 | model.verify( 167 | { 168 | user : accountInfo.user, 169 | getDefaultDomain : function() { 170 | return { 171 | name : domainPublic 172 | } 173 | } 174 | }, 175 | function(err, model, result) { 176 | console.log(err); 177 | err.should.have.ownProperty('errno'); 178 | err.errno.should.equal('ENODATA'); 179 | result._available.should.be.false; 180 | done(); 181 | } 182 | ); 183 | }); 184 | }); -------------------------------------------------------------------------------- /tools/archive_logs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * archives active bip_logs table >=2 months old into namespaced table names of bip_logs_YYYYMM 5 | * and drops from bip_logs. 6 | * 7 | * This is a heavy operation, handle with care 8 | * 9 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | * 23 | */ 24 | process.HEADLESS = true; 25 | process.NOCONSUME = true; 26 | 27 | var bootstrap = require(__dirname + '/../src/bootstrap'), 28 | moment = require('moment'), 29 | dao = bootstrap.app.dao, 30 | Q = require('q'), 31 | _ = require('underscore'), 32 | monthAge = 2; 33 | 34 | dao.on('ready', function(dao) { 35 | 36 | // get max and min 37 | dao.aggregate( 38 | 'bip_log', 39 | [{ 40 | $group: { 41 | _id: "$item", 42 | maxCreated: { $max: "$created" }, 43 | minCreated: { $min: "$created" } 44 | } 45 | }], 46 | function(err, result) { 47 | if (err) { 48 | console.error(err); 49 | } else if (result.length) { 50 | 51 | //result[0].minCreated = 1431128106615; 52 | //monthAge = 0; 53 | 54 | // create date promises 55 | var max = result[0].maxCreated, 56 | min = result[0].minCreated, 57 | realMax = moment().subtract(monthAge, 'months').endOf('month').valueOf(), 58 | realMin = moment(min), 59 | year, month, 60 | minYear, maxYear 61 | promises = {}; 62 | 63 | if (max > realMax) { 64 | max = realMax; 65 | } 66 | 67 | minYear = moment(min).get('year'); 68 | maxYear = moment(max).get('year'); 69 | 70 | minMonth = moment(min).get('month'); 71 | maxMonth = moment(max).get('month'); 72 | 73 | // shard into months 74 | month = ++maxMonth; 75 | 76 | for (var i = maxYear; i >= minYear; i--) { 77 | for (; month > 0; month--) { 78 | 79 | promises[i * 100 + month] = Q.defer(); 80 | 81 | if (i == minYear && month == minMonth) { 82 | break; 83 | } 84 | 85 | if (1 == month) { 86 | month = 12; 87 | break; 88 | } 89 | } 90 | } 91 | 92 | Q.all( 93 | _.pluck( 94 | _.values( 95 | promises 96 | ), 97 | 'promise' 98 | ) 99 | ).then( 100 | // accept 101 | function(results) { 102 | console.log(results.join()); 103 | 104 | process.exit(0); 105 | }, 106 | 107 | // reject 108 | function() { 109 | console.error(arguments); 110 | process.exit(0); 111 | } 112 | ); 113 | 114 | _.each(promises, function(promise, yearMonth) { 115 | var ym = moment(yearMonth, 'YYYYMM'), 116 | from = ym.startOf('month').valueOf(), 117 | to = ym.endOf('month').valueOf(), 118 | filter = 119 | { 120 | created: { 121 | $gte : from, 122 | $lte : to 123 | } 124 | }; 125 | 126 | console.log('ARCHIVING ' + yearMonth) 127 | 128 | // do not overwrite into collection which already has records 129 | // ... manually remove records if you need to regenerate the data. 130 | dao.getConnection().collection('bip_logs_' + yearMonth).count({}, function(err, count) { 131 | if (err) { 132 | promise.reject(err); 133 | } else if (count) { 134 | promise.resolve('SKIPPING ' + yearMonth); 135 | } else if (!count) { 136 | dao.getConnection().collection('bip_logs').aggregate( 137 | [ 138 | { 139 | $match: filter 140 | }, 141 | { 142 | $out: "bip_logs_" + yearMonth 143 | } 144 | ], 145 | function(err) { 146 | if (err) { 147 | promise.reject(err); 148 | } else { 149 | //promise.resolve('DONE ' + yearMonth ); 150 | //return; 151 | // cleanup 152 | console.log('CLEANING UP ' + yearMonth) 153 | dao.removeFilter('bip_log', filter, function(err) { 154 | if (err) { 155 | promise.reject(err); 156 | } else { 157 | promise.resolve('DONE ' + yearMonth ); 158 | } 159 | }); 160 | } 161 | } 162 | ); 163 | } 164 | }); 165 | }); 166 | } 167 | }); 168 | }); 169 | 170 | -------------------------------------------------------------------------------- /tools/avsnarf.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | var dao = require('../src/bootstrap'); 24 | var request = require('request'); 25 | 26 | // given an image url and account id, attaches the account avatar 27 | var ownerId = process.argv[2]; 28 | url = process.argv[3], 29 | dstPath = process.argv[4] ? process.argv[4] : GLOBAL.CFG.datadir + '/perm/cdn/img/av/' 30 | 31 | if (!url || !ownerId) { 32 | console.log('Usage : node avsnarf.js {owner id} {url} {destination path}'); 33 | process.exit(1); 34 | } 35 | 36 | url = request.get(url); 37 | 38 | console.log('fetching avatar...'); 39 | dao.app.modules.cdn.saveAvatar(ownerId, url, dstPath, function(err, response) { 40 | if (!err) { 41 | console.log("OK!"); 42 | console.log(response); 43 | } else { 44 | console.log(response); 45 | console.log('Error'); 46 | } 47 | process.exit(0); 48 | }); 49 | -------------------------------------------------------------------------------- /tools/bip-expire.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Expires bips which have reached their end-life by time 25 | */ 26 | process.HEADLESS = true; 27 | var bootstrap = require(__dirname + '/../src/bootstrap'); 28 | bootstrap.app.dao.expireAll(function(err, msg) { 29 | if (err) { 30 | console.log('ERROR:' + err); 31 | console.log(msg); 32 | } else { 33 | console.log('DONE'); 34 | } 35 | 36 | process.exit(0); 37 | }); 38 | -------------------------------------------------------------------------------- /tools/bip-trigger.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Triggers unpaused trigger bips. 25 | */ 26 | process.HEADLESS = true; 27 | var bootstrap = require(__dirname + '/../src/bootstrap'); 28 | bootstrap.app.bastion.on('readyQueue', function(readyQueue) { 29 | if (readyQueue == 'queue_jobs') { 30 | app.logmessage('BIP-TRIGGER:Trigger Queue Discovered:queue_jobs'); 31 | bootstrap.app.dao.triggerAll(function(err, msg) { 32 | if (err) { 33 | app.logmessage('BIP-TRIGGER:' + err + ' ' + msg); 34 | } else { 35 | app.logmessage(msg); 36 | app.logmessage('BIP-TRIGGER:DONE'); 37 | } 38 | process.exit(0); 39 | }); 40 | } 41 | }); -------------------------------------------------------------------------------- /tools/cdnfactory.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | var dao = require('../src/bootstrap'); 24 | 25 | // given a url, downloads the favicon for the domain into our icofactory CDN 26 | var url = process.argv[2], 27 | bipid = process.argv[3]; 28 | 29 | if (!url) { 30 | console.log('Usage : node cdnfactory.js {url} {bip id optional}'); 31 | process.exit(1); 32 | } 33 | 34 | console.log('fetching icon...'); 35 | dao.getBipRefererIcon(bipid, url, true, function(err, response) { 36 | if (!err) { 37 | console.log("OK!"); 38 | console.log(response); 39 | } else { 40 | console.log(response); 41 | console.log('Error'); 42 | } 43 | process.exit(0); 44 | }); 45 | -------------------------------------------------------------------------------- /tools/create_user.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.HEADLESS = true; 4 | if (!process.argv[3]) { 5 | console.log('Usage - create_user {username} {email} {password} {account_level}'); 6 | process.exit(0); 7 | } 8 | 9 | var username = process.argv[2], 10 | email = process.argv[3], 11 | password = process.argv[4], 12 | accountLevel = process.argv[5] 13 | bootstrap = require(__dirname + '/../src/bootstrap'), 14 | dao = bootstrap.app.dao; 15 | 16 | dao.on('ready', function(dao) { 17 | dao.createUser(username, email, password, function() { 18 | console.log(arguments); 19 | process.exit(0); 20 | }, accountLevel || GLOBAL.DEFS.ACCOUNT_LEVEL.USER); 21 | }); 22 | -------------------------------------------------------------------------------- /tools/crypt_get.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var crypto = require('crypto'); 3 | 4 | process.HEADLESS = true; 5 | 6 | var cryptStr = process.argv[2], 7 | bootstrap = require(__dirname + '/../src/bootstrap'); 8 | 9 | function AESDecrypt(cryptedStr, autoPadding) { 10 | var crypted = new Buffer(cryptedStr, 'base64').toString('utf-8'); 11 | var keyVersion = crypted.substr(0, 1), 12 | iv = crypted.substr(1, 16), 13 | key = CFG.k[keyVersion], 14 | cypher = crypted.substr(17); 15 | 16 | var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); 17 | 18 | if (!autoPadding) { 19 | autoPadding = false; 20 | } 21 | decipher.setAutoPadding(autoPadding); 22 | 23 | var decrypted = (decipher.update(cypher, 'base64', 'ascii') + decipher.final('ascii')); 24 | return decrypted; 25 | } 26 | 27 | console.log(AESDecrypt(cryptStr)); -------------------------------------------------------------------------------- /tools/gencert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | # 4 | # Installs a self signed cert for orgname into an install directory 5 | # 6 | 7 | CN=$1 8 | INSTALL_DIR=$2 9 | 10 | cd $2 11 | 12 | openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 13 | 14 | openssl rsa -passin pass:x -in server.pass.key -out server.key 15 | 16 | rm server.pass.key 17 | 18 | openssl req -new -key server.key -out server.csr \ 19 | -subj "/C=US/ST=NY/L=NY/O=WOTIO/OU=BIPIO/CN=$CN" 20 | 21 | openssl x509 -req -in server.csr -signkey server.key -out server.crt -------------------------------------------------------------------------------- /tools/generate-hub-stats.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Forces a builds active hub/edges stats for today for every user. 25 | * The system will otherwise automatically generate these stats once per day. 26 | */ 27 | 28 | process.HEADLESS = true; 29 | var bootstrap = require(__dirname + '/../src/bootstrap'); 30 | bootstrap.app.dao.generateHubStats(function(err, msg) { 31 | if (err) { 32 | app.logmessage('THERE WERE ERRORS'); 33 | } else { 34 | app.logmessage(msg); 35 | app.logmessage('DONE'); 36 | } 37 | process.exit(0); 38 | }); -------------------------------------------------------------------------------- /tools/git-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" 4 | GITDIR=$BASEDIR/.git 5 | 6 | if [ -a $GITDIR ]; then 7 | mkdir -p $GITDIR/hooks 8 | 9 | # add post-merge 10 | cp $BASEDIR/tools/post-merge $GITDIR/hooks 11 | chmod ug+x $GITDIR/hooks/post-merge 12 | fi -------------------------------------------------------------------------------- /tools/heapdump.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Sends a heapdump packet to target pid via bastion 25 | */ 26 | 27 | process.HEADLESS = true; 28 | if (!process.argv[2]) { 29 | console.log('Usage - heapdump {pid}'); 30 | process.exit(0); 31 | } 32 | 33 | var pid = process.argv[2] 34 | bootstrap = require(__dirname + '/../src/bootstrap'), 35 | 36 | bootstrap.app.bastion.on('readyQueue', function(readyQueue) { 37 | if (readyQueue == 'queue_jobs') { 38 | bootstrap.app.bastion.createJob( 39 | DEFS.JOB_HEAP_DUMP, 40 | { 41 | pid : Number(pid), 42 | key : CFG.dumpKey 43 | } 44 | ); 45 | 46 | //queues don't receipt delivery so wait arbitrarily and close out 47 | setTimeout(function() { 48 | process.exit(0); 49 | }, 1000) 50 | } 51 | }); -------------------------------------------------------------------------------- /tools/init-boilerplate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | NAME=$1 5 | LABEL=$2 6 | 7 | if [ $NAME ]; then 8 | 9 | TARGETDIR="$BASEDIR/../node_modules/bip-pod-$NAME" 10 | 11 | if [ -d $TARGETDIR ]; then 12 | echo "Target pod directory $TARGETDIR already exists" 13 | exit 0 14 | fi 15 | 16 | BOILERDIR="$BASEDIR/../node_modules/bip-pod/boilerplate" 17 | if [ ! -d $BOILERDIR ]; then 18 | echo "Source Boilerplate directory $BOILERDIR could not be found" 19 | exit 0 20 | fi 21 | 22 | mkdir $TARGETDIR 23 | cp $BOILERDIR/* $TARGETDIR 24 | cd $TARGETDIR 25 | echo 'node_modules' > .gitignore 26 | 27 | TITLE=$NAME 28 | 29 | if [ $LABEL ]; then 30 | TITLE=$LABEL 31 | fi 32 | 33 | p1="s/boilerplate/${NAME}/g" 34 | p2="s/Boilerplate/${TITLE}/g" 35 | 36 | perl -pi -e $p1 * 37 | perl -pi -e $p2 * 38 | 39 | mv boilerplate.png $NAME.png 40 | 41 | echo 'Done' 42 | 43 | else 44 | echo 'Usage : ./tools/init-boilerplate.sh {pod-name}' 45 | fi 46 | 47 | -------------------------------------------------------------------------------- /tools/login_set.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Sets an explicit password for an account id 25 | */ 26 | 27 | process.HEADLESS = true; 28 | if (!process.argv[3]) { 29 | console.log('Usage - token_set {account uuid} {password}'); 30 | process.exit(0); 31 | } 32 | 33 | var accountId = process.argv[2], 34 | token = process.argv[3], 35 | bootstrap = require(__dirname + '/../src/bootstrap'), 36 | dao = bootstrap.app.dao, 37 | modelName = 'account_auth'; 38 | 39 | dao.on('ready', function(dao) { 40 | dao.find( 41 | modelName, 42 | { 43 | owner_id : accountId, 44 | type : 'login_primary' 45 | }, 46 | function(err, result) { 47 | if (err || !result) { 48 | console.log(err); 49 | if (!result) { 50 | console.log('account id not found'); 51 | } 52 | process.exit(0); 53 | } else { 54 | result.password = token; 55 | 56 | dao.updateProperties( 57 | modelName, 58 | result.id, 59 | { 60 | password : token 61 | }, 62 | function(err, result) { 63 | if (err) { 64 | console.log(err); 65 | console.log(result); 66 | } else { 67 | console.log('new token : ' + token) 68 | console.log('done'); 69 | } 70 | process.exit(0); 71 | } 72 | ); 73 | } 74 | } 75 | ); 76 | }); 77 | -------------------------------------------------------------------------------- /tools/oauth_refresh.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Triggers unpaused trigger bips. 25 | */ 26 | process.HEADLESS = false; 27 | process.NOCONSUME = true; 28 | 29 | var bootstrap = require(__dirname + '/../src/bootstrap'), 30 | dao = bootstrap.app.dao; 31 | 32 | dao.on('ready', function(dao) { 33 | dao.refreshOAuth(function(err) { 34 | console.log(arguments); 35 | if (err) { 36 | console.error(err); 37 | } else { 38 | console.log('done'); 39 | process.exit(0); 40 | } 41 | }); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /tools/pause_bip.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /* 24 | * Pauses a bip by id 25 | */ 26 | 27 | process.NOCONSUME = true; 28 | process.REQ_PODS = "email"; 29 | 30 | var bootstrap = require(__dirname + '/../src/bootstrap'), 31 | dao = bootstrap.app.dao, 32 | bipId = process.argv[2], 33 | reason = process.argv[3]; 34 | 35 | if (!bipId) { 36 | console.log('Usage - ./tools/pause_bip.js {bip_id} "reason" (optional)'); 37 | process.exit(); 38 | } 39 | 40 | function pauseBip(id, reason, next) { 41 | dao.find( 42 | 'bip', 43 | { 44 | id : id 45 | }, 46 | function(err, bip) { 47 | if (err) { 48 | next(err); 49 | 50 | } else if (bip) { 51 | var message = reason || 'Paused By Administrator', 52 | pod = dao.pod('email'); 53 | 54 | if (pod && pod.getConfig().sender) { 55 | message += ' - Please Contact ' + pod.getConfig().sender + ' for more info'; 56 | } 57 | 58 | dao.pauseBip(bip, true, function(err) { 59 | if (err) { 60 | next(err); 61 | } else { 62 | 63 | var jobPacket = { 64 | owner_id : bip.owner_id, 65 | bip_id : bip.id, 66 | code : 'bip_paused_manual', 67 | message : message 68 | }; 69 | 70 | app.bastion.createJob(DEFS.JOB_BIP_ACTIVITY, jobPacket, function() { 71 | next(); 72 | }); 73 | } 74 | } 75 | ); 76 | 77 | } else { 78 | next('Not Found'); 79 | } 80 | } 81 | ); 82 | } 83 | 84 | dao.on('ready', function(dao) { 85 | pauseBip(bipId, reason, function(err) { 86 | if (err) { 87 | console.error(err); 88 | } else { 89 | console.log('ok'); 90 | } 91 | process.exit(0); 92 | }); 93 | }); -------------------------------------------------------------------------------- /tools/post-merge: -------------------------------------------------------------------------------- 1 | #/usr/bin/env bash 2 | # MIT © Sindre Sorhus - sindresorhus.com 3 | # via https://gist.github.com/sindresorhus/7996717 4 | 5 | changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)" 6 | 7 | check_run() { 8 | echo "$changed_files" | grep --quiet "$1" && eval "$2" 9 | } 10 | 11 | check_run package.json "npm install" 12 | -------------------------------------------------------------------------------- /tools/rm_user.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Removes a user by UUID 25 | */ 26 | 27 | var async = require('async'); 28 | 29 | process.HEADLESS = true; 30 | if (!process.argv[2]) { 31 | console.log('Usage - rm_user {account uuid}'); 32 | process.exit(0); 33 | } 34 | 35 | var accountId = process.argv[2], 36 | bootstrap = require(__dirname + '/../src/bootstrap'), 37 | dao = bootstrap.app.dao, 38 | acctSearch = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(accountId), 39 | emailSearch = -1 !== accountId.indexOf('@'); 40 | 41 | function accountQuery(accountId) { 42 | dao.removeUser( 43 | accountId, 44 | function(err, result) { 45 | if (err || !result) { 46 | console.log(err); 47 | if (!result) { 48 | console.log('account id not found'); 49 | } 50 | process.exit(0); 51 | } else { 52 | console.log('removed user'); 53 | process.exit(0); 54 | } 55 | } 56 | ); 57 | } 58 | 59 | dao.on('ready', function(dao) { 60 | if (acctSearch) { 61 | accountQuery(accountId); 62 | } else { 63 | var filter = {}; 64 | if (emailSearch) { 65 | filter.email_account = accountId; 66 | } else { 67 | filter.username = accountId; 68 | } 69 | 70 | dao.find( 71 | 'account', 72 | filter, 73 | function(err, result) { 74 | if (err || !result) { 75 | if (!result) { 76 | console.log('account id not found'); 77 | } 78 | process.exit(0); 79 | } else { 80 | accountQuery(result.id); 81 | } 82 | } 83 | ); 84 | } 85 | }); -------------------------------------------------------------------------------- /tools/set_user_level.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Sets a user's level by username or account id 25 | */ 26 | 27 | process.HEADLESS = true; 28 | 29 | var accountId = process.argv[2], 30 | level = process.argv[3], 31 | bootstrap = require(__dirname + '/../src/bootstrap'), 32 | dao = bootstrap.app.dao, 33 | modelName = 'account_auth', 34 | acctSearch = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(accountId), 35 | emailSearch = -1 !== accountId.indexOf('@'); 36 | 37 | function showUsage() { 38 | var levels = 'admin|user'; 39 | 40 | if (app.modules.permissions) { 41 | levels = Object.keys(GLOBAL.CFG.modules.permissions.config.plans).join('|') 42 | } 43 | 44 | console.log('Usage - token_get {account uuid|user_name|email address} (' + levels + ')'); 45 | process.exit(0); 46 | } 47 | 48 | if (!process.argv[2]) { 49 | showUsage(); 50 | } 51 | 52 | dao.on('ready', function(dao) { 53 | var levelOK = false; 54 | for (var k in GLOBAL.DEFS.ACCOUNT_LEVEL) { 55 | if (level === GLOBAL.DEFS.ACCOUNT_LEVEL[k]) { 56 | levelOK = true; 57 | break; 58 | } 59 | } 60 | 61 | if (!levelOK) { 62 | console.error('Unknown Account Level'); 63 | showUsage(); 64 | } else { 65 | 66 | if (acctSearch) { 67 | accountQuery(accountId); 68 | } else { 69 | var filter = {}; 70 | if (emailSearch) { 71 | filter.email_account = accountId; 72 | } else { 73 | filter.username = accountId; 74 | } 75 | 76 | dao.find( 77 | 'account', 78 | filter, 79 | function(err, result) { 80 | if (err || !result) { 81 | if (!result) { 82 | console.log('account id not found'); 83 | } 84 | process.exit(0); 85 | } else { 86 | dao.updateColumn('account', result.id, { account_level : level }, function() { 87 | if (err) { 88 | console.error(err); 89 | } else { 90 | console.log('Account Level ' + level + ' Set For ' + result.id); 91 | } 92 | process.exit(0); 93 | }); 94 | } 95 | } 96 | ); 97 | } 98 | } 99 | }); -------------------------------------------------------------------------------- /tools/test_jwt_token.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Creates a testable X-JWT-Signature header for BipIO 25 | */ 26 | 27 | var jwt = require('jsonwebtoken'), 28 | fs = require('fs'), 29 | path = require('path'), 30 | configPath, 31 | config, 32 | expireMinutes, 33 | options = {}, 34 | appEnv = process.env.NODE_ENV; 35 | 36 | if (appEnv === 'development' || !appEnv) { 37 | appEnv = 'default'; 38 | } 39 | 40 | configPath = path.resolve( 41 | process.env.NODE_CONFIG_DIR || path.join(__dirname, '../config/'), 42 | appEnv + '.json' 43 | ); 44 | 45 | config = JSON.parse(fs.readFileSync(configPath)); 46 | 47 | if (!process.argv[2]) { 48 | console.log('Usage - test_jwt_token payload (expiryMinutes)'); 49 | process.exit(0); 50 | } 51 | 52 | if (process.argv[3]) { 53 | options.expiresInMinutes = process.argv[3]; 54 | } 55 | 56 | console.log(jwt.sign(JSON.parse(process.argv[2]), config.jwtKey, options)); -------------------------------------------------------------------------------- /tools/token_get.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Gets a token by account uuid 25 | */ 26 | 27 | process.HEADLESS = true; 28 | if (!process.argv[2]) { 29 | console.log('Usage - token_get {account uuid|user_name}'); 30 | process.exit(0); 31 | } 32 | 33 | var accountId = process.argv[2], 34 | bootstrap = require(__dirname + '/../src/bootstrap'), 35 | dao = bootstrap.app.dao, 36 | modelName = 'account_auth', 37 | acctSearch = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(accountId), 38 | emailSearch = -1 !== accountId.indexOf('@'); 39 | 40 | function accountQuery(accountId, username) { 41 | dao.find( 42 | modelName, 43 | { 44 | owner_id : accountId, 45 | type : 'token' 46 | }, 47 | function(err, result) { 48 | if (err || !result) { 49 | console.log(err); 50 | if (!result) { 51 | console.log('account id not found'); 52 | } 53 | process.exit(0); 54 | } else { 55 | if (username) { 56 | console.log('Username : ' + username) 57 | } 58 | console.log('Token : ' + dao.modelFactory(modelName, result).getPassword()); 59 | process.exit(0); 60 | } 61 | } 62 | ); 63 | } 64 | 65 | dao.on('ready', function(dao) { 66 | if (acctSearch) { 67 | accountQuery(accountId); 68 | } else { 69 | var filter = {}; 70 | if (emailSearch) { 71 | filter.email_account = accountId; 72 | } else { 73 | filter.username = accountId; 74 | } 75 | 76 | dao.find( 77 | 'account', 78 | filter, 79 | function(err, result) { 80 | if (err || !result) { 81 | if (!result) { 82 | console.log('account id not found'); 83 | } 84 | process.exit(0); 85 | } else { 86 | accountQuery(result.id, result.username); 87 | } 88 | } 89 | ); 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /tools/token_regen.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Regenerates a token for an account id 25 | */ 26 | 27 | process.HEADLESS = true; 28 | if (!process.argv[2]) { 29 | console.log('Usage - token_regen {account uuid}'); 30 | process.exit(0); 31 | } 32 | 33 | var accountId = process.argv[2], 34 | crypto = require('crypto'), 35 | bootstrap = require(__dirname + '/../src/bootstrap'), 36 | dao = bootstrap.app.dao, 37 | modelName = 'account_auth'; 38 | 39 | dao.on('ready', function(dao) { 40 | dao.regenToken(accountId, function(err, token) { 41 | if (err) { 42 | console.log(err); 43 | console.log(result); 44 | } else { 45 | console.log('new token : ' + token) 46 | console.log('done'); 47 | process.exit(0); 48 | } 49 | }); 50 | }); -------------------------------------------------------------------------------- /tools/token_set.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Sets an explicit token for an account id 25 | */ 26 | 27 | process.HEADLESS = true; 28 | if (!process.argv[3]) { 29 | console.log('Usage - token_set {account uuid} {token}'); 30 | process.exit(0); 31 | } 32 | 33 | var accountId = process.argv[2], 34 | token = process.argv[3], 35 | bootstrap = require(__dirname + '/../src/bootstrap'), 36 | dao = bootstrap.app.dao, 37 | modelName = 'account_auth'; 38 | 39 | if (token.length > 32) { 40 | console.log('Token Must be 32 Bytes'); 41 | process.exit(0); 42 | } 43 | 44 | dao.on('ready', function(dao) { 45 | dao.find( 46 | modelName, 47 | { 48 | owner_id : accountId, 49 | type : 'token' 50 | }, 51 | function(err, result) { 52 | if (err || !result) { 53 | console.log(err); 54 | if (!result) { 55 | console.log('account id not found'); 56 | } 57 | process.exit(0); 58 | } else { 59 | result.password = token; 60 | 61 | dao.updateProperties( 62 | modelName, 63 | result.id, 64 | { 65 | password : token 66 | }, 67 | function(err, result) { 68 | if (err) { 69 | console.log(err); 70 | console.log(result); 71 | } else { 72 | console.log('new token : ' + token) 73 | console.log('done'); 74 | } 75 | process.exit(0); 76 | } 77 | ); 78 | } 79 | } 80 | ); 81 | }); 82 | -------------------------------------------------------------------------------- /tools/triage.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | var bootstrap = require(__dirname + '/../src/bootstrap'), 24 | dao = bootstrap.app.dao, 25 | fs = require('fs'); 26 | 27 | function getDisposition(obj) { 28 | var dispotion; 29 | if (obj.required) { 30 | disposition = obj.required.slice(0); 31 | } else { 32 | disposition = []; 33 | } 34 | 35 | for (var k in obj.properties) { 36 | if (obj.properties.hasOwnProperty(k)) { 37 | if (-1 === disposition.indexOf(k)) { 38 | disposition.push(k); 39 | } 40 | } 41 | } 42 | 43 | return disposition; 44 | 45 | } 46 | 47 | 48 | dao.on('ready', function(dao) { 49 | dao.describe('pod', null, function(err, modelName, results) { 50 | var mPath, 51 | mBody, 52 | s, ds, keys, 53 | action, 54 | podActual, 55 | disposition, 56 | initOAuthConfigs = [ 'clientID', 'clientSecret', 'consumerKey', 'consumerSecret' ]; 57 | 58 | // write manifest for each pod 59 | for (var podName in results) { 60 | if (results.hasOwnProperty(podName)) { 61 | // if ('syndication' === podName) { 62 | mPath = GLOBAL.SERVER_ROOT + '/../node_modules/bip-pod-' + podName + '/bpm.json'; 63 | 64 | s = results[podName]; 65 | podActual = dao.pod(podName); 66 | 67 | // cleanup 68 | for (r in s.renderers) { 69 | if (s.renderers.hasOwnProperty(r)) { 70 | s.renderers[r].title = s.renderers[r].description 71 | 72 | delete s.renderers[r]._href; 73 | delete s.renderers[r].description; 74 | } 75 | } 76 | 77 | mBody = { 78 | name : s.name, 79 | title : s.title, 80 | description : s.description, 81 | url : '', 82 | trackDuplicates : podActual._trackDuplicates, // inspect pod 83 | config : app._.clone(podActual.getConfig()), 84 | auth : { 85 | strategy : s.auth.type 86 | }, 87 | rpcs : s.renderers, 88 | actions : s.actions 89 | }; 90 | 91 | if (podActual._dataSources.length) { 92 | mBody.dataSources = {}; 93 | for (var i = 0; i < podActual._dataSources.length; i++) { 94 | ds = podActual._dataSources[i]; 95 | 96 | 97 | app._.each(ds.entitySchema, function(schema) { 98 | if (schema.type.Inflector) { 99 | schema.type = 'string'; 100 | } else if (/Number/.test(schema.type)) { 101 | schema.type = 'number'; 102 | 103 | } else if (/Mixed/.test(schema.type)) { 104 | schema.type = 'mixed'; 105 | 106 | } else if (/Object/.test(schema.type)) { 107 | schema.type = 'object'; 108 | 109 | } else if (/Array/.test(schema.type)) { 110 | schema.type = 'array'; 111 | 112 | } else if (/Boolean/.test(schema.type)) { 113 | schema.type = 'boolean'; 114 | 115 | } else { 116 | console.log(schema.type, schema.type instanceof String); 117 | process.exit(0); 118 | } 119 | }); 120 | 121 | mBody.dataSources[ds.entityName.replace('pod_' + s.name + '_', '')] = 122 | { 123 | properties : ds.entitySchema, 124 | keys : ds.compoundKeyConstraints ? Object.keys(ds.compoundKeyConstraints) : [ ds.entityIndex ] 125 | }; 126 | } 127 | } 128 | 129 | 130 | //console.log(s); 131 | // console.log(podActual._dataSources); 132 | //console.log(mBody.dataSources); 133 | //process.exit(0); 134 | 135 | if ('oauth' === s.auth.type) { 136 | if (mBody.config.oauth) { 137 | for (var o in mBody.config.oauth) { 138 | if (-1 !== initOAuthConfigs.indexOf(o)) { 139 | mBody.config.oauth[o] = ''; 140 | } 141 | } 142 | } 143 | 144 | } else if ('issuer_token' === s.auth.type) { 145 | mBody.auth.authMap = s.authMap 146 | } 147 | 148 | // cleanup actions 149 | delete mBody.auth.type; 150 | 151 | for (var a in s.actions) { 152 | if (s.actions.hasOwnProperty(a)) { 153 | action = s.actions[a]; 154 | 155 | if (action.trigger) { 156 | action.trigger = action.socket ? 'realtime' : 'poll'; 157 | } else { 158 | action.trigger = 'invoke'; 159 | } 160 | 161 | // config 162 | if (Object.keys(action.config.properties).length) { 163 | action.config.disposition = getDisposition(action.config); 164 | } 165 | delete action.config['$schema']; 166 | 167 | // imports 168 | if (Object.keys(action.imports.properties).length) { 169 | action.imports.disposition = getDisposition(action.imports); 170 | } 171 | delete action.imports['$schema']; 172 | 173 | // exports 174 | delete action.exports['$schema']; 175 | 176 | 177 | // deprecated attributes 178 | delete action.singleton; 179 | delete action.auto; 180 | delete action.defaults; 181 | delete action.socket; 182 | delete action.auth_required; 183 | 184 | if (Object.keys(action.renderers).length) { 185 | action.rpcs = app._.clone(action.renderers); 186 | // normalize rpc attributes 187 | app._.each(action.rpcs, function(rpc) { 188 | rpc.title = rpc.description; 189 | rpc.description = rpc.description_long; 190 | delete rpc.description_long; 191 | }); 192 | } 193 | 194 | delete action.renderers; 195 | 196 | } 197 | } 198 | 199 | console.log('----------------'); 200 | console.log('wrote ' + mPath); 201 | // console.log(mBody); 202 | // console.log(mBody.actions.send); 203 | 204 | fs.writeFileSync(mPath , JSON.stringify(mBody, null, 2)); 205 | 206 | } 207 | } 208 | 209 | process.exit(0); 210 | }); 211 | }); 212 | -------------------------------------------------------------------------------- /tools/update-transforms.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * The Bipio API Server 5 | * 6 | * Copyright (c) 2017 InterDigital, Inc. All Rights Reserved 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | * 21 | * A Bipio Commercial OEM License may be obtained via hello@bip.io 22 | */ 23 | /** 24 | * Updates System Transforms. 25 | */ 26 | process.HEADLESS = true; 27 | var bootstrap = require(__dirname + '/../src/bootstrap'); 28 | bootstrap.app.dao.on('ready', function(read) { 29 | // if (GLOBAL.CFG.updateTransforms) { 30 | app.logmessage('DAO:Updating System Transforms'); 31 | bootstrap.app.dao.reduceTransformDefaults(function(err, msg) { 32 | if (err) { 33 | app.logmessage('DAO:' + err + ' ' + msg); 34 | } else { 35 | app.logmessage('DAO:Updating System Transforms:Done'); 36 | } 37 | process.exit(0); 38 | }); 39 | // } 40 | }); 41 | --------------------------------------------------------------------------------