diff --git a/docker/couchdb/Dockerfile b/docker/couchdb/Dockerfile index d19542f5..cce27785 100644 --- a/docker/couchdb/Dockerfile +++ b/docker/couchdb/Dockerfile @@ -9,7 +9,7 @@ RUN curl https://github.com/just-containers/s6-overlay/releases/download/v1.21.8 && tar xzf /tmp/s6-overlay-${S6_ARCH}.tar.gz -C / \ && rm -rf /tmp/s6-overlay-${S6_ARCH}.tar.gz -COPY /docker/couchdb/local.ini /opt/couchdb/etc/ +COPY /docker/couchdb/local.ini /opt/couchdb/etc/local.ini COPY /docker/rootfs / RUN rm -rf /etc/services.d/fasten #delete the fasten app from the couchdbase container. diff --git a/docker/rootfs/etc/cont-init.d/50-couchdb-init b/docker/rootfs/etc/cont-init.d/50-couchdb-init index 726c537b..30f2aaab 100644 --- a/docker/rootfs/etc/cont-init.d/50-couchdb-init +++ b/docker/rootfs/etc/cont-init.d/50-couchdb-init @@ -3,10 +3,11 @@ if [ -f "/opt/couchdb/data/.init_complete" ]; then echo "Couchdb initialization has already completed, skipping" else - cp /opt/couchdb/etc/setup.ini /opt/couchdb/etc/local.d/setup.ini # start couchdb as a background process (store PID) - echo "Couchdb initialization: start couchdb in background mode" + echo "Couchdb initialization: start couchdb in background mode (non-standard port)" # https://linux.die.net/man/1/couchdb + sed -i -e 's/;port = 5984/port = 5432/g' /opt/couchdb/etc/local.ini + sed -i -e 's/bind_address = 0.0.0.0/bind_address = 127.0.0.1/g' /opt/couchdb/etc/local.ini /opt/couchdb/bin/couchdb -b & COUCHDB_PID=$! @@ -15,16 +16,17 @@ else # create couch_peruser required system databases manually on startup echo "couchdb ready, start creating system databases" - curl --fail -X PUT -u admin:mysetuppassword http://127.0.0.1:5432/_users - curl --fail -X PUT -u admin:mysetuppassword http://127.0.0.1:5432/_replicator - curl --fail -X PUT -u admin:mysetuppassword http://127.0.0.1:5432/_global_changes + curl --fail -X PUT -u admin:mysecretpassword http://127.0.0.1:5432/_users + curl --fail -X PUT -u admin:mysecretpassword http://127.0.0.1:5432/_replicator + curl --fail -X PUT -u admin:mysecretpassword http://127.0.0.1:5432/_global_changes echo "system databases created successfully" - # gracefully stop couchdb process echo "killing couchdb process" /opt/couchdb/bin/couchdb -k - rm -f /opt/couchdb/etc/local.d/setup.ini + + sed -i -e 's/port = 5432/;port = 5984/g' /opt/couchdb/etc/local.ini + sed -i -e 's/bind_address = 127.0.0.1/bind_address = 0.0.0.0/g' /opt/couchdb/etc/local.ini # create the init complete flag echo "Couchdb initialization: complete" diff --git a/docker/rootfs/opt/couchdb/etc/setup.ini b/docker/rootfs/opt/couchdb/etc/setup.ini deleted file mode 100644 index ecafd389..00000000 --- a/docker/rootfs/opt/couchdb/etc/setup.ini +++ /dev/null @@ -1,18 +0,0 @@ -; CouchDB Configuration Settings - -; This minimal file is only for use during first setup. It is insecure and WILL NOT be used after setup completes -; it will be copied into /opt/couchdb/etc/local.d temporarily. -[couchdb] -single_node = true - -[couch_peruser] -enable = true - -[chttpd] -; bind to localhost and a non-standard, unexposed port. -port = 5432 -bind_address = 127.0.0.1 - -[admins] -; THIS PASSWORD IS INSECURE, it is only used during setup and does not persist. -admin = mysetuppassword diff --git a/frontend/src/app/services/fasten-db.service.ts b/frontend/src/app/services/fasten-db.service.ts index 3009d55f..07a11bea 100644 --- a/frontend/src/app/services/fasten-db.service.ts +++ b/frontend/src/app/services/fasten-db.service.ts @@ -25,16 +25,11 @@ PouchDB.plugin(PouchAuth); }) export class FastenDbService extends PouchdbRepository { - //TODO: move most of this functionality back into the lib as a separate file. - replicationHandler: any - remotePouchEndpoint: string // "http://localhost:5984" + constructor(private _httpClient: HttpClient) { const userIdentifier = localStorage.getItem("current_user") super(userIdentifier, "my-secret-encryption-key"); - this.remotePouchEndpoint = `${window.location.protocol}//${window.location.host}${this.getBasePath()}/database` - if(userIdentifier){ - this.enableSync(userIdentifier) - } + } @@ -53,7 +48,10 @@ export class FastenDbService extends PouchdbRepository { .then((loginResp)=>{ return this.postLoginHook(loginResp.name, true) }) - .catch((err) => console.error("an error occurred during login/setup", err)) + .catch((err) => { + console.error("an error occurred during login/setup", err) + throw err + }) } /** @@ -86,20 +84,20 @@ export class FastenDbService extends PouchdbRepository { //if we have a local database, lets see if we have an active session to the remote database. const remotePouchDb = new PouchDB(this.getRemoteUserDb(localStorage.getItem("current_user")), {skip_setup: true}); const session = await remotePouchDb.getSession() - const isAuth = !!session?.userCtx?.name + const authUser = session?.userCtx?.name + const isAuth = !!authUser console.warn("IsAuthenticated? getSession() ====> ", isAuth) - - return isAuth + if(!isAuth){ + return false + } + //confirm that the logged in user matches the session user + return authUser == localStorage.getItem("current_user") } catch (e) { return false } } public Close(): Promise { - // Stop remote replication for existing database - if(this.replicationHandler){ - this.replicationHandler.cancel() - } return super.Close() } @@ -182,42 +180,25 @@ export class FastenDbService extends PouchdbRepository { console.log("DB createIndex complete", createIndexMsg) if(sync){ + console.log("DB sync init...", userIdentifier, this.getRemoteUserDb(userIdentifier)) + this.enableSync(userIdentifier) + // .on('paused', function (info) { + // // replication was paused, usually because of a lost connection + // console.warn("replication was paused, usually because of a lost connection", info) + // }).on('active', function (info) { + // // replication was resumed + // console.warn("replication was resumed", info) + // }).on('error', function (err) { + // // totally unhandled error (shouldn't happen) + // console.error("replication unhandled error (shouldn't happen)", err) + // }); + console.log("DB sync enabled") + } console.warn( "Configured PouchDB database for,", this.localPouchDb.name ); return } - private enableSync(userIdentifier: string){ - console.log("DB sync init...", userIdentifier, this.getRemoteUserDb(userIdentifier)) - this.replicationHandler = this.localPouchDb.sync(this.getRemoteUserDb(userIdentifier), {live: true, retry: true}) - .on('paused', function (info) { - // replication was paused, usually because of a lost connection - console.warn("replication was paused, usually because of a lost connection", info) - }).on('active', function (info) { - // replication was resumed - console.warn("replication was resumed", info) - }).on('error', function (err) { - // totally unhandled error (shouldn't happen) - console.error("replication unhandled error (shouldn't happen)", err) - }); - console.log("DB sync enabled") - } - private getRemoteUserDb(username: string){ - return `${this.remotePouchEndpoint}/userdb-${this.toHex(username)}` - } - private toHex(s: string) { - // utf8 to latin1 - s = unescape(encodeURIComponent(s)) - let h = '' - for (let i = 0; i < s.length; i++) { - h += s.charCodeAt(i).toString(16) - } - return h - } - - private getBasePath(): string { - return window.location.pathname.split('/web').slice(0, 1)[0]; - } } diff --git a/frontend/src/lib/database/pouchdb_repository.ts b/frontend/src/lib/database/pouchdb_repository.ts index 2cea2947..f6301351 100644 --- a/frontend/src/lib/database/pouchdb_repository.ts +++ b/frontend/src/lib/database/pouchdb_repository.ts @@ -19,6 +19,9 @@ PouchDB.plugin(PouchCrypto); // PouchDB.plugin(find); // PouchDB.debug.enable('pouchdb:find') +// YOU MUST USE globalThis not window or self. +// YOU MUST NOT USE console.* as its not available in a webworker context + // this is required, otherwise PouchFind fails when looking for the global PouchDB variable @@ -33,16 +36,28 @@ export function NewRepositiory(userIdentifier?: string, encryptionKey?: string): export class PouchdbRepository implements IDatabaseRepository { - + replicationHandler: any + remotePouchEndpoint: string // "http://localhost:5984" encryptionKey: string localPouchDb: PouchDB.Database + + /** + * This class can be initialized in 2 states + * - unauthenticated + * - authenticated - determined using cookie and localStorage.current_user + * @param userIdentifier + * @param encryptionKey + */ constructor(userIdentifier?: string, encryptionKey?: string) { + this.remotePouchEndpoint = `${globalThis.location.protocol}//${globalThis.location.host}${this.getBasePath()}/database` + //setup PouchDB Plugins //https://pouchdb.com/guides/mango-queries.html this.localPouchDb = null if(userIdentifier){ this.localPouchDb = new PouchDB(userIdentifier); this.encryptionKey = encryptionKey + this.enableSync(userIdentifier) } } @@ -55,6 +70,11 @@ export class PouchdbRepository implements IDatabaseRepository { if (!this.localPouchDb) { return; } + // Stop remote replication for existing database + if(this.replicationHandler){ + this.replicationHandler.cancel() + } + this.localPouchDb.close(); this.localPouchDb = null; return @@ -251,4 +271,31 @@ export class PouchdbRepository implements IDatabaseRepository { }) } + /////////////////////////////////////////////////////////////////////////////////////// + // Sync private/protected methods + /////////////////////////////////////////////////////////////////////////////////////// + protected getRemoteUserDb(username: string){ + return `${this.remotePouchEndpoint}/userdb-${this.toHex(username)}` + } + protected enableSync(userIdentifier: string){ + return this.replicationHandler = this.localPouchDb.sync(this.getRemoteUserDb(userIdentifier), {live: true, retry: true}) + } + + + private toHex(s: string) { + // utf8 to latin1 + s = unescape(encodeURIComponent(s)) + let h = '' + for (let i = 0; i < s.length; i++) { + h += s.charCodeAt(i).toString(16) + } + return h + } + + /////////////////////////////////////////////////////////////////////////////////////// + // Helper methods + /////////////////////////////////////////////////////////////////////////////////////// + protected getBasePath(): string { + return globalThis.location.pathname.split('/web').slice(0, 1)[0]; + } }