<commit_before>// ==========================================================================
//                            DG.UserEntryDialog
//
//  Copyright (c) 2015 by The Concord Consortium, Inc. All rights reserved.
//
//  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
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  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.
// ==========================================================================

/** @class

  Dialog showing activity choices for when there is no current document.

  @extends SC.PanelPane
*/
DG.UserEntryDialog = SC.PanelPane.extend({
  layout: { width: 900, height: 310, centerX: 0, centerY: 0},
  contentView: SC.View.design({
    layout: { top: 0, right: 0, left: 0, bottom: 0, zIndex: 0 },
    childViews: 'welcomeHeader welcomeInstructions choiceViews choiceButtons'.w(),

    welcomeHeader: SC.LabelView.design({
      layout: { left: 10, right: 0, top: 10, height: 24 },
      controlSize: SC.LARGE_CONTROL_SIZE,
      fontWeight: SC.BOLD_WEIGHT,
      textAlign: SC.ALIGN_CENTER,
      localize: true,
      value: 'DG.UserEntryDialog.welcome'
    }),

    welcomeInstructions: SC.LabelView.design({
      layout: { left: 10, right: 0, top: 49, height: 18 },
      textAlign: SC.ALIGN_LEFT,
      localize: true,
      value: 'DG.UserEntryDialog.welcome2'
    }),

    choiceButtons: SC.SegmentedView.design({
      layout: { left: 10, top: 82, height: 72*3, width: 250 },
      controlSize: SC.JUMBO_CONTROL_SIZE,
      layoutDirection: SC.LAYOUT_VERTICAL,
      init: function() {
        sc_super();
        this.set('value', ['new']);
      },
      itemTitleKey: 'title',
      itemValueKey: 'value',
      isSaveEnabledBinding: SC.Binding.oneWay('DG.authorizationController.isSaveEnabled'),
      items: function() {
        var items = [
          { title: 'DG.UserEntryDialog.openNew.option'.loc(), value: 'new' },
          { title: 'DG.UserEntryDialog.openExample.option'.loc(), value: 'example' },
          { title: 'DG.UserEntryDialog.openFile.option'.loc(), value: 'file' }
        ];
        if (DG.authorizationController.get('isSaveEnabled')) {
          items.push({ title: 'DG.UserEntryDialog.documentServer.option'.loc(), value: 'cloud' });
        }
        return items;
      }.property('isSaveEnabled'),
      value: null,
      valueChanged: function() {
        var val = this.get('value');
        switch(val) {
          case 'cloud':
            this.setPath('parentView.choiceViews.nowShowing', 'cloudBrowseView');
            break;
          case 'file':
            this.setPath('parentView.choiceViews.nowShowing', 'openFileView');
            break;
          case 'example':
            this.setPath('parentView.choiceViews.nowShowing', 'openExampleView');
            break;
          case 'new':
            this.setPath('parentView.choiceViews.nowShowing', 'openNewView');
            break;
          default:
            this.setPath('parentView.choiceViews.nowShowing', 'openNewView');
        }
      }.observes('value')
    }),

    choiceViews: SC.WellView.design({
      layout: { left: 265, right: 10, top: 82, height: 72*3, zIndex: 5 },
      init: function() {
        sc_super();
        this.set('nowShowing', 'openNewView');
      },
      openNewView: SC.View.design({
        layout: {top: 0, left: 0, right: 0, bottom: 0},
        childViews: 'titleLabel titleField okButton'.w(),

        titleLabel: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openNew.prompt'
        }),

        titleField: SC.TextFieldView.design({
          layout: { top: 40, left: 5, right: 5, height:24 },
          localize: true,
          hint: 'DG.UserEntryDialog.openNew.titleFieldHint'
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          localize: true,
          title: 'DG.UserEntryDialog.openNew.button',
          toolTip: 'DG.UserEntryDialog.openNew.buttonTooltip',
          target: 'DG.userEntryController',
          action: 'openNewDocument',
          isDefault: true
        })
      }),

      openFileView: SC.LabelView.design({
        layout: {top: 0, left: 0, right: 0, bottom: 0},
        childViews: 'promptView editView alertView okButton'.w(),
        promptView: SC.LabelView.extend({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openFile.prompt'
        }),
        editView: DG.FileImportView.design({
          layout: { top: 40, left: 5, right: 5, height:24 },
          value: '',
          isTextArea: false,
          spellCheckEnabled: false,
          action: function () {
            this.get('parentView').hideAlert();
          }
        }),
        // alertView: Initially invisible, only exposed if invalid file
        // uploaded
        alertView: SC.TextFieldView.extend({
          layout: { top: 70, left: 5, right: 5, height:48 },
          classNames: ['dg-alert'],
          localize: true,
          isVisible: NO,
          isTextArea: YES,
          value: 'DG.AppController.importDocument.alert'
        }),
        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.UserEntryDialog.openFile.button',  // "OK"
          toolTip: 'DG.UserEntryDialog.openFile.buttonTooltip',
          target: 'DG.userEntryController',
          action: 'openFile',
          isDefault: true
        }),

        /**
         * Show an alert message. If an error is passed will show its (assumed
         * localized) message. If not will display the default.
         *
         * @param error {Error} a Javascript Error object
         */
        showAlert: function (error) {
          if (error) {
            this.setPath('alertView.value', error);
          }
          this.setPath('alertView.isVisible', YES);
        },

        hideAlert: function () {
          this.setPath('alertView.isVisible', NO);
        },

        value: function() {
          return this.getPath('editView.files');
        }.property(),

        close: function() {
          // NOOP. Implemented to mimic the expected dialog API used in DG.appController.importFileWithConfirmation
        }
      }),

      openExampleView: SC.View.design({
        layout: { left: 0, right: 0, top: 0, bottom: 0 },
        childViews: 'promptView exampleListView okButton'.w(),

        promptView: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openExample.prompt'   // "Choose an example to open:"
        }),

        exampleListView: SC.ScrollView.extend({
          layout: { top: 40, left: 5, right: 5, height: 115 },
          contentView: SC.ListView.extend({
            layout: {top: 0, left: 0, right: 0, bottom: 0 },
            rowHeight: 19,
            rowSpacing: 4,
            init: function() {
              sc_super();
              this.set('content', DG.ExampleListController.create({ allowsMultipleSelection: NO }));
            },
            exampleView: SC.View.extend(SC.ContentDisplay, {
              classNameBindings: ['status'],
              contentDisplayProperties: 'name description'.w(),
              childViews: 'nameView descriptionView'.w(),
              status: function() {
                var isSelected = this.get('isSelected'),
                    isEnabled = this.get('isEnabled');

                if (isEnabled && isSelected) {
                  return 'sel';
                } else if (!isEnabled) {
                  return 'disabled';
                } else {
                  return '';
                }
              }.property('isSelected', 'isEnabled'),

              nameView: SC.LabelView.extend({
                layout: { left: 3, width: 145, top: 0, bottom: 0 },
                classNames: ["dg-example-name"],
                valueBinding: SC.Binding.oneWay('*parentView.content.name')
              }),

              descriptionView: SC.LabelView.extend({
                layout: { left: 152, right: 3, top: 0, bottom: 0 },
                controlSize: SC.SMALL_CONTROL_SIZE,
                valueBinding: SC.Binding.oneWay('*parentView.content.description')
              })
            })
          })
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.UserEntryDialog.openExample.okTitle',  // "Open"
          target: 'DG.userEntryController',
          action: 'openExample',
          toolTip: 'DG.UserEntryDialog.openExample.okTooltip',  // "Open the specified example",
          isDefault: true
        }),

        selectedBinding: SC.Binding.oneWay('*exampleListView.contentView.selection.firstObject')
      }),

      cloudBrowseView: SC.View.design({
        layout: { left: 0, right: 0, top: 0, bottom: 0 },
        childViews: 'promptView documentListView okButton'.w(),

        promptView: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.AppController.openDocument.prompt'   // "Choose a document
        }),

        documentListView: SC.SelectView.extend({
          displayProperties: ['items.content'],
          layout: {width: 150, height: 24, centerX: 0, centerY: 0},
          itemTitleKey: 'name',
          itemValueKey: 'id',
          emptyName: 'DG.UserEntryDialog.openFile.prompt', // "Select a document to open..."
          //itemIconKey: 'icon',
            init: function() {
              sc_super();
            this.set('items',
              DG.DocumentListController.create({allowsMultipleSelection: NO}));
          }
          //classNames: ['my-scroll-view'],
          //layout: { top: 40, left: 5, right: 5, height: 113 },
          //hasHorizontalScroller: false,
          //contentView: SC.ListView.extend({
          //  layout: {top: 0, left: 0, right: 0, bottom: 0 },
          //  classNames: ['my-list-view'],
          //  rowHeight: 24,
          //  contentValueKey: 'name',
          //  init: function() {
          //    sc_super();
          //    this.set('content', DG.DocumentListController.create({ allowsMultipleSelection: NO }));
          //  },
          //  fieldValue: function() {
          //    return this.getPath('selection.firstObject.id');
          //  }.property('selection')
          //})
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.AppController.openDocument.okTitle',  // "Open"
          target: 'DG.userEntryController',
          action: 'openExistingDocument',
          toolTip: 'DG.AppController.openDocument.okTooltip',  // "Open",
          isDefault: true
        }),

        documentName: function() {
          var docList = this.getPath('documentListView.items.content'), i, docCount = docList &&
              docList.get('length'), docID = this.getPath('documentListView.value'), docEntry;
          for( i = 0; i < docCount; ++i) {
            docEntry = docList.objectAt( i);
            if( docEntry.id === docID)
              return docEntry.name;
          }
          return null;
        }.property('*documentListView.value'),

        documentID: function() {
          return this.getPath('documentListView.value');
        }.property('*documentListView.contentView.fieldValue'),

        close: function() {
          // NOOP. Implemented to mimic the expected dialog API used in DG.appController.openDocumentFromDialog
        }
      })
    }),

    isAppended: false,
    didAppendToDocument: function() {
      this.set('isAppended', true);
    },

    willRemoveFromDocument: function() {
      this.set('isAppended', false);
    }
  })
});

