[Solved] PouchDB not syncing with CouchDB unless logged into Fauxton beforehand


#1

I’m using PouchDB with CouchDB hosted remotely on my server running Node (Digital Ocean Droplet using Ubuntu). I use SuperLogin for Authentication.

My App can’t doesn’t sync with Couchdb unless I log into Fauxton in the same browser that I access ionic serve and I have no idea why.

I don’t think it’s CORS related - I’ve already added it with:

add-cors-to-couchdb

My server file (including superlogin config):

var express = require('express');
var https = require('https');
var bodyParser = require('body-parser');
var logger = require('morgan');
var cors = require('cors');
var SuperLogin = require('superlogin');
var path = require('path');

var FacebookTokenStrategy = require('passport-facebook-token');
var GoogleTokenStrategy = require('passport-google-token');
var TwitterTokenStrategy = require('passport-twitter-token');

var FacebookStrategy = require('passport-facebook');
var TwitterStrategy = require('passport-twitter');
var GoogleStrategy = require('passport-google-oauth');

var app = express();
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());

var config = {
  dbServer: {
    protocol: 'http://',
    host: 'localhost:5984',
    user: process.env.PUMPDB_USER,
    password: process.env.PUMPDB_PASSWORD,
    userDB: 'pumpnote-users',
    couchAuthDB: '_users'
  },
  security: {
    defaultRoles: ['user'],
    maxFailedLogins: 5,
    lockoutTime: 60,
    sessionLife: 7776000, // one quarter
    tokenLife: 86400, // one day
    loginOnRegistration: true
  },
  local: {
    emailUsername: false,
    usernameField: 'username',
    passwordField: 'password',
    passwordConstraints : {
      length: {
        minimum: 6,
        message: "Must be at least 6 characters"
      }
    }
  },
  userModel: {
    whitelist: [
      'profile.dob',
      'profile.avatar',
      'profile.id_gender'
    ]
  },
  userDBs: {
    defaultDBs: {
      shared: ['pumpnote']
    },
    model: {
      pumpnote: {
        designDocs: [
          'genders',
          'filters'
        ],
      }
    },
    privatePrefix: 'pumpnote',
    designDocDir: path.join(__dirname, './designDocs')
  },
  providers: { 
    local: true,
    facebook: {
      credentials: {
        clientID: process.env.PUMPNOTE_FACEBOOK_KEY,
        clientSecret: process.env.PUMPNOTE_FACEBOOK_SECRET
      },
      options: {
        scope: ['email']
      }
    }
  }
};

// Initialize SuperLogin 
var superlogin = new SuperLogin(config);

// Mount SuperLogin's routes to the app 
app.use('/auth', superlogin.router);

superlogin.registerOAuth2('facebook', FacebookStrategy);
superlogin.registerOAuth2('twitter', TwitterStrategy);

superlogin.registerTokenProvider('facebook', FacebookTokenStrategy);
superlogin.registerTokenProvider('google', GoogleTokenStrategy);
superlogin.registerTokenProvider('twitter', TwitterTokenStrategy);

superlogin.onCreate(function(userDoc, provider){
  if(userDoc.profile === undefined){
    userDoc.profile = {};
  }

  if(provider !== 'local'){
    const displayName = userDoc[provider].profile.displayName;
  }

  return Promise.resolve(userDoc);
})

app.listen(process.env.PORT || 3000);

I’ve also added some design docs to superlogin:

filter.js:

module.exports = {
    filters: {
        views: {
            goal: function (doc) {
                if(doc.type == 'goal'){
                  emit(doc._id, 1);
                }              
            },
            experience: function (doc) {
                if(doc.type == 'experience'){
                  emit(doc._id, 1);
                }              
            },
            facility: function (doc) {
                if(doc.type == 'facility'){
                  emit(doc._id, 1);
                }              
            }
        }
    }
}

genders.js:

module.exports = {
    genders: {
        views: {
            all: function (doc) {
                if(doc.type == 'gender'){
                  emit(doc._id, 1);
                }              
            }
        }
    }
}

I can access Fauxton remotely and have set bind_address to 0.0.0.0 for httpd and chttpd.

On the ionic side I initialise the database like so:


  initDatabase(remote = DataProvider.DB_URL): void {
    this.db = new PouchDB('pumpnote', {
      auto_compaction: true
    });

    this.remote = remote;

    this.initRemoteSync();
  }

  initRemoteSync(): void {

    let options = {
      live : true,
      retry: true
    }

    this.db.sync(this.remote, options);
  }

The remote address (DB_URL) is correct and accessible. (I can navigate to it on chrome without a problem)

On Login I do:

  login(credentials) : Observable<any> {
    return this.dataProvider.postData(DataProvider.AUTH_LOGIN, credentials)
      .do( res => {

        this.saveUserData(res);

        this.zone.runOutsideAngular(() => {
          this.dataProvider.initDatabase(res.userDBs.pumpnote);
        });

      });
  }

I’m considering hosting CouchDB somewhere else if I can’t resolve it. but I’m not confident that it will resolve my problem.


#2

SOLVED:

After logging in, SuperLogin generates an database url with a token and ‘password’ to provide access to the database in the following format:

http://<access token>:<access password>@localhost:5984/<dbname>

Superlogin is running on the server that generates this auth URL. This would be fine if I was dealing with a local database, but I’m connecting to it remotely so I needed to change the auth URL so it had my remote address rather than “localhost”:

http://<access token>:<access password>@<remote address>:5984/<dbname>

Note - To provide external access to couchdb from the server:

  • I had to change my firewall settings to allow the database port to be accessed externally: “sudo ufw allow 5984”
  • I set bind_address to 0.0.0.0

I’m really not sure why logging into Fauxton hid this issue, however.


#3

@Jerezle how can I keep a user logged in after page refresh in ionic with superlogin? I am new to ionic framework and superlogin plugin too