You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ac...@apache.org on 2018/06/27 16:29:48 UTC

[couchdb-fauxton] branch master updated: Use relative url instead of static urls (#1078)

This is an automated email from the ASF dual-hosted git repository.

acote pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a3e520  Use relative url instead of static urls (#1078)
8a3e520 is described below

commit 8a3e520e17e2e79ee6015861f2592fadcb944558
Author: Alexis Côté <po...@users.noreply.github.com>
AuthorDate: Wed Jun 27 12:29:46 2018 -0400

    Use relative url instead of static urls (#1078)
    
    * Use relative url instead of static urls
    * Add helper functions to get API url and Server urls
    * Fix documentation urls that were using _utils instead of relative
    * Changed default configuration in settings.json
---
 app/addons/activetasks/layout.js                   |  3 +-
 app/addons/activetasks/resources.js                |  4 +-
 app/addons/auth/api.js                             | 13 ++++--
 app/addons/cluster/resources.js                    |  9 ++---
 app/addons/config/resources.js                     |  9 ++---
 app/addons/cors/reducers.js                        |  3 +-
 app/addons/databases/actions.js                    |  3 +-
 app/addons/databases/base.js                       | 19 +++++----
 app/addons/databases/resources.js                  |  5 ++-
 app/addons/documentation/components.js             |  3 +-
 .../tests/nightwatch/checksDocsPage.js             |  4 +-
 app/addons/documents/base.js                       | 46 +++++++++++-----------
 .../__tests__/doc-editor.components.test.js        |  2 +-
 app/addons/documents/resources.js                  |  2 +-
 app/addons/documents/rev-browser/actions.js        |  3 +-
 app/addons/replication/__tests__/actions.test.js   | 18 ++++-----
 app/addons/replication/__tests__/api.tests.js      | 18 ++++-----
 app/addons/replication/actions.js                  | 14 ++++---
 app/addons/replication/api.js                      | 23 +++++++----
 app/addons/replication/base.js                     |  3 +-
 app/addons/verifyinstall/resources.js              |  4 +-
 app/app.js                                         | 10 -----
 app/constants.js                                   | 39 +++++++++---------
 app/helpers.js                                     | 29 ++++++++++----
 jest-setup.js                                      |  5 +++
 settings.json.default.json                         |  4 +-
 26 files changed, 163 insertions(+), 132 deletions(-)

diff --git a/app/addons/activetasks/layout.js b/app/addons/activetasks/layout.js
index b01a84e..b6b88d2 100644
--- a/app/addons/activetasks/layout.js
+++ b/app/addons/activetasks/layout.js
@@ -11,6 +11,7 @@
 // the License.
 
 import React from 'react';
+import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import {OnePane, OnePaneHeader, OnePaneContent} from '../components/layouts';
 import {ActiveTasksController, ActiveTasksPollingWidgetController} from "./components";
@@ -25,7 +26,7 @@ export const ActiveTasksLayout = () => {
       <OnePaneHeader
         crumbs={crumbs}
         docURL={FauxtonAPI.constants.DOC_URLS.ACTIVE_TASKS}
-        endpoint={`${window.location.origin}/_active_tasks`}
+        endpoint={Helpers.getApiUrl('/_active_tasks')}
       >
         <ActiveTasksPollingWidgetController />
       </OnePaneHeader>
diff --git a/app/addons/activetasks/resources.js b/app/addons/activetasks/resources.js
index 7b88677..23803bb 100644
--- a/app/addons/activetasks/resources.js
+++ b/app/addons/activetasks/resources.js
@@ -10,14 +10,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from "../../app";
+import Helpers from "../../helpers";
 import Actions from "./actions";
 var Active = {};
 
 Active.AllTasks = Backbone.Collection.extend({
 
   url: function () {
-    return app.host + '/_active_tasks';
+    return Helpers.getServerUrl('/_active_tasks');
   },
 
   pollingFetch: function () { //still need this for the polling
diff --git a/app/addons/auth/api.js b/app/addons/auth/api.js
index d706820..7536376 100644
--- a/app/addons/auth/api.js
+++ b/app/addons/auth/api.js
@@ -11,6 +11,7 @@
 // the License.
 
 import app from './../../app';
+import Helpers from "../../helpers";
 import { defaultsDeep } from "lodash";
 
 export const json = (url, opts = {}) => fetch(
@@ -43,7 +44,8 @@ export const formEncoded = (url, opts = {}) => fetch(
 
 
 export function createAdmin({name, password, node}) {
-  return json(`${app.host}/_node/${node}/_config/admins/${name}`, {
+  const url = Helpers.getServerUrl(`/_node/${node}/_config/admins/${name}`);
+  return json(url, {
     method: "PUT",
     body: JSON.stringify(password)
   });
@@ -56,7 +58,8 @@ export function getSession() {
     return loggedInSessionPromise;
   }
 
-  const promise = json(app.host + "/_session").then(resp => {
+  const url = Helpers.getServerUrl('/_session');
+  const promise = json(url).then(resp => {
     if (resp.userCtx.name) {
       loggedInSessionPromise = promise;
     }
@@ -67,7 +70,8 @@ export function getSession() {
 }
 
 export function login(body) {
-  return formEncoded(app.host + "/_session", {
+  const url = Helpers.getServerUrl('/_session');
+  return formEncoded(url, {
     method: "POST",
     body: app.utils.queryParams(body)
   });
@@ -75,7 +79,8 @@ export function login(body) {
 
 export function logout() {
   loggedInSessionPromise = null;
-  return formEncoded(app.host + "/_session", {
+  const url = Helpers.getServerUrl('/_session');
+  return formEncoded(url, {
     method: "DELETE",
     body: app.utils.queryParams({ username: "_", password: "_" })
   });
diff --git a/app/addons/cluster/resources.js b/app/addons/cluster/resources.js
index 675c8ac..95b7ccb 100644
--- a/app/addons/cluster/resources.js
+++ b/app/addons/cluster/resources.js
@@ -10,17 +10,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from "../../app";
 import FauxtonAPI from "../../core/api";
+import Helpers from "../../helpers";
 
 var Cluster = FauxtonAPI.addon();
 
 Cluster.ClusterNodes = Backbone.Model.extend({
-  url: function (context) {
-    if (context === 'apiurl') {
-      return window.location.origin + '/_membership';
-    }
-    return app.host + '/_membership';
+  url: function () {
+    return Helpers.getServerUrl('/_membership');
   },
 
   parse: function (res) {
diff --git a/app/addons/config/resources.js b/app/addons/config/resources.js
index 9882006..cd4401d 100644
--- a/app/addons/config/resources.js
+++ b/app/addons/config/resources.js
@@ -10,8 +10,8 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from "../../app";
 import FauxtonAPI from "../../core/api";
+import Helpers from "../../helpers";
 import { deleteRequest, put } from "../../core/ajax";
 
 var Config = FauxtonAPI.addon();
@@ -23,9 +23,9 @@ Config.OptionModel = Backbone.Model.extend({
     if (!this.get('node')) {
       throw new Error('no node set');
     }
-
-    return app.host + '/_node/' + this.get('node') + '/_config/' +
+    const endpointUrl = '/_node/' + this.get('node') + '/_config/' +
       this.get('sectionName') + '/' + encodeURIComponent(this.get('optionName'));
+    return Helpers.getServerUrl(endpointUrl);
   },
 
   isNew () { return false; },
@@ -59,8 +59,7 @@ Config.ConfigModel = Backbone.Model.extend({
     if (!this.get('node')) {
       throw new Error('no node set');
     }
-
-    return app.host + '/_node/' + this.get('node') + '/_config';
+    return Helpers.getServerUrl('/_node/' + this.get('node') + '/_config');
   },
 
   parse (resp) {
diff --git a/app/addons/cors/reducers.js b/app/addons/cors/reducers.js
index 0a35d4b..81ede61 100644
--- a/app/addons/cors/reducers.js
+++ b/app/addons/cors/reducers.js
@@ -11,6 +11,7 @@
 // the License.
 
 import ActionTypes from "./actiontypes";
+import _ from "lodash";
 
 const initialState = {
   corsEnabled: false,
@@ -34,7 +35,7 @@ export default function cors (state = initialState, action) {
         isLoading: false,
         node: corsOptions.node,
         corsEnabled: corsOptions.corsEnabled,
-        isAllOrigins: _.include(corsOptions.origins, '*'),
+        isAllOrigins: _.includes(corsOptions.origins, '*'),
         origins: corsOptions.origins,
         deleteDomainModalVisible: false,
         domainToDelete: ''
diff --git a/app/addons/databases/actions.js b/app/addons/databases/actions.js
index 5b112bc..5e21781 100644
--- a/app/addons/databases/actions.js
+++ b/app/addons/databases/actions.js
@@ -10,6 +10,7 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 import app from "../../app";
+import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import { get } from "../../core/ajax";
 import Stores from "./stores";
@@ -184,7 +185,7 @@ export default {
       limit: 30
     });
 
-    const url = `${app.host}/_all_dbs${query}`;
+    const url = Helpers.getServerUrl(`/_all_dbs${query}`);
     get(url).then((dbs) => {
       const options = dbs.map(db => {
         return {
diff --git a/app/addons/databases/base.js b/app/addons/databases/base.js
index 8264e93..72d50e3 100644
--- a/app/addons/databases/base.js
+++ b/app/addons/databases/base.js
@@ -11,6 +11,7 @@
 // the License.
 
 import app from "../../app";
+import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import Databases from "./routes";
 import "./assets/less/databases.less";
@@ -34,7 +35,7 @@ Databases.databaseUrl = function (database) {
 
 FauxtonAPI.registerUrls('changes', {
   server: function (id, query) {
-    return app.host + '/' + id + '/_changes' + query;
+    return Helpers.getServerUrl('/' + id + '/_changes' + query);
   },
 
   app: function (id, query) {
@@ -42,13 +43,13 @@ FauxtonAPI.registerUrls('changes', {
   },
 
   apiurl: function (id, query) {
-    return window.location.origin + '/' + id + '/_changes' + query;
+    return Helpers.getApiUrl('/' + id + '/_changes' + query);
   }
 });
 
 FauxtonAPI.registerUrls('allDBs', {
   server: function (query = '') {
-    return `${app.host}/_all_dbs${query}`;
+    return Helpers.getServerUrl(`/_all_dbs${query}`);
   },
 
   app: function () {
@@ -56,15 +57,17 @@ FauxtonAPI.registerUrls('allDBs', {
   },
 
   apiurl: function () {
-    return window.location.origin + '/_all_dbs';
+    return Helpers.getApiUrl('/_all_dbs');
   }
 });
 
 FauxtonAPI.registerUrls('databaseBaseURL', {
   server: function (database) {
-    return window.location.origin + '/' + app.utils.safeURLName(database);
+    return Helpers.getServerUrl('/' + app.utils.safeURLName(database));
+  },
+  apiurl: function(database) {
+    return Helpers.getApiUrl('/' + app.utils.safeURLName(database));
   },
-
   app: function (database) {
     return '/database/' + database;
   }
@@ -72,7 +75,7 @@ FauxtonAPI.registerUrls('databaseBaseURL', {
 
 FauxtonAPI.registerUrls('permissions', {
   server: function (db) {
-    return app.host + '/' + db + '/_security';
+    return Helpers.getServerUrl('/' + db + '/_security');
   },
 
   app: function (db) {
@@ -80,7 +83,7 @@ FauxtonAPI.registerUrls('permissions', {
   },
 
   apiurl: function (db) {
-    return window.location.origin + '/' + db + '/_security';
+    return Helpers.getApiUrl('/' + db + '/_security');
   }
 });
 
diff --git a/app/addons/databases/resources.js b/app/addons/databases/resources.js
index dfb9c46..ae379c4 100644
--- a/app/addons/databases/resources.js
+++ b/app/addons/databases/resources.js
@@ -11,6 +11,7 @@
 // the License.
 
 import app from "../../app";
+import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import Documents from "../documents/resources";
 var Databases = FauxtonAPI.addon();
@@ -47,7 +48,7 @@ Databases.Model = FauxtonAPI.Model.extend({
     } else if (context === "web-index") {
       return "#/database/" + this.safeID() + "/_all_docs?limit=" + Databases.DocLimit;
     } else if (context === "apiurl") {
-      return window.location.origin + "/database/" + this.safeID() + "/_all_docs";
+      return Helpers.getApiUrl("/database/" + this.safeID() + "/_all_docs");
     } else if (context === "changes") {
       return FauxtonAPI.urls('changes', 'app', this.safeID(), '?descending=true&limit=100&include_docs=true');
     } else if (context === "changes-apiurl") {
@@ -55,7 +56,7 @@ Databases.Model = FauxtonAPI.Model.extend({
     } else if (context === "app") {
       return "/database/" + this.safeID();
     }
-    return app.host + "/" + this.safeID();
+    return Helpers.getServerUrl("/" + this.safeID());
 
   },
 
diff --git a/app/addons/documentation/components.js b/app/addons/documentation/components.js
index 1cde5d7..b2fe310 100644
--- a/app/addons/documentation/components.js
+++ b/app/addons/documentation/components.js
@@ -11,6 +11,7 @@
 // the License.
 
 import React from "react";
+import Helpers from '../../helpers';
 
 const docLinks = [
   {
@@ -20,7 +21,7 @@ const docLinks = [
   },
   {
     title: 'CouchDB Official Documentation — Offline',
-    link: '/_utils/docs/index.html',
+    link: Helpers.getServerUrl('/docs/index.html'),
     iconClassName: 'couchdb-icon'
   },
   {
diff --git a/app/addons/documentation/tests/nightwatch/checksDocsPage.js b/app/addons/documentation/tests/nightwatch/checksDocsPage.js
index e36e3c5..0fc5baa 100644
--- a/app/addons/documentation/tests/nightwatch/checksDocsPage.js
+++ b/app/addons/documentation/tests/nightwatch/checksDocsPage.js
@@ -14,13 +14,13 @@
 
 module.exports = {
   'Check the documentation page exists': function (client) {
-    var waitTime = client.globals.maxWaitTime;
+    const waitTime = client.globals.maxWaitTime;
 
     client
       .loginToGUI()
       .clickWhenVisible('a[href="#/documentation"]')
       .waitForElementVisible('a[href="http://docs.couchdb.org/en/latest/"]', waitTime, false)
-      .waitForElementVisible('a[href="/_utils/docs/index.html"]', waitTime, false)
+      .waitForElementVisible('a[href="./docs/index.html"]', waitTime, false)
       .waitForElementVisible('a[href="http://blog.couchdb.org/"]', waitTime, false)
       .waitForElementVisible('a[href="https://couchdb.apache.org/"]', waitTime, false)
       .waitForElementVisible('a[href="https://github.com/apache/couchdb"]', waitTime, false)
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 053b9b4..144be20 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -11,6 +11,7 @@
 // the License.
 
 import app from "../../app";
+import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import Documents from "./routes";
 import reducers from "./index-results/reducers";
@@ -36,7 +37,7 @@ function getQueryParam (query) {
 FauxtonAPI.registerUrls('allDocs', {
   server: function (id, query) {
     /** XXX DEPRECATED: use allDocsSanitized **/
-    return app.host + '/' + id + '/_all_docs' + getQueryParam(query);
+    return Helpers.getServerUrl('/' + id + '/_all_docs' + getQueryParam(query));
   },
   app: function (id, query) {
     /** XXX DEPRECATED: use allDocsSanitized **/
@@ -44,14 +45,14 @@ FauxtonAPI.registerUrls('allDocs', {
   },
   apiurl: function (id, query) {
     /** XXX DEPRECATED: use allDocsSanitized **/
-    return window.location.origin + '/' + id + '/_all_docs' + getQueryParam(query);
+    return Helpers.getApiUrl('/' + id + '/_all_docs' + getQueryParam(query));
   }
 });
 
 FauxtonAPI.registerUrls('allDocsSanitized', {
   server: function (id, query) {
     id = encodeURIComponent(id);
-    return app.host + '/' + id + '/_all_docs' + getQueryParam(query);
+    return Helpers.getServerUrl('/' + id + '/_all_docs' + getQueryParam(query));
   },
 
   app: function (id, query) {
@@ -61,19 +62,19 @@ FauxtonAPI.registerUrls('allDocsSanitized', {
 
   apiurl: function (id, query) {
     id = encodeURIComponent(id);
-    return window.location.origin + '/' + id + '/_all_docs' + getQueryParam(query);
+    return Helpers.getApiUrl('/' + id + '/_all_docs' + getQueryParam(query));
   }
 });
 
 FauxtonAPI.registerUrls('bulk_docs', {
   server: function (id, query) {
-    return app.host + '/' + encodeURIComponent(id) + '/_bulk_docs' + getQueryParam(query);
+    return Helpers.getServerUrl('/' + encodeURIComponent(id) + '/_bulk_docs' + getQueryParam(query));
   },
   app: function (id, query) {
     return 'database/' + id + '/_bulk_docs' + getQueryParam(query);
   },
   apiurl: function (id, query) {
-    return window.location.origin + '/' + id + '/_bulk_docs' + getQueryParam(query);
+    return Helpers.getApiUrl('/' + id + '/_bulk_docs' + getQueryParam(query));
   }
 });
 
@@ -85,7 +86,7 @@ FauxtonAPI.registerUrls('revision-browser', {
 
 FauxtonAPI.registerUrls('designDocs', {
   server: function (id, designDoc) {
-    return app.host + '/' + id + '/' + designDoc + '/_info';
+    return Helpers.getServerUrl('/' + id + '/' + designDoc + '/_info');
   },
 
   app: function (id, designDoc) {
@@ -93,13 +94,13 @@ FauxtonAPI.registerUrls('designDocs', {
   },
 
   apiurl: function (id, designDoc) {
-    return window.location.origin + '/' + id + '/' + designDoc + '/_info';
+    return Helpers.getApiUrl('/' + id + '/' + designDoc + '/_info');
   }
 });
 
 FauxtonAPI.registerUrls('view', {
   server: function (database, designDoc, viewName) {
-    return app.host + '/' + database + '/_design/' + designDoc + '/_view/' + viewName;
+    return Helpers.getServerUrl('/' + database + '/_design/' + designDoc + '/_view/' + viewName);
   },
 
   app: function (database, designDoc) {
@@ -107,7 +108,7 @@ FauxtonAPI.registerUrls('view', {
   },
 
   apiurl: function (id, designDoc, viewName) {
-    return window.location.origin + '/' + id + '/_design/' + designDoc + '/_view/' + viewName;
+    return Helpers.getApiUrl('/' + id + '/_design/' + designDoc + '/_view/' + viewName);
   },
 
   edit: function (database, designDoc, indexName) {
@@ -128,14 +129,14 @@ FauxtonAPI.registerUrls('document', {
     if (_.isUndefined(query)) {
       query = '';
     }
-    return app.host + '/' + database + '/' + doc + query;
+    return Helpers.getServerUrl('/' + database + '/' + doc + query);
   },
 
   attachment: function (database, doc, filename, query) {
     if (_.isUndefined(query)) {
       query = '';
     }
-    return app.host + '/' + database + '/' + doc + '/' + filename + query;
+    return Helpers.getServerUrl('/' + database + '/' + doc + '/' + filename + query);
   },
 
   app: function (database, doc) {
@@ -145,10 +146,9 @@ FauxtonAPI.registerUrls('document', {
   apiurl: function (database, doc) {
     if (!doc) {
       // api url for creating a doc with POST
-      return window.location.origin + '/' + database;
+      return Helpers.getApiUrl('/' + database);
     }
-
-    return window.location.origin + '/' + database + '/' + doc;
+    return Helpers.getApiUrl('/' + database + '/' + doc);
   },
 
   'web-index': function (database, doc) {
@@ -172,7 +172,7 @@ FauxtonAPI.registerUrls('new', {
 
 FauxtonAPI.registerUrls('base', {
   server: function (database) {
-    return app.host + '/' + database + '/';
+    return Helpers.getServerUrl('/' + database + '/');
   },
 
   app: function (database) {
@@ -187,7 +187,7 @@ FauxtonAPI.registerUrls('mango', {
       query = '';
     }
 
-    return app.host + '/' + db + '/_index' + query;
+    return Helpers.getServerUrl('/' + db + '/_index' + query);
   },
 
   'index-apiurl': function (db, query) {
@@ -195,7 +195,7 @@ FauxtonAPI.registerUrls('mango', {
       query = '';
     }
 
-    return window.location.origin + '/' + db + '/_index' + query;
+    return Helpers.getApiUrl('/' + db + '/_index' + query);
   },
 
   'index-app': function (db, query) {
@@ -207,7 +207,7 @@ FauxtonAPI.registerUrls('mango', {
   },
 
   'index-server-bulk-delete': function (db) {
-    return app.host + '/' + db + '/_index/_bulk_delete';
+    return Helpers.getServerUrl('/' + db + '/_index/_bulk_delete');
   },
 
   'query-server': function (db, query) {
@@ -215,7 +215,7 @@ FauxtonAPI.registerUrls('mango', {
       query = '';
     }
 
-    return app.host + '/' + db + '/_find' + query;
+    return Helpers.getServerUrl('/' + db + '/_find' + query);
   },
 
   'query-apiurl': function (db, query) {
@@ -223,7 +223,7 @@ FauxtonAPI.registerUrls('mango', {
       query = '';
     }
 
-    return window.location.origin + '/' + db + '/_find' + query;
+    return Helpers.getApiUrl('/' + db + '/_find' + query);
   },
 
   'query-app': function (db, query) {
@@ -235,11 +235,11 @@ FauxtonAPI.registerUrls('mango', {
   },
 
   'explain-server': function (db) {
-    return app.host + '/' + db + '/_explain';
+    return Helpers.getServerUrl('/' + db + '/_explain');
   },
 
   'explain-apiurl': function (db) {
-    return window.location.origin + '/' + db + '/_explain';
+    return Helpers.getApiUrl('/' + db + '/_explain');
   }
 });
 
diff --git a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
index 8f314ba..c247e91 100644
--- a/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
+++ b/app/addons/documents/doc-editor/__tests__/doc-editor.components.test.js
@@ -137,7 +137,7 @@ describe('DocEditorController', () => {
     const $attachmentNode = el.find('.view-attachments-section .dropdown-menu li');
     const attachmentURLactual = $attachmentNode.find('a').first().prop('href');
 
-    assert.equal(attachmentURLactual, '../../a%2Fspecial%3Fdb/_design%2Ftest%23doc/one%252F.png');
+    assert.equal(attachmentURLactual, './a%2Fspecial%3Fdb/_design%2Ftest%23doc/one%252F.png');
   });
 
   it.skip('setting deleteDocModal=true in store shows modal', () => {
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index 4fda5f7..9f843a5 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -174,7 +174,7 @@ Documents.BulkDeleteDocCollection = FauxtonAPI.Collection.extend({
 
 Documents.MangoBulkDeleteDocCollection = Documents.BulkDeleteDocCollection.extend({
   url: function () {
-    return app.host + '/' + this.databaseId + '/_index/_bulk_delete';
+    return Helpers.getServerUrl(`/${this.databaseId}/_index/_bulk_delete`);
   },
 
   createPayload: function (documents) {
diff --git a/app/addons/documents/rev-browser/actions.js b/app/addons/documents/rev-browser/actions.js
index 116af0e..81af70e 100644
--- a/app/addons/documents/rev-browser/actions.js
+++ b/app/addons/documents/rev-browser/actions.js
@@ -23,7 +23,8 @@ PouchDB.plugin(PouchHttpAdapter);
 let db;
 
 export const initDiffEditor = (dbName, docId) => dispatch => {
-  const url = FauxtonAPI.urls('databaseBaseURL', 'server', dbName);
+  // We have to use API url here because PouchDB doesn't take relative urls.
+  const url = FauxtonAPI.urls('databaseBaseURL', 'apiurl', dbName);
   db = PouchDB(url);
 
   Promise.all([db.get(docId), getTree(db, docId)])
diff --git a/app/addons/replication/__tests__/actions.test.js b/app/addons/replication/__tests__/actions.test.js
index 929a2c7..36646a7 100644
--- a/app/addons/replication/__tests__/actions.test.js
+++ b/app/addons/replication/__tests__/actions.test.js
@@ -37,7 +37,7 @@ describe("Replication Actions", () => {
 
     it('creates a new database if it does not exist', (done) => {
       const dispatch = () => {};
-      fetchMock.postOnce('/_replicator', {
+      fetchMock.postOnce('./_replicator', {
         status: 404,
         body: {
           error: "not_found",
@@ -45,14 +45,14 @@ describe("Replication Actions", () => {
         }
       });
 
-      fetchMock.putOnce('/_replicator', {
+      fetchMock.putOnce('./_replicator', {
         status: 200,
         body: {
           ok: true
         }
       });
 
-      const finalPost = fetchMock.postOnce('/_replicator', {
+      const finalPost = fetchMock.postOnce('./_replicator', {
         status: 200,
         body: {
           ok: true
@@ -74,7 +74,7 @@ describe("Replication Actions", () => {
 
       //this is not pretty, and might cause some false errors. But its tricky to tell when this test has completed
       setTimeout(() => {
-        assert.ok(finalPost.called('/_replicator'));
+        assert.ok(finalPost.called('./_replicator'));
         done();
       }, 100);
     });
@@ -131,7 +131,7 @@ describe("Replication Actions", () => {
         }
       };
 
-      fetchMock.getOnce('/_replicator/7dcea9874a8fcb13c6630a1547001559', doc);
+      fetchMock.getOnce('./_replicator/7dcea9874a8fcb13c6630a1547001559', doc);
       getReplicationStateFrom(doc._id)(dispatch);
     });
 
@@ -185,7 +185,7 @@ describe("Replication Actions", () => {
         }
       };
 
-      fetchMock.getOnce('/_replicator/rep_custom_auth', docWithCustomAuth);
+      fetchMock.getOnce('./_replicator/rep_custom_auth', docWithCustomAuth);
       getReplicationStateFrom(docWithCustomAuth._id)(dispatch);
     });
   });
@@ -224,9 +224,9 @@ describe("Replication Actions", () => {
         }
       ];
 
-      fetchMock.getOnce('/_scheduler/jobs', 404);
-      fetchMock.getOnce('/_replicator/_all_docs?include_docs=true&limit=100', {rows: []});
-      fetchMock.postOnce('/_replicator/_bulk_docs', {
+      fetchMock.getOnce('./_scheduler/jobs', 404);
+      fetchMock.getOnce('./_replicator/_all_docs?include_docs=true&limit=100', {rows: []});
+      fetchMock.postOnce('./_replicator/_bulk_docs', {
         status: 200,
         body: resp
       });
diff --git a/app/addons/replication/__tests__/api.tests.js b/app/addons/replication/__tests__/api.tests.js
index 113c75b..4b39507 100644
--- a/app/addons/replication/__tests__/api.tests.js
+++ b/app/addons/replication/__tests__/api.tests.js
@@ -401,7 +401,7 @@ describe('Replication API', () => {
     });
 
     it('returns true for support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', {});
+      fetchMock.getOnce('./_scheduler/jobs', {});
       return supportNewApi(true)
         .then(resp => {
           assert.ok(resp);
@@ -409,7 +409,7 @@ describe('Replication API', () => {
     });
 
     it('returns false for no support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', {
+      fetchMock.getOnce('./_scheduler/jobs', {
         status: 404,
         body: {error: "missing"}
       });
@@ -425,7 +425,7 @@ describe('Replication API', () => {
   describe('setCredentials', () => {
 
     it('returns true for support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', {});
+      fetchMock.getOnce('./_scheduler/jobs', {});
       return supportNewApi(true)
         .then(resp => {
           assert.ok(resp);
@@ -433,7 +433,7 @@ describe('Replication API', () => {
     });
 
     it('returns false for no support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', {
+      fetchMock.getOnce('./_scheduler/jobs', {
         status: 404,
         body: {error: "missing"}
       });
@@ -547,8 +547,8 @@ describe('Replication API', () => {
       });
 
       it("returns parsedReplicationDocs and ignores all design docs", () => {
-        fetchMock.getOnce('/_scheduler/jobs', 404);
-        fetchMock.get('/_replicator/_all_docs?include_docs=true&limit=100', _repDocs);
+        fetchMock.getOnce('./_scheduler/jobs', 404);
+        fetchMock.get('./_replicator/_all_docs?include_docs=true&limit=100', _repDocs);
         return supportNewApi(true)
           .then(fetchReplicationDocs)
           .then(docs => {
@@ -564,9 +564,9 @@ describe('Replication API', () => {
       });
 
       it("returns parsedReplicationDocs", () => {
-        fetchMock.getOnce('/_scheduler/jobs', 200);
-        fetchMock.get('/_replicator/_all_docs?include_docs=true&limit=100', _repDocs);
-        fetchMock.get('/_scheduler/docs?include_docs=true', _schedDocs);
+        fetchMock.getOnce('./_scheduler/jobs', 200);
+        fetchMock.get('./_replicator/_all_docs?include_docs=true&limit=100', _repDocs);
+        fetchMock.get('./_scheduler/docs?include_docs=true', _schedDocs);
         return supportNewApi(true)
           .then(fetchReplicationDocs)
           .then(docs => {
diff --git a/app/addons/replication/actions.js b/app/addons/replication/actions.js
index 08e0733..a1d2d3d 100644
--- a/app/addons/replication/actions.js
+++ b/app/addons/replication/actions.js
@@ -14,6 +14,7 @@ import FauxtonAPI from '../../core/api';
 import {get, post} from '../../core/ajax';
 import ActionTypes from './actiontypes';
 import Helpers from './helpers';
+import MainHelper from '../../helpers';
 import Constants from './constants';
 import {
   supportNewApi,
@@ -38,7 +39,8 @@ export const initReplicator = (routeLocalSource, localSource) => dispatch => {
 };
 
 export const getDatabasesList = () => dispatch => {
-  get('/_all_dbs')
+  const url = MainHelper.getServerUrl("/_all_dbs");
+  get(url)
     .then((databases) => {
       dispatch({
         type: ActionTypes.REPLICATION_DATABASES_LOADED,
@@ -51,8 +53,8 @@ export const getDatabasesList = () => dispatch => {
 
 export const replicate = (params) => dispatch => {
   const replicationDoc = createReplicationDoc(params);
-
-  const promise = post('/_replicator', replicationDoc);
+  const url = MainHelper.getServerUrl("/_replicator");
+  const promise = post(url, replicationDoc);
 
   const source = Helpers.getDatabaseLabel(replicationDoc.source);
   const target = Helpers.getDatabaseLabel(replicationDoc.target);
@@ -211,7 +213,8 @@ export const deleteDocs = (docs) => dispatch => {
     clear: true
   });
 
-  post('/_replicator/_bulk_docs', {docs: bulkDocs}, {raw: true})
+  const url = MainHelper.getServerUrl('/_replicator/_bulk_docs');
+  post(url, {docs: bulkDocs}, {raw: true})
     .then(resp => {
       if (!resp.ok) {
         throw resp;
@@ -326,7 +329,8 @@ export const getReplicationStateFrom = (id) => dispatch => {
     type: ActionTypes.REPLICATION_FETCHING_FORM_STATE
   });
 
-  get(`/_replicator/${encodeURIComponent(id)}`)
+  const url = MainHelper.getServerUrl(`/_replicator/${encodeURIComponent(id)}`);
+  get(url)
     .then((doc) => {
       const stateDoc = {
         replicationDocName: doc._id,
diff --git a/app/addons/replication/api.js b/app/addons/replication/api.js
index 363228d..2ec00f8 100644
--- a/app/addons/replication/api.js
+++ b/app/addons/replication/api.js
@@ -13,6 +13,7 @@
 import '@webcomponents/url';
 import Constants from './constants';
 import FauxtonAPI from '../../core/api';
+import Helpers from '../../helpers';
 import {get, post, put} from '../../core/ajax';
 import base64 from 'base-64';
 import _ from 'lodash';
@@ -21,7 +22,8 @@ let newApiPromise = null;
 export const supportNewApi = (forceCheck) => {
   if (!newApiPromise || forceCheck) {
     newApiPromise = new FauxtonAPI.Promise((resolve) => {
-      get('/_scheduler/jobs', {raw: true})
+      const url = Helpers.getServerUrl('/_scheduler/jobs');
+      get(url, {raw: true})
         .then(resp => {
           if (resp.status > 202) {
             return resolve(false);
@@ -298,7 +300,8 @@ export const combineDocsAndScheduler = (docs, schedulerDocs) => {
 export const fetchReplicationDocs = () => {
   return supportNewApi()
     .then(newApi => {
-      const docsPromise = get('/_replicator/_all_docs?include_docs=true&limit=100')
+      const url = Helpers.getServerUrl('/_replicator/_all_docs?include_docs=true&limit=100');
+      const docsPromise = get(url)
         .then((res) => {
           if (res.error) {
             return [];
@@ -321,7 +324,8 @@ export const fetchReplicationDocs = () => {
 };
 
 export const fetchSchedulerDocs = () => {
-  return get('/_scheduler/docs?include_docs=true')
+  const url = Helpers.getServerUrl('/_scheduler/docs?include_docs=true');
+  return get(url)
     .then((res) => {
       if (res.error) {
         return [];
@@ -333,7 +337,8 @@ export const fetchSchedulerDocs = () => {
 
 export const checkReplicationDocID = (docId) => {
   return new Promise((resolve) => {
-    get(`/_replicator/${docId}`)
+    const url = Helpers.getServerUrl(`/_replicator/${docId}`);
+    get(url)
       .then(resp => {
         if (resp.error === "not_found") {
           resolve(false);
@@ -369,7 +374,8 @@ export const fetchReplicateInfo = () => {
         return [];
       }
 
-      return get('/_scheduler/jobs')
+      const url = Helpers.getServerUrl('/_scheduler/jobs');
+      return get(url)
         .then(resp => {
           return parseReplicateInfo(resp);
         });
@@ -382,15 +388,16 @@ export const deleteReplicatesApi = (replicates) => {
       replication_id: replicate._id,
       cancel: true
     };
-
-    return post('/_replicate', data);
+    const url = Helpers.getServerUrl('/_replicate');
+    return post(url, data);
   });
 
   return FauxtonAPI.Promise.all(promises);
 };
 
 export const createReplicatorDB = () => {
-  return put('/_replicator')
+  const url = Helpers.getServerUrl('/_replicator');
+  return put(url)
     .then(res => {
       if (!res.ok) {
         throw {reason: 'Failed to create the _replicator database.'};
diff --git a/app/addons/replication/base.js b/app/addons/replication/base.js
index e4b966b..951a335 100644
--- a/app/addons/replication/base.js
+++ b/app/addons/replication/base.js
@@ -11,6 +11,7 @@
 // the License.
 
 import FauxtonAPI from '../../core/api';
+import Helpers from "../../helpers";
 import replication from './route';
 import './assets/less/replication.less';
 import { checkForNewApi } from './actions';
@@ -29,7 +30,7 @@ FauxtonAPI.addReducers({
 
 FauxtonAPI.registerUrls('replication', {
   app: (db) => '#/replication/_create/' + db,
-  api: () => window.location.origin + '/_replicator'
+  api: () => Helpers.getApiUrl('/_replicator')
 });
 
 export default replication;
diff --git a/app/addons/verifyinstall/resources.js b/app/addons/verifyinstall/resources.js
index 5690660..0c9f689 100644
--- a/app/addons/verifyinstall/resources.js
+++ b/app/addons/verifyinstall/resources.js
@@ -10,8 +10,8 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from "../../app";
 import FauxtonAPI from "../../core/api";
+import Helpers from "../../helpers";
 import { get, post } from "../../core/ajax";
 import Databases from "../databases/resources";
 import Documents from "../documents/resources";
@@ -125,7 +125,7 @@ Verifyinstall.testProcess = {
       target: 'verifytestdb_replicate'
     };
     return post(
-      app.host + '/_replicate',
+      Helpers.getServerUrl('/_replicate'),
       body
     ).then(res => {
       if (res.error) {
diff --git a/app/app.js b/app/app.js
index 260c0ae..6c39160 100644
--- a/app/app.js
+++ b/app/app.js
@@ -26,16 +26,6 @@ if (_.isUndefined(console)) {
   };
 }
 
-// make sure we have location.origin
-if (_.isUndefined(window.location.origin)) {
-  var port = '';
-  if (window.location.port) {
-    port = ':' + window.location.port;
-  }
-  window.location.origin = window.location.protocol + '//' +
-    window.location.hostname + port;
-}
-
 // Provide a global location to place configuration settings and module
 // creation also mix in Backbone.Events
 Object.assign(app, {
diff --git a/app/constants.js b/app/constants.js
index 5fd260b..66fdc2e 100644
--- a/app/constants.js
+++ b/app/constants.js
@@ -9,7 +9,6 @@
 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 // License for the specific language governing permissions and limitations under
 // the License.
-
 export default {
 
   MISC: {
@@ -29,24 +28,24 @@ export default {
 
   // documentation URLs
   DOC_URLS: {
-    GENERAL: '/_utils/docs/intro/api.html#documents',
-    ALL_DBS: '/_utils/docs/api/server/common.html?highlight=all_dbs#get--_all_dbs',
-    REPLICATION: '/_utils/docs/replication/replicator.html#basics',
-    DESIGN_DOCS: '/_utils/docs/ddocs/ddocs.html#design-docs',
-    DESIGN_DOC_METADATA: '/_utils/docs/api/ddoc/common.html#api-ddoc-view-index-info',
-    VIEW_FUNCS: '/_utils/docs/ddocs/ddocs.html#view-functions',
-    MAP_FUNCS: '/_utils/docs/ddocs/ddocs.html#map-functions',
-    REDUCE_FUNCS: '/_utils/docs/ddocs/ddocs.html#reduce-and-rereduce-functions',
-    API_REF: '/_utils/docs/http-api.html',
-    DB_PERMISSION: '/_utils/docs/api/database/security.html#db-security',
-    STATS: '/_utils/docs/api/server/common.html?highlight=stats#get--_stats',
-    ACTIVE_TASKS: '/_utils/docs/api/server/common.html?highlight=stats#active-tasks',
-    LOG: '/_utils/docs/api/server/common.html?highlight=stats#log',
-    CONFIG: '/_utils/docs/config/index.html',
-    VIEWS: '/_utils/docs/intro/overview.html#views',
-    MANGO_INDEX: '/_utils/docs/intro/api.html#documents',
-    MANGO_SEARCH: '/_utils/docs/intro/api.html#documents',
-    SETUP: '/_utils/docs/cluster/setup.html#the-cluster-setup-wizard',
-    CHANGES: '/_utils/docs/api/database/changes.html?highlight=changes#post--db-_changes'
+    GENERAL:'./docs/intro/api.html#documents',
+    ALL_DBS:'./docs/api/server/common.html?highlight=all_dbs#get--_all_dbs',
+    REPLICATION:'./docs/replication/replicator.html#basics',
+    DESIGN_DOCS:'./docs/ddocs/ddocs.html#design-docs',
+    DESIGN_DOC_METADATA:'./docs/api/ddoc/common.html#api-ddoc-view-index-info',
+    VIEW_FUNCS:'./docs/ddocs/ddocs.html#view-functions',
+    MAP_FUNCS:'./docs/ddocs/ddocs.html#map-functions',
+    REDUCE_FUNCS:'./docs/ddocs/ddocs.html#reduce-and-rereduce-functions',
+    API_REF:'./docs/http-api.html',
+    DB_PERMISSION:'./docs/api/database/security.html#db-security',
+    STATS:'./docs/api/server/common.html?highlight=stats#get--_stats',
+    ACTIVE_TASKS:'./docs/api/server/common.html?highlight=stats#active-tasks',
+    LOG:'./docs/api/server/common.html?highlight=stats#log',
+    CONFIG:'./docs/config/index.html',
+    VIEWS:'./docs/intro/overview.html#views',
+    MANGO_INDEX:'./docs/intro/api.html#documents',
+    MANGO_SEARCH:'./docs/intro/api.html#documents',
+    CHANGES:'./docs/api/database/changes.html?highlight=changes#post--db-_changes',
+    SETUP: './docs/cluster/setup.html#the-cluster-setup-wizard'
   }
 };
diff --git a/app/helpers.js b/app/helpers.js
index 0b49194..ed25130 100644
--- a/app/helpers.js
+++ b/app/helpers.js
@@ -17,11 +17,12 @@
 // want to change this later, but for now this should be thought of as a
 // "purely functional" helper system.
 
-import app from "./app";
 import constants from "./constants";
-import { get } from "./core/ajax";
+import app from "./initialize";
 import utils from "./core/utils";
 import moment from "moment";
+import url from "url";
+import {get} from "./core/ajax";
 import _ from 'lodash';
 
 var Helpers = {};
@@ -63,6 +64,25 @@ Helpers.escapeJQuerySelector = function (selector) {
   return selector && selector.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
 };
 
+Helpers.getApiUrl = endpointRoute => {
+  if (app.host.endsWith('/') && endpointRoute.startsWith("/")) {
+    endpointRoute = endpointRoute.substr(1);
+  }
+  return url.resolve(window.location.href, app.host + endpointRoute);
+};
+
+Helpers.getServerUrl = endpointRoute => {
+  if (app.host.endsWith('/') && endpointRoute.startsWith("/")) {
+    endpointRoute = endpointRoute.substr(1);
+  }
+  return app.host + endpointRoute;
+};
+
+Helpers.getUUID = function (count = 1) {
+  const url = Helpers.getServerUrl(`/_uuids?count=${count}`);
+  return get(url);
+};
+
 /**
  * Determine if the current application is running on IE10 or IE11
  * @returns {boolean} True if on IE10 or IE11. Otherwise false.
@@ -71,9 +91,4 @@ Helpers.isIE1X = function() {
   return document.documentMode == 11 || document.documentMode == 10;
 };
 
-Helpers.getUUID = function (count = 1) {
-  const url = `${app.host}/_uuids?count=${count}`;
-  return get(url);
-};
-
 export default Helpers;
diff --git a/jest-setup.js b/jest-setup.js
index 870b7f8..47c7260 100644
--- a/jest-setup.js
+++ b/jest-setup.js
@@ -39,6 +39,11 @@ Object.defineProperty(window.location, 'origin', {
   value: 'http://dev:8000'
 });
 
+Object.defineProperty(window.location, 'href', {
+  writable: true,
+  value: 'http://dev:8000'
+});
+
 // Setup enzyme's react adapter
 const Enzyme = require('enzyme');
 const EnzymeAdapter = require('enzyme-adapter-react-16');
diff --git a/settings.json.default.json b/settings.json.default.json
index 9a5e8ad..31935fb 100644
--- a/settings.json.default.json
+++ b/settings.json.default.json
@@ -27,7 +27,7 @@
         },
         "app": {
           "root": "/",
-          "host": "../..",
+          "host": "./",
           "version": "1.0.dev"
         }
       },
@@ -42,7 +42,7 @@
         },
         "app": {
           "root": "/",
-          "host": "../..",
+          "host": "../",
           "version": "1.0"
         }
       },