<commit_msg>Added classNames to user entry buttons<commit_after>// ==========================================================================
//                            DG.UserEntryDialog
//
//  Copyright (c) 2015 by The Concord Consortium, Inc. All rights reserved.
//
//  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
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  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.
// ==========================================================================

/** @class

  Dialog showing activity choices for when there is no current document.

  @extends SC.PanelPane
*/
DG.UserEntryDialog = SC.PanelPane.extend({
  layout: { width: 900, height: 310, centerX: 0, centerY: 0},
  contentView: SC.View.design({
    layout: { top: 0, right: 0, left: 0, bottom: 0, zIndex: 0 },
    childViews: 'welcomeHeader welcomeInstructions choiceViews choiceButtons'.w(),

    welcomeHeader: SC.LabelView.design({
      layout: { left: 10, right: 0, top: 10, height: 24 },
      controlSize: SC.LARGE_CONTROL_SIZE,
      fontWeight: SC.BOLD_WEIGHT,
      textAlign: SC.ALIGN_CENTER,
      localize: true,
      value: 'DG.UserEntryDialog.welcome'
    }),

    welcomeInstructions: SC.LabelView.design({
      layout: { left: 10, right: 0, top: 49, height: 18 },
      textAlign: SC.ALIGN_LEFT,
      localize: true,
      value: 'DG.UserEntryDialog.welcome2'
    }),

    choiceButtons: SC.SegmentedView.design({
      layout: { left: 10, top: 82, height: 72*3, width: 250 },
      controlSize: SC.JUMBO_CONTROL_SIZE,
      layoutDirection: SC.LAYOUT_VERTICAL,
      init: function() {
        sc_super();
        this.set('value', ['new']);
      },
      itemTitleKey: 'title',
      itemValueKey: 'value',
      itemLayerIdKey: 'id',
      isSaveEnabledBinding: SC.Binding.oneWay('DG.authorizationController.isSaveEnabled'),
      items: function() {
        var items = [
          { title: 'DG.UserEntryDialog.openNew.option'.loc(), value: 'new', id: 'dg-user-entry-new-doc-button' },
          { title: 'DG.UserEntryDialog.openExample.option'.loc(), value: 'example', id: 'dg-user-entry-example-button' },
          { title: 'DG.UserEntryDialog.openFile.option'.loc(), value: 'file', id: 'dg-user-entry-open-local-doc-button' }
        ];
        if (DG.authorizationController.get('isSaveEnabled')) {
          items.push({ title: 'DG.UserEntryDialog.documentServer.option'.loc(), value: 'cloud', id: 'dg-user-entry-open-cloud-document' });
        }
        return items;
      }.property('isSaveEnabled'),
      value: null,
      valueChanged: function() {
        var val = this.get('value');
        switch(val) {
          case 'cloud':
            this.setPath('parentView.choiceViews.nowShowing', 'cloudBrowseView');
            break;
          case 'file':
            this.setPath('parentView.choiceViews.nowShowing', 'openFileView');
            break;
          case 'example':
            this.setPath('parentView.choiceViews.nowShowing', 'openExampleView');
            break;
          case 'new':
            this.setPath('parentView.choiceViews.nowShowing', 'openNewView');
            break;
          default:
            this.setPath('parentView.choiceViews.nowShowing', 'openNewView');
        }
      }.observes('value')
    }),

    choiceViews: SC.WellView.design({
      layout: { left: 265, right: 10, top: 82, height: 72*3, zIndex: 5 },
      init: function() {
        sc_super();
        this.set('nowShowing', 'openNewView');
      },
      openNewView: SC.View.design({
        layout: {top: 0, left: 0, right: 0, bottom: 0},
        childViews: 'titleLabel titleField okButton'.w(),

        titleLabel: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openNew.prompt'
        }),

        titleField: SC.TextFieldView.design({
          layout: { top: 40, left: 5, right: 5, height:24 },
          localize: true,
          hint: 'DG.UserEntryDialog.openNew.titleFieldHint',
          classNames: ['dg-user-entry-new-doc-title-field']
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          localize: true,
          title: 'DG.UserEntryDialog.openNew.button',
          toolTip: 'DG.UserEntryDialog.openNew.buttonTooltip',
          target: 'DG.userEntryController',
          action: 'openNewDocument',
          isDefault: true,
          classnames:['dg-ok-new-doc-button']
        })
      }),

      openFileView: SC.LabelView.design({
        layout: {top: 0, left: 0, right: 0, bottom: 0},
        childViews: 'promptView editView alertView okButton'.w(),
        promptView: SC.LabelView.extend({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openFile.prompt'
        }),
        editView: DG.FileImportView.design({
          layout: { top: 40, left: 5, right: 5, height:24 },
          value: '',
          isTextArea: false,
          spellCheckEnabled: false,
          action: function () {
            this.get('parentView').hideAlert();
          }
        }),
        // alertView: Initially invisible, only exposed if invalid file
        // uploaded
        alertView: SC.TextFieldView.extend({
          layout: { top: 70, left: 5, right: 5, height:48 },
          classNames: ['dg-alert'],
          localize: true,
          isVisible: NO,
          isTextArea: YES,
          value: 'DG.AppController.importDocument.alert'
        }),
        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.UserEntryDialog.openFile.button',  // "OK"
          toolTip: 'DG.UserEntryDialog.openFile.buttonTooltip',
          target: 'DG.userEntryController',
          action: 'openFile',
          isDefault: true,
          classNames: ['dg-ok-open-local-button']
        }),

        /**
         * Show an alert message. If an error is passed will show its (assumed
         * localized) message. If not will display the default.
         *
         * @param error {Error} a Javascript Error object
         */
        showAlert: function (error) {
          if (error) {
            this.setPath('alertView.value', error);
          }
          this.setPath('alertView.isVisible', YES);
        },

        hideAlert: function () {
          this.setPath('alertView.isVisible', NO);
        },

        value: function() {
          return this.getPath('editView.files');
        }.property(),

        close: function() {
          // NOOP. Implemented to mimic the expected dialog API used in DG.appController.importFileWithConfirmation
        }
      }),

      openExampleView: SC.View.design({
        layout: { left: 0, right: 0, top: 0, bottom: 0 },
        childViews: 'promptView exampleListView okButton'.w(),

        promptView: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.UserEntryDialog.openExample.prompt'   // "Choose an example to open:"
        }),

        exampleListView: SC.ScrollView.extend({
          layout: { top: 40, left: 5, right: 5, height: 115 },
          contentView: SC.ListView.extend({
            layout: {top: 0, left: 0, right: 0, bottom: 0 },
            rowHeight: 19,
            rowSpacing: 4,
            init: function() {
              sc_super();
              this.set('content', DG.ExampleListController.create({ allowsMultipleSelection: NO }));
            },
            exampleView: SC.View.extend(SC.ContentDisplay, {
              classNameBindings: ['status'],
              contentDisplayProperties: 'name description'.w(),
              childViews: 'nameView descriptionView'.w(),
              status: function() {
                var isSelected = this.get('isSelected'),
                    isEnabled = this.get('isEnabled');

                if (isEnabled && isSelected) {
                  return 'sel';
                } else if (!isEnabled) {
                  return 'disabled';
                } else {
                  return '';
                }
              }.property('isSelected', 'isEnabled'),

              nameView: SC.LabelView.extend({
                layout: { left: 3, width: 145, top: 0, bottom: 0 },
                classNames: ["dg-example-name"],
                valueBinding: SC.Binding.oneWay('*parentView.content.name')
              }),

              descriptionView: SC.LabelView.extend({
                layout: { left: 152, right: 3, top: 0, bottom: 0 },
                controlSize: SC.SMALL_CONTROL_SIZE,
                valueBinding: SC.Binding.oneWay('*parentView.content.description')
              })
            })
          })
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.UserEntryDialog.openExample.okTitle',  // "Open"
          target: 'DG.userEntryController',
          action: 'openExample',
          toolTip: 'DG.UserEntryDialog.openExample.okTooltip',  // "Open the specified example",
          isDefault: true,
          classNames:['dg-example-ok-button']
        }),

        selectedBinding: SC.Binding.oneWay('*exampleListView.contentView.selection.firstObject')
      }),

      cloudBrowseView: SC.View.design({
        layout: { left: 0, right: 0, top: 0, bottom: 0 },
        childViews: 'promptView documentListView okButton'.w(),

        promptView: SC.LabelView.design({
          layout: { top: 10, left: 5, right: 5, height:24 },
          localize: true,
          value: 'DG.AppController.openDocument.prompt'   // "Choose a document
        }),

        documentListView: SC.SelectView.extend({
          displayProperties: ['items.content'],
          layout: {width: 150, height: 24, centerX: 0, centerY: 0},
          itemTitleKey: 'name',
          itemValueKey: 'id',
          emptyName: 'DG.UserEntryDialog.openFile.prompt', // "Select a document to open..."
          //itemIconKey: 'icon',
            init: function() {
              sc_super();
            this.set('items',
              DG.DocumentListController.create({allowsMultipleSelection: NO}));
          }
          //classNames: ['my-scroll-view'],
          //layout: { top: 40, left: 5, right: 5, height: 113 },
          //hasHorizontalScroller: false,
          //contentView: SC.ListView.extend({
          //  layout: {top: 0, left: 0, right: 0, bottom: 0 },
          //  classNames: ['my-list-view'],
          //  rowHeight: 24,
          //  contentValueKey: 'name',
          //  init: function() {
          //    sc_super();
          //    this.set('content', DG.DocumentListController.create({ allowsMultipleSelection: NO }));
          //  },
          //  fieldValue: function() {
          //    return this.getPath('selection.firstObject.id');
          //  }.property('selection')
          //})
        }),

        okButton: SC.ButtonView.design({
          layout: { bottom:5, right: 5, height:24, width: 90 },
          titleMinWidth: 0,
          localize: true,
          title: 'DG.AppController.openDocument.okTitle',  // "Open"
          target: 'DG.userEntryController',
          action: 'openExistingDocument',
          toolTip: 'DG.AppController.openDocument.okTooltip',  // "Open",
          isDefault: true,
          classNames: ['dg-ok-open-cloud-doc-button']
        }),

        documentName: function() {
          var docList = this.getPath('documentListView.items.content'), i, docCount = docList &&
              docList.get('length'), docID = this.getPath('documentListView.value'), docEntry;
          for( i = 0; i < docCount; ++i) {
            docEntry = docList.objectAt( i);
            if( docEntry.id === docID)
              return docEntry.name;
          }
          return null;
        }.property('*documentListView.value'),

        documentID: function() {
          return this.getPath('documentListView.value');
        }.property('*documentListView.contentView.fieldValue'),

        close: function() {
          // NOOP. Implemented to mimic the expected dialog API used in DG.appController.openDocumentFromDialog
        }
      })
    }),

    isAppended: false,
    didAppendToDocument: function() {
      this.set('isAppended', true);
    },

    willRemoveFromDocument: function() {
      this.set('isAppended', false);
    }
  })
});

<|endoftext|>


<commit_before>var express = require('express');
var app = module.exports = express();
var configure = require('./config/env');
var mongoose = require('mongoose');
var Comment = require('./models/comment');

configure(app);

var ensureRequestComesFromRightDomain = function(req, res, next) {
  if (req.hostname === req.param('domain')) {
    return next();
  }
  if (req.hostname === '127.0.0.1') {
    return next();
  }
  res.send(403);
};

app.get('/', function(req, res) {
  res.send('Hello World');
});

app.get('/db', function(req, res) {
  res.send((mongoose.connection.readyState === 1) ? 200 : 503);
});

app.get('/:domain/:reference/comments', ensureRequestComesFromRightDomain, function(req, res) {
  Comment.find(
    {domain: req.param('domain'), reference: req.param('reference')},
    function(err, comments) {
      if (err) {
        return res.send(500);
      }
      res.status(200).json(comments);
    }
  );
});

app.post(
  '/:domain/:reference/comments',
  ensureRequestComesFromRightDomain,
  require('body-parser').json(),
  require('body-parser').urlencoded(),
  function(req, res) {
    Comment.create(
      { domain: req.param('domain'),
        reference: req.param('reference'),
        author: req.body.author,
        text: req.body.text,
      },
      function(err, comment) {
        if (err) {
          if (err.name === 'ValidationError') {
            return res.status(400).json(err.errors);
          }
          return res.send(500);
        }
        res
          .status(201)
          .location(comment.location())
          .json(comment);
      }
    );
  }
);

app.use(express.static(__dirname +
  (process.env.NODE_ENV === 'dist' ? '/../client-dist' : '/../client')
));

if (require.main === module) {
  mongoose.connect(app.get('db'), function() {
    var server = app.listen(3000, function() {
      console.log('Listening on port %d', server.address().port);
    });
  });
}
<commit_msg>Remove deprecation warning from body-parser<commit_after>var express = require('express');
var app = module.exports = express();
var configure = require('./config/env');
var mongoose = require('mongoose');
var Comment = require('./models/comment');

configure(app);

var ensureRequestComesFromRightDomain = function(req, res, next) {
  if (req.hostname === req.param('domain')) {
    return next();
  }
  if (req.hostname === '127.0.0.1') {
    return next();
  }
  res.send(403);
};

app.get('/', function(req, res) {
  res.send('Hello World');
});

app.get('/db', function(req, res) {
  res.send((mongoose.connection.readyState === 1) ? 200 : 503);
});

app.get('/:domain/:reference/comments', ensureRequestComesFromRightDomain, function(req, res) {
  Comment.find(
    {domain: req.param('domain'), reference: req.param('reference')},
    function(err, comments) {
      if (err) {
        return res.send(500);
      }
      res.status(200).json(comments);
    }
  );
});

app.post(
  '/:domain/:reference/comments',
  ensureRequestComesFromRightDomain,
  require('body-parser').json(),
  require('body-parser').urlencoded({extended: false}),
  function(req, res) {
    Comment.create(
      { domain: req.param('domain'),
        reference: req.param('reference'),
        author: req.body.author,
        text: req.body.text,
      },
      function(err, comment) {
        if (err) {
          if (err.name === 'ValidationError') {
            return res.status(400).json(err.errors);
          }
          return res.send(500);
        }
        res
          .status(201)
          .location(comment.location())
          .json(comment);
      }
    );
  }
);

app.use(express.static(__dirname +
  (process.env.NODE_ENV === 'dist' ? '/../client-dist' : '/../client')
));

if (require.main === module) {
  mongoose.connect(app.get('db'), function() {
    var server = app.listen(3000, function() {
      console.log('Listening on port %d', server.address().port);
    });
  });
}
<|endoftext|>


<commit_before>/**
 * Created by Abhinav on 05-03-2017.
 */
var express = require('express');
var app = express();
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var type = require('type-of-is');
var base64url = require('base64-url');
var historyid;
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://mail.google.com'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
var HISTORY_PATH = TOKEN_DIR + 'History_id.txt';

app.listen(3000);

app.get('/users/google/callback', function(req, res) {
    console.log(req.query.code);
    res.end("boo");
});
// Load client secrets from a local file.
authorize(start);

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(callback) {
    var clientSecret = "KZaOO-GM-cRN0mOsM8xx_78e";
    var clientId = "52910464016-fnip280jvncgt7ljh437btian1e8eu0f.apps.googleusercontent.com";
    var redirectUrl ="http://localhost:3000/users/google/callback";

    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    google.options({
        auth: oauth2Client
    });

    // Check if we have previously stored a token.
    fs.readFile(TOKEN_PATH, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            callback(oauth2Client);
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */

function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: SCOPES
    });
    console.log('Authorize this app by visiting this url: ', authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    rl.question('Enter the code from that page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                console.log('Error while trying to retrieve access token', err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token);
            callback(oauth2Client);
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 */
function storeToken(token) {
    try {
        fs.mkdirSync(TOKEN_DIR);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(TOKEN_PATH, JSON.stringify(token));
    console.log('Token stored to ' + TOKEN_PATH);
}
function storehistoryid(token) {
    try {
        fs.mkdirSync(HISTORY_PATH);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(HISTORY_PATH, JSON.stringify(token));
    console.log('Token stored to ' + HISTORY_PATH);
}

function start(auth){
    var gmail = google.gmail('v1');
    fs.readFile(HISTORY_PATH,function(err, hId) {
        if (err) {
            console.log(err);
        } else {
            historyid=JSON.parse(hId);

            list(auth);
        }
    });

}

function list(auth){console.log(historyid);
    var gmail = google.gmail('v1');
    gmail.users.history.list({
        auth: auth,
        userId: 'me',
        startHistoryId: historyid,
        historyTypes:'messageAdded',
        labelId:'INBOX'

    },  function (err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
            /* auth.refreshAccessToken(function(err, tokens) {
             storeToken(tokens)
             });*/
        }
        var msg="";
        try
        {var id = response.history;
            historyid=response.historyId;
            storehistoryid(historyid);
            console.log(response);
            console.log(id.length);
            for(var i=0;id[i];i++){
                // console.log(id[i].messages[0].id);

                getmessage(id[i].messages[0].id,auth,gmail);
                if(i==id.length-1){
                    setTimeout(function (auth){
                        list(auth);
                    },5000);}
            }
        }
        catch(err) {console.log("no new messages");
            setTimeout(function (auth){
                list(auth);
            },5000);}
    });

}

function getmessage(id,auth,gmail) {
    gmail.users.messages.get({
        auth: auth,
        userId: 'me',
        id: id,
        format:'metadata',
        metadataHeaders: 'From'
    },function (err,response){

        var mailto=response.payload.headers[0].value;
        var string="To:"+mailto +"\n" +
            "Subject: hello\n";

        var r=base64url.escape(base64url.encode(string));
        console.log(r);
        gmail.users.messages.send({
            auth: auth,
            userId: 'me',
            resource: {raw:r}}
        )
    })
    return;
}
/**
 * Lists the labels in the user's account.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function listLabels1(auth) {
    var gmail = google.gmail('v1');
    gmail.users.messages.list({
        auth: auth,
        userId: 'me',
        maxResults: 5,
        labelIds: "INBOX"

    }, function (err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
            return;
        }
        var id = response.messages;
        {
            console.log(id);
            getmessage(id[0].id,auth,gmail);
        }
    });
}

<commit_msg>Revert "Update app.js"<commit_after>/**
 * Created by Abhinav on 05-03-2017.
 */
var express = require('express');
var app = express();
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var type = require('type-of-is');
var base64url = require('base64-url');
var historyid;
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://mail.google.com'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
var HISTORY_PATH = TOKEN_DIR + 'History_id.txt';

app.listen(3000);

app.get('/users/google/callback', function(req, res) {
    console.log(req.query.code);
    res.end("boo");
});
// Load client secrets from a local file.
authorize(start);

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(callback) {
    var clientSecret = "YOUR CLIENT SECRET";
    var clientId = "YOUR CLIENT ID";
    var redirectUrl ="YOUR REDIRECT URL";

    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    google.options({
        auth: oauth2Client
    });

    // Check if we have previously stored a token.
    fs.readFile(TOKEN_PATH, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            callback(oauth2Client);
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */

function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: SCOPES
    });
    console.log('Authorize this app by visiting this url: ', authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    rl.question('Enter the code from that page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                console.log('Error while trying to retrieve access token', err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token);
            callback(oauth2Client);
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 */
function storeToken(token) {
    try {
        fs.mkdirSync(TOKEN_DIR);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(TOKEN_PATH, JSON.stringify(token));
    console.log('Token stored to ' + TOKEN_PATH);
}
function storehistoryid(token) {
    try {
        fs.mkdirSync(HISTORY_PATH);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(HISTORY_PATH, JSON.stringify(token));
    console.log('Token stored to ' + HISTORY_PATH);
}

function start(auth){
    var gmail = google.gmail('v1');
    fs.readFile(HISTORY_PATH,function(err, hId) {
        if (err) {
            console.log(err);
        } else {
            historyid=JSON.parse(hId);

            list(auth);
        }
    });

}

function list(auth){console.log(historyid);
    var gmail = google.gmail('v1');
    gmail.users.history.list({
        auth: auth,
        userId: 'me',
        startHistoryId: historyid,
        historyTypes:'messageAdded',
        labelId:'INBOX'

    },  function (err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
          /* auth.refreshAccessToken(function(err, tokens) {
           storeToken(tokens)
           });*/
        }
        var id = response.history;
        var newhistoryid=response.historyId;
        storehistoryid(newhistoryid);
        console.log(response);
        var msg="";
        try
        {
            for(var i=0;id[i];i++){
                // console.log(id[i].messages[0].id);

                getmessage(id[i].messages[0].id,auth,gmail);
            }
        }
        catch(err) {console.log("no new messages");
            return;}
    });

}

function getmessage(id,auth,gmail) {
    gmail.users.messages.get({
        auth: auth,
        userId: 'me',
        id: id,
        format:'metadata',
        metadataHeaders: 'From'
    },function (err,response){

        var mailto=response.payload.headers[0].value;
        var string="To:"+mailto +"\n" +
            "Subject: hello";
        var r=base64url.escape(base64url.encode(string));
        console.log(r);
        gmail.users.messages.send({
            auth: auth,
            userId: 'me',
            resource: {raw:r}}
        )
    })

}
/**
 * Lists the labels in the user's account.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function listLabels1(auth) {
    var gmail = google.gmail('v1');
    gmail.users.messages.list({
        auth: auth,
        userId: 'me',
        maxResults: 5,
        labelIds: "INBOX"

    }, function (err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
            return;
        }
        var id = response.messages;
        {
            console.log(id);
            getmessage(id[0].id,auth,gmail);
        }
    });
}

<|endoftext|>


<commit_before>import FacebookStrategy from 'passport-facebook';
import config from '../config';
import db from './db';
import express from 'express';
import historyApiFallback from 'connect-history-api-fallback';
import passport from 'passport';
import redisStoreFactory from 'connect-redis';
import session from 'express-session';

const RedisStore = redisStoreFactory(session);
const app = express();
const debug = require('debug')('app:server');
const paths = config.utils_paths;

class Queries {
  static findUser(id) {
    return db.oneOrNone('SELECT * FROM users WHERE id = $1', id);
  }

  static findUserFromFacebookId(facebookId) {
    return db.oneOrNone('SELECT * FROM users WHERE facebook_id = $1', facebookId);
  }

  static createUser({ facebookId, facebookAccessToken, name, emailAddress }) {
    return db.one(
      `INSERT INTO users (id, facebook_id, facebook_access_token, name, email_address)
      VALUES (
        uuid_generate_v4(),
        $/facebookId/,
        $/facebookAccessToken/,
        $/name/,
        $/emailAddress/
      ) RETURNING *`,
      { facebookId, facebookAccessToken, name, emailAddress }
    );
  }
}

passport.serializeUser((user, done) => {
  done(null, user);
});

passport.deserializeUser((user, done) => {
  done(null, user);
});

passport.use('facebook',
  new FacebookStrategy({
    clientID: config.fb.app_id,
    clientSecret: config.fb.app_secret,
    callbackURL: config.fb.callback_url,
  },
  // Facebook will send back the tokens and Facebook profile
  (accessToken, refreshToken, profile, done) => {
    // Find the user in the database based on their Facebook ID
    const {
      id: facebookId,
      displayName: name,
    } = profile;

    Queries.findUserFromFacebookId(facebookId)
      .catch(err => done(err))
      .then(user => {
        // If the user is found, then log them in
        if (user) {
          return done(null, user); // user found, return that user
        }

        // Save our user to the database
        const emailAddress =
          profile.emails &&
          profile.emails[0] &&
          profile.emails[0].value;

        const newUser = {
          facebookId,
          facebookAccessToken: accessToken,
          name,
          emailAddress,
        };

        Queries.createUser(newUser)
          .catch(err => done(err))
          .then(savedUser => done(null, savedUser));
      });
  })
);

app.use(session({
  resave: false,
  saveUninitialized: false,
  secret: 'fancy feast',
  store: new RedisStore(),
}));
// Initialize Passport!  Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());

app.get('/auth/login/facebook',
  passport.authenticate('facebook', { scope: 'email' })
);

app.get('/auth/login/facebook/callback',
  passport.authenticate('facebook', { failureRedirect: '/login' }),
  (req, res) => res.redirect('/home')
);

app.get('/auth/logout', (req, res) => {
  req.logout();
  res.redirect('/');
});

// API

app.get('/api/users/:id', (req, res) => {
  Queries.findUser(req.params.id)
    .catch(error => res.send(error))
    .then(data => {
      // Sanitize user data to prevent leaking secrets
      const user = {
        id: data.id,
        name: data.name,
      };
      if (req.user && req.user.id === user.id) {
        user.email_address = data.email_address;
      }

      res.send(user);
    });
});

// END API

app.use(historyApiFallback({
  verbose: false,
}));

// Serve app with Webpack if HMR is enabled
if (config.compiler_enable_hmr) {
  const webpack = require('webpack');
  const webpackConfig = require('../build/webpack.config');
  const compiler = webpack(webpackConfig);

  app.use(require('./middleware/webpack-dev')({
    compiler,
    publicPath: webpackConfig.output.publicPath,
  }));
  app.use(require('./middleware/webpack-hmr')({ compiler }));
} else {
  debug(
    'Application is being run outside of development mode. This starter kit ' +
    'does not provide any production-specific server functionality. To learn ' +
    'more about deployment strategies, check out the "deployment" section ' +
    'in the README.'
  );

  // Serving ~/dist by default. Ideally these files should be served by
  // the web server and not the app server, but this helps to demo the
  // server in production.
  app.use(express.static(paths.base(config.dir_dist)));
}

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.  Otherwise, the user will be redirected to the
//   login page.
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.redirect('/login');
}

export default app;
<commit_msg>Redirect to / instead of /login after failed login attempt<commit_after>import FacebookStrategy from 'passport-facebook';
import config from '../config';
import db from './db';
import express from 'express';
import historyApiFallback from 'connect-history-api-fallback';
import passport from 'passport';
import redisStoreFactory from 'connect-redis';
import session from 'express-session';

const RedisStore = redisStoreFactory(session);
const app = express();
const debug = require('debug')('app:server');
const paths = config.utils_paths;

class Queries {
  static findUser(id) {
    return db.oneOrNone('SELECT * FROM users WHERE id = $1', id);
  }

  static findUserFromFacebookId(facebookId) {
    return db.oneOrNone('SELECT * FROM users WHERE facebook_id = $1', facebookId);
  }

  static createUser({ facebookId, facebookAccessToken, name, emailAddress }) {
    return db.one(
      `INSERT INTO users (id, facebook_id, facebook_access_token, name, email_address)
      VALUES (
        uuid_generate_v4(),
        $/facebookId/,
        $/facebookAccessToken/,
        $/name/,
        $/emailAddress/
      ) RETURNING *`,
      { facebookId, facebookAccessToken, name, emailAddress }
    );
  }
}

passport.serializeUser((user, done) => {
  done(null, user);
});

passport.deserializeUser((user, done) => {
  done(null, user);
});

passport.use('facebook',
  new FacebookStrategy({
    clientID: config.fb.app_id,
    clientSecret: config.fb.app_secret,
    callbackURL: config.fb.callback_url,
  },
  // Facebook will send back the tokens and Facebook profile
  (accessToken, refreshToken, profile, done) => {
    // Find the user in the database based on their Facebook ID
    const {
      id: facebookId,
      displayName: name,
    } = profile;

    Queries.findUserFromFacebookId(facebookId)
      .catch(err => done(err))
      .then(user => {
        // If the user is found, then log them in
        if (user) {
          return done(null, user); // user found, return that user
        }

        // Save our user to the database
        const emailAddress =
          profile.emails &&
          profile.emails[0] &&
          profile.emails[0].value;

        const newUser = {
          facebookId,
          facebookAccessToken: accessToken,
          name,
          emailAddress,
        };

        Queries.createUser(newUser)
          .catch(err => done(err))
          .then(savedUser => done(null, savedUser));
      });
  })
);

app.use(session({
  resave: false,
  saveUninitialized: false,
  secret: 'fancy feast',
  store: new RedisStore(),
}));
// Initialize Passport!  Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());

app.get('/auth/login/facebook',
  passport.authenticate('facebook', { scope: 'email' })
);

app.get('/auth/login/facebook/callback',
  passport.authenticate('facebook', { failureRedirect: '/' }),
  (req, res) => res.redirect('/home')
);

app.get('/auth/logout', (req, res) => {
  req.logout();
  res.redirect('/');
});

// API

app.get('/api/users/:id', (req, res) => {
  Queries.findUser(req.params.id)
    .catch(error => res.send(error))
    .then(data => {
      // Sanitize user data to prevent leaking secrets
      const user = {
        id: data.id,
        name: data.name,
      };
      if (req.user && req.user.id === user.id) {
        user.email_address = data.email_address;
      }

      res.send(user);
    });
});

// END API

app.use(historyApiFallback({
  verbose: false,
}));

// Serve app with Webpack if HMR is enabled
if (config.compiler_enable_hmr) {
  const webpack = require('webpack');
  const webpackConfig = require('../build/webpack.config');
  const compiler = webpack(webpackConfig);

  app.use(require('./middleware/webpack-dev')({
    compiler,
    publicPath: webpackConfig.output.publicPath,
  }));
  app.use(require('./middleware/webpack-hmr')({ compiler }));
} else {
  debug(
    'Application is being run outside of development mode. This starter kit ' +
    'does not provide any production-specific server functionality. To learn ' +
    'more about deployment strategies, check out the "deployment" section ' +
    'in the README.'
  );

  // Serving ~/dist by default. Ideally these files should be served by
  // the web server and not the app server, but this helps to demo the
  // server in production.
  app.use(express.static(paths.base(config.dir_dist)));
}

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.  Otherwise, the user will be redirected to the
//   login page.
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.redirect('/login');
}

export default app;
<|endoftext|>


<commit_before>'use strict'

import {Component}   from 'react'
import {Link}        from 'react-router'
import DocumentTitle from 'react-document-title'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'

import {BlogActions}  from '../datastore/Blog'
import {StatsActions} from '../datastore/Stats'
import Image          from '../components/Image'

function mapStateToProps(state) {
  return {
    posts: state.blog.posts,
    stats: state.stats,
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    Object.assign({}, BlogActions, StatsActions),
    dispatch
  )
}

class HomePage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      stats: this.props.stats,
      posts: this.props.posts
    }
  }

  componentWillMount() {
    if (this.props.posts.length === 0) {
      this.props.fetchPosts()
    }
    this.props.fetchStats()
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.stats !== this.props.stats) {
      const stats = nextProps.stats
      this.setState({
        stats: stats,
      })
    }

    if (nextProps.posts !== this.props.posts) {
      this.setState({
        posts: nextProps.posts,
      })
    }
  }

  render() {
    const firstThreePosts = this.state.posts.slice(0, 3)

    return (
      <DocumentTitle title="Blockstack, building the decentralized internet">

                  Build decentralized apps

                  Blockstack is a new decentralized internet where users own their data and where apps run locally without remote servers.

                  <Link to="#" role="button"
                    className="btn btn-lg btn-secondary btn-block">
                    Join the Waitlist
                  </Link>

                  <Link to="#" role="button"
                    className="btn btn-lg btn-secondary btn-block">
                    Get the Developer Kit
                  </Link>

                  In production for 3+ years · {this.state.stats.domains} domains registered

                    The old internet is broken

                        Mass data breaches

                        Identity theft

                        Walled gardens

                    The new internet is here

                        Own your data

                        Own your identity

                        Own your apps

                    Why build on Blockstack?

                        User empowerment

                        Build apps that put users in control of their identity and data. Remove data as a liability. Attract users who value their freedom and security.

                        No infrastructure

                        Build apps without databases or server maintainance. Publish apps to the decentralized internet where they will run on user devices and live forever.

                        Be paid for open source

                        New business models enable you to get paid for open source code. Utilize micropayments, blockchain protocols, and more.

                    A full trustless stack

                        Identity

                        With Blockstack, users get digital keys that let them own their identity.
                        They sign in to apps locally without remote servers or identity providers.

                        Storage

                        Blockstack's storage system allows users to bring their own storage providers and control their data.
                        Data is encrypted and easily shared between applications.

                        Payments

                        Blockstack uses Bitcoin and other crypto-currencies for simple peer-to-peer payments.
                        Developers can charge for downloads, subscriptions, and more.

                        Payments

                        With Blockstack, you can accept simple one-time payments, enable continuous micropayments, and do much more.
                        All with a global-accepted and decentralized currency called Bitcoin.

                        Encryption

                        With Blockstack, all files and communication is end-to-end encrypted by default.
                        Blockstack handles key distribution and discovery so you don't have to.

                    Easily build apps like these

                        Liquid democracy

                        Peer-to-peer marketplaces

                        Decentralized social networks

                        Open ridesharing

                        Verified file publishing

                        Encrypted health records

                    Step-by-step tutorials

                    <Link to="/tutorials/hello-blockstack">
                      <Image className="landing-feat-img"
                        src="/images/tutorials/hello-blockstack-fastforward.gif"
                        fallbackSrc="/images/tutorials/hello-blockstack-fastforward.gif"
                        retinaSupport={false} />
                    </Link>

                    Complete the step-by-step tutorial and see how easy it is to build an app with a decentralized identity system in a few lines of code and no servers.

                    <Link to="/tutorials/hello-blockstack" role="button"
                      className="btn btn-outline-primary btn-block">
                      Hello Blockstack Tutorial
                    </Link>

                    Want to learn more about building apps on blockstack? Tutorials on building serverless apps with decentralized storage are coming soon.

                    News

                  { firstThreePosts.map((post, index) => {
                    return (

                        { post.urlSlug && post.title ?
                        <Link to={'/blog/' + post.urlSlug}>

{ post.title }

                        </Link>
                        : null }
                        { post.preview ?

                        : null }

                          { post.creator ?
                          {post.creator.name} |
                          : null }
                          { post.datetime && post.date ?
                          <time className="post-date" dateTime={post.datetime}>
                            {post.date}
                          </time>
                          : null }

                    )
                  }) }

                    <Link to="/newsletter" role="button"
                      className="btn btn-lg btn-outline-primary btn-block">
                      Get Updates
                    </Link>

      </DocumentTitle>
    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomePage)

/*

                    New internet. New browser experience.

                    Introducing the Blockstack browser portal - your way to access the new decentralized internet.

                        Free yourself from platform lock-in

                        Be safe from tracking & mass breaches

                        Powered by blockchain technology

                    <Link to="/install" role="button"
                      className="btn btn-lg btn-outline-primary btn-block">
                      Install Browser Portal
                    </Link>

                    The Blockstack app platform is here today

                        In production for over 3 years

                        {this.state.stats.domains} domains registered

                        Used by Microsoft, OpenBazaar & more

                    Build on a decentralized platform that's been in production use for years,
                    with open source software maintained by a well-funded team.

*/<commit_msg>added latest headline and description copy for hero<commit_after>'use strict'

import {Component}   from 'react'
import {Link}        from 'react-router'
import DocumentTitle from 'react-document-title'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'

import {BlogActions}  from '../datastore/Blog'
import {StatsActions} from '../datastore/Stats'
import Image          from '../components/Image'

function mapStateToProps(state) {
  return {
    posts: state.blog.posts,
    stats: state.stats,
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    Object.assign({}, BlogActions, StatsActions),
    dispatch
  )
}

class HomePage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      stats: this.props.stats,
      posts: this.props.posts
    }
  }

  componentWillMount() {
    if (this.props.posts.length === 0) {
      this.props.fetchPosts()
    }
    this.props.fetchStats()
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.stats !== this.props.stats) {
      const stats = nextProps.stats
      this.setState({
        stats: stats,
      })
    }

    if (nextProps.posts !== this.props.posts) {
      this.setState({
        posts: nextProps.posts,
      })
    }
  }

  render() {
    const firstThreePosts = this.state.posts.slice(0, 3)

    return (
      <DocumentTitle title="Blockstack, building the decentralized internet">

                 A New Internet for Decentralized Apps

                  Blockstack is a new decentralized internet where users own their data and apps run locally. A browser portal is all that’s needed to get started.

                  <Link to="#" role="button"
                    className="btn btn-lg btn-secondary btn-block">
                    Join the Waitlist
                  </Link>

                  <Link to="#" role="button"
                    className="btn btn-lg btn-secondary btn-block">
                    Get the Developer Kit
                  </Link>

                  In production for 3+ years · {this.state.stats.domains} domains registered

                    The old internet is broken

                        Mass data breaches

                        Identity theft

                        Walled gardens

                    The new internet is here

                        Own your data

                        Own your identity

                        Own your apps

                    Why build on Blockstack?

                        User empowerment

                        Build apps that put users in control of their identity and data. Remove data as a liability. Attract users who value their freedom and security.

                        No infrastructure

                        Build apps without databases or server maintainance. Publish apps to the decentralized internet where they will run on user devices and live forever.

                        Be paid for open source

                        New business models enable you to get paid for open source code. Utilize micropayments, blockchain protocols, and more.

                    A full trustless stack

                        Identity

                        With Blockstack, users get digital keys that let them own their identity.
                        They sign in to apps locally without remote servers or identity providers.

                        Storage

                        Blockstack's storage system allows users to bring their own storage providers and control their data.
                        Data is encrypted and easily shared between applications.

                        Payments

                        Blockstack uses Bitcoin and other crypto-currencies for simple peer-to-peer payments.
                        Developers can charge for downloads, subscriptions, and more.

                        Payments

                        With Blockstack, you can accept simple one-time payments, enable continuous micropayments, and do much more.
                        All with a global-accepted and decentralized currency called Bitcoin.

                        Encryption

                        With Blockstack, all files and communication is end-to-end encrypted by default.
                        Blockstack handles key distribution and discovery so you don't have to.

                    Easily build apps like these

                        Liquid democracy

                        Peer-to-peer marketplaces

                        Decentralized social networks

                        Open ridesharing

                        Verified file publishing

                        Encrypted health records

                    Step-by-step tutorials

                    <Link to="/tutorials/hello-blockstack">
                      <Image className="landing-feat-img"
                        src="/images/tutorials/hello-blockstack-fastforward.gif"
                        fallbackSrc="/images/tutorials/hello-blockstack-fastforward.gif"
                        retinaSupport={false} />
                    </Link>

                    Complete the step-by-step tutorial and see how easy it is to build an app with a decentralized identity system in a few lines of code and no servers.

                    <Link to="/tutorials/hello-blockstack" role="button"
                      className="btn btn-outline-primary btn-block">
                      Hello Blockstack Tutorial
                    </Link>

                    Want to learn more about building apps on blockstack? Tutorials on building serverless apps with decentralized storage are coming soon.

                    News

                  { firstThreePosts.map((post, index) => {
                    return (

                        { post.urlSlug && post.title ?
                        <Link to={'/blog/' + post.urlSlug}>

{ post.title }

                        </Link>
                        : null }
                        { post.preview ?

                        : null }

                          { post.creator ?
                          {post.creator.name} |
                          : null }
                          { post.datetime && post.date ?
                          <time className="post-date" dateTime={post.datetime}>
                            {post.date}
                          </time>
                          : null }

                    )
                  }) }

                    <Link to="/newsletter" role="button"
                      className="btn btn-lg btn-outline-primary btn-block">
                      Get Updates
                    </Link>

      </DocumentTitle>
    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomePage)

/*

                    New internet. New browser experience.

                    Introducing the Blockstack browser portal - your way to access the new decentralized internet.

                        Free yourself from platform lock-in

                        Be safe from tracking & mass breaches

                        Powered by blockchain technology

                    <Link to="/install" role="button"
                      className="btn btn-lg btn-outline-primary btn-block">
                      Install Browser Portal
                    </Link>

                    The Blockstack app platform is here today

                        In production for over 3 years

                        {this.state.stats.domains} domains registered

                        Used by Microsoft, OpenBazaar & more

                    Build on a decentralized platform that's been in production use for years,
                    with open source software maintained by a well-funded team.

*/<|endoftext|>


<commit_before>'use strict';

angular.module('<%=angularAppName%>').controller('<%= entityClass %>DialogController',
    ['$scope', '$stateParams', '$modalInstance', 'entity', '<%= entityClass %>'<% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) {%>, '<%= differentTypes[idx] %>'<% } } %>,
        function($scope, $stateParams, $modalInstance, entity, <%= entityClass %><% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) {%>, <%= differentTypes[idx] %><% } } %>) {

      $scope.<%= entityInstance %> = entity;<% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) { %>
      $scope.<%= differentTypes[idx].toLowerCase() %>s = <%= differentTypes[idx] %>.query();<% } } %>
      $scope.load = function(id) {
          <%= entityClass %>.get({id : id}, function(result) {
                $scope.<%= entityInstance %> = result;
          });
      };

      var onSaveFinished = function (result) {
        $scope.$emit('<%=angularAppName%>:<%= entityInstance %>Update', result);
        $modalInstance.close(result);
      };

      $scope.save = function () {
        if ($scope.<%= entityInstance %>.id != null) {
          <%= entityClass %>.update($scope.<%= entityInstance %>, onSaveFinished);
        } else {
          <%= entityClass %>.save($scope.<%= entityInstance %>, onSaveFinished);
        }
      };

      $scope.clear = function() {
        $modalInstance.dismiss('cancel');
      };
}]);
<commit_msg>Formatting<commit_after>'use strict';

angular.module('<%=angularAppName%>').controller('<%= entityClass %>DialogController',
    ['$scope', '$stateParams', '$modalInstance', 'entity', '<%= entityClass %>'<% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) {%>, '<%= differentTypes[idx] %>'<% } } %>,
        function($scope, $stateParams, $modalInstance, entity, <%= entityClass %><% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) {%>, <%= differentTypes[idx] %><% } } %>) {

        $scope.<%= entityInstance %> = entity;<% for (idx in differentTypes) { if (differentTypes[idx] != entityClass) { %>
        $scope.<%= differentTypes[idx].toLowerCase() %>s = <%= differentTypes[idx] %>.query();<% } } %>
        $scope.load = function(id) {
            <%= entityClass %>.get({id : id}, function(result) {
                $scope.<%= entityInstance %> = result;
            });
        };

        var onSaveFinished = function (result) {
            $scope.$emit('<%=angularAppName%>:<%= entityInstance %>Update', result);
            $modalInstance.close(result);
        };

        $scope.save = function () {
            if ($scope.<%= entityInstance %>.id != null) {
                <%= entityClass %>.update($scope.<%= entityInstance %>, onSaveFinished);
            } else {
                <%= entityClass %>.save($scope.<%= entityInstance %>, onSaveFinished);
            }
        };

        $scope.clear = function() {
            $modalInstance.dismiss('cancel');
        };
}]);
<|endoftext|>


<commit_before>module.exports = function (mongoose, properties) {
  var vin = new mongoose.Schema({
    sequence: Number,
    coinbase: {type: String, index: true},
    txid: {type: String, index: true},
    vout: {type: Number, index: true},
    scriptSig: {
      asm: String,
      hex: String
    },
    previousOutput: {
      asm: String,
      hex: String,
      reqSigs: Number,
      type: {type: String, index: true},
      addresses: {type: [String]}
    },
    assets: [{
      assetId: {type: String, index: true},
      amount: { type: Number, set: function (v) { return Math.round(v) }},
      issueTxid: String,
      divisibility: Number,
      lockStatus: Boolean
    }],
    value: { type: Number, set: function (v) { return Math.round(v) }, index: true},
    fixed: {type: Boolean, index: true, default: false}
  }, {_id: false })

  var vout = new mongoose.Schema({
    value: { type: Number, set: function (v) { return Math.round(v) } },
    n: Number,
    scriptPubKey: {
      asm: String,
      hex: String,
      reqSigs: Number,
      type: {type: String, index: true},
      addresses: {type: [String]}
    },
    assets: [{
      assetId: {type: String, index: true},
      amount: { type: Number, set: function (v) { return Math.round(v) }},
      issueTxid: String,
      divisibility: Number,
      lockStatus: Boolean
    }]
  }, {_id: false })

  var payment = new mongoose.Schema({
    input: Number,
    output: Number,
    amountOfUnits: Number,
    range: Boolean,
    percent: Boolean
  }, {_id: false })

  var ccdata = new mongoose.Schema({
    type: String,
    noRules: Boolean,
    payments: [payment],
    protocol: Number,
    version: Number,
    divisibility: Number,
    lockStatus: Boolean,
    amount: Number,
    sha2: String,
    torrentHash: String,
    multiSig: [{
      index: {type: Number},
      hashType: String
    }]
  }, {_id: false })

  // Define our token schema
  var RawTransactionsSchema = new mongoose.Schema({
    txid: { type: String, index: { unique: true } },
    hex: String,
    version: Number,
    loctime: Number,
    fee: { type: Number, set: function (v) { return Math.round(v) } },
    totalsent: { type: Number, set: function (v) { return Math.round(v) } },
    blockhash: {type: String, index: true},
    time: {type: Number, index: true},
    blocktime: {type: Number, index: true},
    blockheight: {type: Number, index: true},
    confirmations: {type: Number, index: true},
    vin: [vin],
    vout: [vout],
    ccdata: [ccdata],
    iosparsed: {type: Boolean, index: true, default: false},
    tries: {type: Number, index: true, default: 0},
    colored: {type: Boolean, index: true, default: false},
    ccparsed: {type: Boolean, index: true, default: false},
    overflow: {type: Boolean, index: true, default: false}
  })

  RawTransactionsSchema.index({
    'blockheight': 1,
    'iosparsed': 1
  })

  var round = function (doc) {
    if (doc.fee) doc.reward = Math.round(doc.reward)
    if (doc.totalsent) doc.totalsent = Math.round(doc.totalsent)
    if (doc.vin) {
      doc.vin.forEach(function (vin) {
        if (vin.value) vin.value = Math.round(vin.value)
        if (vin.assets) {
          vin.assets.forEach(function (asset) {
            if (asset.amount) asset.amount = Math.round(asset.amount)
          })
        }
      })
    }
    if (doc.vout) {
      doc.vout.forEach(function (vout) {
        if (vout.value) vout.value = Math.round(vout.value)
        if (vout.assets) {
          vout.assets.forEach(function (asset) {
            if (asset.amount) asset.amount = Math.round(asset.amount)
          })
        }
      })
    }
  }

  RawTransactionsSchema.post('find', function (docs) {
    if (docs && properties) {
      docs.forEach(function (doc) {
        if (properties.last_block && doc.blockheight > -1) {
          doc.confirmations = properties.last_block - doc.blockheight + 1
        } else {
          doc.confirmations = 0
        }
        calc_fee(doc)
        round(doc)
      })
    }
  })

  RawTransactionsSchema.post('findOne', function (doc) {
    if (doc && properties) {
      if (properties.last_block && doc.blockheight > -1) {
        doc.confirmations = properties.last_block - doc.blockheight + 1
      } else {
        doc.confirmations = 0
      }
      calc_fee(doc)
      round(doc)
    }
  })

  var calc_fee = function (doc) {
    if (doc.totalsent) return
    var fee = 0
    var totalsent = 0
    var coinbase = false
    if ('vin' in doc && doc.vin) {
      doc.vin.forEach(function (vin) {
        if ('coinbase' in vin && vin.coinbase) {
          coinbase = true
        }
        if (vin.value) {
          fee += vin.value
        }
      })
    }
    if (doc.vout) {
      doc.vout.forEach(function (vout) {
        if ('value' in vout && vout.value) {
          fee -= vout.value
          totalsent += vout.value
        }
      })
    }
    doc.totalsent = totalsent
    doc.fee = coinbase ? 0 : fee
  }

  return RawTransactionsSchema
}
<commit_msg>small bug<commit_after>module.exports = function (mongoose, properties) {
  var vin = new mongoose.Schema({
    sequence: Number,
    coinbase: {type: String, index: true},
    txid: {type: String, index: true},
    vout: {type: Number, index: true},
    scriptSig: {
      asm: String,
      hex: String
    },
    previousOutput: {
      asm: String,
      hex: String,
      reqSigs: Number,
      type: {type: String, index: true},
      addresses: {type: [String]}
    },
    assets: [{
      assetId: {type: String, index: true},
      amount: { type: Number, set: function (v) { return Math.round(v) }},
      issueTxid: String,
      divisibility: Number,
      lockStatus: Boolean
    }],
    value: { type: Number, set: function (v) { return Math.round(v) }, index: true},
    fixed: {type: Boolean, index: true, default: false}
  }, {_id: false })

  var vout = new mongoose.Schema({
    value: { type: Number, set: function (v) { return Math.round(v) } },
    n: Number,
    scriptPubKey: {
      asm: String,
      hex: String,
      reqSigs: Number,
      type: {type: String, index: true},
      addresses: {type: [String]}
    },
    assets: [{
      assetId: {type: String, index: true},
      amount: { type: Number, set: function (v) { return Math.round(v) }},
      issueTxid: String,
      divisibility: Number,
      lockStatus: Boolean
    }]
  }, {_id: false })

  var payment = new mongoose.Schema({
    input: Number,
    output: Number,
    amountOfUnits: Number,
    range: Boolean,
    percent: Boolean
  }, {_id: false })

  var ccdata = new mongoose.Schema({
    type: String,
    noRules: Boolean,
    payments: [payment],
    protocol: Number,
    version: Number,
    divisibility: Number,
    lockStatus: Boolean,
    amount: Number,
    sha2: String,
    torrentHash: String,
    multiSig: [{
      index: {type: Number},
      hashType: String
    }]
  }, {_id: false })

  // Define our token schema
  var RawTransactionsSchema = new mongoose.Schema({
    txid: { type: String, index: { unique: true } },
    hex: String,
    version: Number,
    loctime: Number,
    fee: { type: Number, set: function (v) { return Math.round(v) } },
    totalsent: { type: Number, set: function (v) { return Math.round(v) } },
    blockhash: {type: String, index: true},
    time: {type: Number, index: true},
    blocktime: {type: Number, index: true},
    blockheight: {type: Number, index: true},
    confirmations: {type: Number, index: true},
    vin: [vin],
    vout: [vout],
    ccdata: [ccdata],
    iosparsed: {type: Boolean, index: true, default: false},
    tries: {type: Number, index: true, default: 0},
    colored: {type: Boolean, index: true, default: false},
    ccparsed: {type: Boolean, index: true, default: false},
    overflow: {type: Boolean, index: true, default: false}
  })

  RawTransactionsSchema.index({
    'blockheight': 1,
    'iosparsed': 1
  })

  var round = function (doc) {
    if (doc.fee) doc.fee = Math.round(doc.fee)
    if (doc.totalsent) doc.totalsent = Math.round(doc.totalsent)
    if (doc.vin) {
      doc.vin.forEach(function (vin) {
        if (vin.value) vin.value = Math.round(vin.value)
        if (vin.assets) {
          vin.assets.forEach(function (asset) {
            if (asset.amount) asset.amount = Math.round(asset.amount)
          })
        }
      })
    }
    if (doc.vout) {
      doc.vout.forEach(function (vout) {
        if (vout.value) vout.value = Math.round(vout.value)
        if (vout.assets) {
          vout.assets.forEach(function (asset) {
            if (asset.amount) asset.amount = Math.round(asset.amount)
          })
        }
      })
    }
  }

  RawTransactionsSchema.post('find', function (docs) {
    if (docs && properties) {
      docs.forEach(function (doc) {
        if (properties.last_block && doc.blockheight > -1) {
          doc.confirmations = properties.last_block - doc.blockheight + 1
        } else {
          doc.confirmations = 0
        }
        calc_fee(doc)
        round(doc)
      })
    }
  })

  RawTransactionsSchema.post('findOne', function (doc) {
    if (doc && properties) {
      if (properties.last_block && doc.blockheight > -1) {
        doc.confirmations = properties.last_block - doc.blockheight + 1
      } else {
        doc.confirmations = 0
      }
      calc_fee(doc)
      round(doc)
    }
  })

  var calc_fee = function (doc) {
    if (doc.totalsent) return
    var fee = 0
    var totalsent = 0
    var coinbase = false
    if ('vin' in doc && doc.vin) {
      doc.vin.forEach(function (vin) {
        if ('coinbase' in vin && vin.coinbase) {
          coinbase = true
        }
        if (vin.value) {
          fee += vin.value
        }
      })
    }
    if (doc.vout) {
      doc.vout.forEach(function (vout) {
        if ('value' in vout && vout.value) {
          fee -= vout.value
          totalsent += vout.value
        }
      })
    }
    doc.totalsent = totalsent
    doc.fee = coinbase ? 0 : fee
  }

  return RawTransactionsSchema
}
<|endoftext|>


<commit_before>'use strict';

let logger = require('../log.js');
let expressWinston = require('express-winston');
let addRequestId = require('express-request-id')();
let uuidV1 = require('uuid/v1');
let moment = require('moment');

let bodyParser = require('body-parser');
let cookieSession = require('cookie-session');
let express = require('express');
let path = require('path');

let passport = require('passport');
let OAuth2Strategy = require('passport-oauth2').Strategy;
let request = require('request');

let helmet = require('helmet');
let csurf = require('csurf');
let compression = require('compression');
let git = require('git-rev-sync');

let index = require('../routes/index');
let disclaimer = require('../routes/disclaimer');
let search = require('../routes/search');
let subject = require('../routes/subject');

let content = require('../data/content.js');
let config = require('../server/config');

const version = git.short();
const production = process.env.NODE_ENV === 'production';

//  Express Configuration
let app = express();

// Configure Express for running behind proxies - https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', true);

// HACK: Azure doesn't support X-Forwarded-Proto so we add it manually
// http://stackoverflow.com/a/18455265/173062
app.use(function(req, res, next) {
    if (req.headers['x-arr-ssl'] && !req.headers['x-forwarded-proto']) {
        req.headers['x-forwarded-proto'] = 'https';
    }
    return next();
});

// Secure code best practice - see:
// 1. https://expressjs.com/en/advanced/best-practice-security.html,
// 2. https://www.npmjs.com/package/helmet
app.use(helmet());

// Automatically log every request with user details, a unique session id, and a unique request id
app.use(addRequestId);
function requestLogger() {
    return expressWinston.logger({
        winstonInstance: logger,
        meta: true,
        dynamicMeta: function(req, res) {
            return {
                userId: req.user ? req.user.id : null,
                userEmail: req.user ? req.user.email : null,
                requestId: req.id,
                sessionTag: req.user ? req.user.sessionTag : null
            };
        },
        colorize: true,
        requestWhitelist: ['url', 'method', 'originalUrl', 'query', 'body']
    });
}

// SSO configuration
let testMode = process.env.NODE_ENV === 'test' ? 'true' : 'false';
let ssoConfig = config.sso;

app.use(cookieSession({
    name: 'session',
    keys: [config.sessionSecret],
    maxAge: 60 * 60 * 1000,
    secure: config.https,
    httpOnly: true,
    signed: true,
    overwrite: true,
    sameSite: true
}));

if (testMode === 'true') {
    logger.info('Authentication disabled - using default test user profile');
    app.use(dummyUserProfile);
} else {
    logger.info('Authentication enabled');
    enableSSO();
}

// View Engine Configuration
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view engine', 'jade');

// Server Configuration
app.set('port', process.env.PORT || 3000);

// Request Processing Configuration
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// Resource Delivery Configuration
app.use(compression());

if (production) {
    app.locals.version = version;
} else {
    app.use(function(req, res, next) {
        res.locals.version = moment.now();
        return next();
    });
}

//  Static Resources Configuration
let cacheControl = {maxAge: config.staticResourceCacheDuration * 1000};

let publicResourcePaths = [
    '../public',
    '../govuk_modules/govuk_template/assets',
    '../govuk_modules/govuk_frontend_toolkit'
];

let iconResourcePaths = [
    '../govuk_modules/govuk_frontend_toolkit/images'
];

publicResourcePaths.forEach((dir) => {
    app.use('/public', express.static(path.join(__dirname, dir), cacheControl));
});

iconResourcePaths.forEach((dir) => {
    app.use('/public/images/icons', express.static(path.join(__dirname, dir), cacheControl));
});

// GovUK Template Configuration
/* jshint ignore:start */
app.locals.asset_path = '/public/';
/* jshint ignore:end */

// Don't cache dynamic resources
app.use(helmet.noCache());

// Express Routing Configuration
app.use('/', index);
app.use('/disclaimer/', disclaimer);
if (testMode !== 'true') {
    app.use(csurf());
}
if (testMode !== 'true') {
    app.use(authRequired);
    app.use(addTemplateVariables);
}
app.use(requestLogger());
app.use('/search/', search);
app.use('/subject/', subject);

// Error Handler
app.use(function(req, res, next) {
    let error = new Error('Not Found');
    error.status = 404;
    res.render('notfound', {nav: true, content: content.view.notfound});
});

app.use(logErrors);
app.use(clientErrors);

function logErrors(error, req, res, next) {
    logger.error('Unhandled error: ' + error.stack);
    next(error);
}

function clientErrors(error, req, res, next) {
    res.locals.error = error;
    res.locals.stack = production ? null : error.stack;
    res.locals.message = production ? 'Something went wrong. The error has been logged. Please try again' : error.message;

    res.status(error.status || 500);

    res.render('error', {nav: true, content: content.view.error});
}

//  SSO utility methods
function authRequired(req, res, next) {
    if (!req.user) {
        logger.info('Authorisation required - redirecting to login');
        return res.redirect('/login');
    }
    if (!req.user.disclaimer) {
        logger.info('Disclaimer required - redirecting to disclaimer');
        return res.redirect('/disclaimer');
    }
    res.locals.nav = true;
    next();
}

function addTemplateVariables(req, res, next) {
    res.locals.profile = req.user;
    next();
}

function dummyUserProfile(req, res, next) {
    req.user = {
        'email': 'test@test.com',
        'firstName': 'Test',
        'lastName': 'Tester',
        'profileLink': '/profile',
        'logoutLink': '/logout'
    };
    res.locals.profile = req.user;
    next();
}

function enableSSO() {

    app.use(passport.initialize());
    app.use(passport.session());

    passport.use(new OAuth2Strategy({
            authorizationURL: ssoConfig.TOKEN_HOST + ssoConfig.AUTHORIZE_PATH,
            tokenURL: ssoConfig.TOKEN_HOST + ssoConfig.TOKEN_PATH,
            clientID: ssoConfig.CLIENT_ID,
            clientSecret: ssoConfig.CLIENT_SECRET,
            proxy: true // trust upstream proxy
        },
        function(accessToken, refreshToken, profile, cb) {
            logger.info('Passport authentication invoked');

            let options = {
                uri: ssoConfig.TOKEN_HOST + ssoConfig.USER_DETAILS_PATH,
                qs: {access_token: accessToken},
                json: true
            };
            request(options, function(error, response, userDetails) {
                if (!error && response.statusCode === 200) {
                    logger.info('User authentication success');
                    return cb(null, userFor(userDetails));
                } else {
                    logger.error('Authentication failure:' + error);
                    return cb(error);
                }
            });
        })
    );

    function userFor(userDetails) {
        return {
            id: userDetails.id,
            email: userDetails.email,
            firstName: userDetails.first_name,
            lastName: userDetails.last_name,
            profileLink: userDetails.links.profile,
            logoutLink: userDetails.links.logout,
            sessionTag: uuidV1()
        };
    }

    passport.serializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

}

module.exports = app;
<commit_msg>Use timestamp for per-startup version number since git hash unavailable<commit_after>'use strict';

let logger = require('../log.js');
let expressWinston = require('express-winston');
let addRequestId = require('express-request-id')();
let uuidV1 = require('uuid/v1');
let moment = require('moment');

let bodyParser = require('body-parser');
let cookieSession = require('cookie-session');
let express = require('express');
let path = require('path');

let passport = require('passport');
let OAuth2Strategy = require('passport-oauth2').Strategy;
let request = require('request');

let helmet = require('helmet');
let csurf = require('csurf');
let compression = require('compression');

let index = require('../routes/index');
let disclaimer = require('../routes/disclaimer');
let search = require('../routes/search');
let subject = require('../routes/subject');

let content = require('../data/content.js');
let config = require('../server/config');

const version = moment.now().toString();
const production = process.env.NODE_ENV === 'production';

//  Express Configuration
let app = express();

// Configure Express for running behind proxies - https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', true);

// HACK: Azure doesn't support X-Forwarded-Proto so we add it manually
// http://stackoverflow.com/a/18455265/173062
app.use(function(req, res, next) {
    if (req.headers['x-arr-ssl'] && !req.headers['x-forwarded-proto']) {
        req.headers['x-forwarded-proto'] = 'https';
    }
    return next();
});

// Secure code best practice - see:
// 1. https://expressjs.com/en/advanced/best-practice-security.html,
// 2. https://www.npmjs.com/package/helmet
app.use(helmet());

// Automatically log every request with user details, a unique session id, and a unique request id
app.use(addRequestId);
function requestLogger() {
    return expressWinston.logger({
        winstonInstance: logger,
        meta: true,
        dynamicMeta: function(req, res) {
            return {
                userId: req.user ? req.user.id : null,
                userEmail: req.user ? req.user.email : null,
                requestId: req.id,
                sessionTag: req.user ? req.user.sessionTag : null
            };
        },
        colorize: true,
        requestWhitelist: ['url', 'method', 'originalUrl', 'query', 'body']
    });
}

// SSO configuration
let testMode = process.env.NODE_ENV === 'test' ? 'true' : 'false';
let ssoConfig = config.sso;

app.use(cookieSession({
    name: 'session',
    keys: [config.sessionSecret],
    maxAge: 60 * 60 * 1000,
    secure: config.https,
    httpOnly: true,
    signed: true,
    overwrite: true,
    sameSite: true
}));

if (testMode === 'true') {
    logger.info('Authentication disabled - using default test user profile');
    app.use(dummyUserProfile);
} else {
    logger.info('Authentication enabled');
    enableSSO();
}

// View Engine Configuration
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view engine', 'jade');

// Server Configuration
app.set('port', process.env.PORT || 3000);

// Request Processing Configuration
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// Resource Delivery Configuration
app.use(compression());

if (production) {
    app.locals.version = version;
} else {
    app.use(function(req, res, next) {
        res.locals.version = moment.now().toString();
        return next();
    });
}

//  Static Resources Configuration
let cacheControl = {maxAge: config.staticResourceCacheDuration * 1000};

let publicResourcePaths = [
    '../public',
    '../govuk_modules/govuk_template/assets',
    '../govuk_modules/govuk_frontend_toolkit'
];

let iconResourcePaths = [
    '../govuk_modules/govuk_frontend_toolkit/images'
];

publicResourcePaths.forEach((dir) => {
    app.use('/public', express.static(path.join(__dirname, dir), cacheControl));
});

iconResourcePaths.forEach((dir) => {
    app.use('/public/images/icons', express.static(path.join(__dirname, dir), cacheControl));
});

// GovUK Template Configuration
/* jshint ignore:start */
app.locals.asset_path = '/public/';
/* jshint ignore:end */

// Don't cache dynamic resources
app.use(helmet.noCache());

// Express Routing Configuration
app.use('/', index);
app.use('/disclaimer/', disclaimer);
if (testMode !== 'true') {
    app.use(csurf());
}
if (testMode !== 'true') {
    app.use(authRequired);
    app.use(addTemplateVariables);
}
app.use(requestLogger());
app.use('/search/', search);
app.use('/subject/', subject);

// Error Handler
app.use(function(req, res, next) {
    let error = new Error('Not Found');
    error.status = 404;
    res.render('notfound', {nav: true, content: content.view.notfound});
});

app.use(logErrors);
app.use(clientErrors);

function logErrors(error, req, res, next) {
    logger.error('Unhandled error: ' + error.stack);
    next(error);
}

function clientErrors(error, req, res, next) {
    res.locals.error = error;
    res.locals.stack = production ? null : error.stack;
    res.locals.message = production ? 'Something went wrong. The error has been logged. Please try again' : error.message;

    res.status(error.status || 500);

    res.render('error', {nav: true, content: content.view.error});
}

//  SSO utility methods
function authRequired(req, res, next) {
    if (!req.user) {
        logger.info('Authorisation required - redirecting to login');
        return res.redirect('/login');
    }
    if (!req.user.disclaimer) {
        logger.info('Disclaimer required - redirecting to disclaimer');
        return res.redirect('/disclaimer');
    }
    res.locals.nav = true;
    next();
}

function addTemplateVariables(req, res, next) {
    res.locals.profile = req.user;
    next();
}

function dummyUserProfile(req, res, next) {
    req.user = {
        'email': 'test@test.com',
        'firstName': 'Test',
        'lastName': 'Tester',
        'profileLink': '/profile',
        'logoutLink': '/logout'
    };
    res.locals.profile = req.user;
    next();
}

function enableSSO() {

    app.use(passport.initialize());
    app.use(passport.session());

    passport.use(new OAuth2Strategy({
            authorizationURL: ssoConfig.TOKEN_HOST + ssoConfig.AUTHORIZE_PATH,
            tokenURL: ssoConfig.TOKEN_HOST + ssoConfig.TOKEN_PATH,
            clientID: ssoConfig.CLIENT_ID,
            clientSecret: ssoConfig.CLIENT_SECRET,
            proxy: true // trust upstream proxy
        },
        function(accessToken, refreshToken, profile, cb) {
            logger.info('Passport authentication invoked');

            let options = {
                uri: ssoConfig.TOKEN_HOST + ssoConfig.USER_DETAILS_PATH,
                qs: {access_token: accessToken},
                json: true
            };
            request(options, function(error, response, userDetails) {
                if (!error && response.statusCode === 200) {
                    logger.info('User authentication success');
                    return cb(null, userFor(userDetails));
                } else {
                    logger.error('Authentication failure:' + error);
                    return cb(error);
                }
            });
        })
    );

    function userFor(userDetails) {
        return {
            id: userDetails.id,
            email: userDetails.email,
            firstName: userDetails.first_name,
            lastName: userDetails.last_name,
            profileLink: userDetails.links.profile,
            logoutLink: userDetails.links.logout,
            sessionTag: uuidV1()
        };
    }

    passport.serializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

}

module.exports = app;
<|endoftext|>