fixed standalone couchdb setup.

fixed couchdb sync.
This commit is contained in:
Jason Kulatunga 2022-10-10 08:11:00 -07:00
parent 981fcfbc7b
commit 2e21176ad7
5 changed files with 85 additions and 73 deletions

View File

@ -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 / \ && tar xzf /tmp/s6-overlay-${S6_ARCH}.tar.gz -C / \
&& rm -rf /tmp/s6-overlay-${S6_ARCH}.tar.gz && 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 / COPY /docker/rootfs /
RUN rm -rf /etc/services.d/fasten #delete the fasten app from the couchdbase container. RUN rm -rf /etc/services.d/fasten #delete the fasten app from the couchdbase container.

View File

@ -3,10 +3,11 @@
if [ -f "/opt/couchdb/data/.init_complete" ]; then if [ -f "/opt/couchdb/data/.init_complete" ]; then
echo "Couchdb initialization has already completed, skipping" echo "Couchdb initialization has already completed, skipping"
else else
cp /opt/couchdb/etc/setup.ini /opt/couchdb/etc/local.d/setup.ini
# start couchdb as a background process (store PID) # 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 # 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 & /opt/couchdb/bin/couchdb -b &
COUCHDB_PID=$! COUCHDB_PID=$!
@ -15,16 +16,17 @@ else
# create couch_peruser required system databases manually on startup # create couch_peruser required system databases manually on startup
echo "couchdb ready, start creating system databases" 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:mysecretpassword 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:mysecretpassword 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/_global_changes
echo "system databases created successfully" echo "system databases created successfully"
# gracefully stop couchdb process # gracefully stop couchdb process
echo "killing couchdb process" echo "killing couchdb process"
/opt/couchdb/bin/couchdb -k /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 # create the init complete flag
echo "Couchdb initialization: complete" echo "Couchdb initialization: complete"

View File

@ -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

View File

@ -25,16 +25,11 @@ PouchDB.plugin(PouchAuth);
}) })
export class FastenDbService extends PouchdbRepository { 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) { constructor(private _httpClient: HttpClient) {
const userIdentifier = localStorage.getItem("current_user") const userIdentifier = localStorage.getItem("current_user")
super(userIdentifier, "my-secret-encryption-key"); 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)=>{ .then((loginResp)=>{
return this.postLoginHook(loginResp.name, true) 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. //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 remotePouchDb = new PouchDB(this.getRemoteUserDb(localStorage.getItem("current_user")), {skip_setup: true});
const session = await remotePouchDb.getSession() const session = await remotePouchDb.getSession()
const isAuth = !!session?.userCtx?.name const authUser = session?.userCtx?.name
const isAuth = !!authUser
console.warn("IsAuthenticated? getSession() ====> ", isAuth) console.warn("IsAuthenticated? getSession() ====> ", isAuth)
if(!isAuth){
return isAuth return false
}
//confirm that the logged in user matches the session user
return authUser == localStorage.getItem("current_user")
} catch (e) { } catch (e) {
return false return false
} }
} }
public Close(): Promise<void> { public Close(): Promise<void> {
// Stop remote replication for existing database
if(this.replicationHandler){
this.replicationHandler.cancel()
}
return super.Close() return super.Close()
} }
@ -182,42 +180,25 @@ export class FastenDbService extends PouchdbRepository {
console.log("DB createIndex complete", createIndexMsg) console.log("DB createIndex complete", createIndexMsg)
if(sync){ if(sync){
console.log("DB sync init...", userIdentifier, this.getRemoteUserDb(userIdentifier))
this.enableSync(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 ); console.warn( "Configured PouchDB database for,", this.localPouchDb.name );
return 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];
}
} }

View File

@ -19,6 +19,9 @@ PouchDB.plugin(PouchCrypto);
// PouchDB.plugin(find); // PouchDB.plugin(find);
// PouchDB.debug.enable('pouchdb: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 // 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 { export class PouchdbRepository implements IDatabaseRepository {
replicationHandler: any
remotePouchEndpoint: string // "http://localhost:5984"
encryptionKey: string encryptionKey: string
localPouchDb: PouchDB.Database 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) { constructor(userIdentifier?: string, encryptionKey?: string) {
this.remotePouchEndpoint = `${globalThis.location.protocol}//${globalThis.location.host}${this.getBasePath()}/database`
//setup PouchDB Plugins //setup PouchDB Plugins
//https://pouchdb.com/guides/mango-queries.html //https://pouchdb.com/guides/mango-queries.html
this.localPouchDb = null this.localPouchDb = null
if(userIdentifier){ if(userIdentifier){
this.localPouchDb = new PouchDB(userIdentifier); this.localPouchDb = new PouchDB(userIdentifier);
this.encryptionKey = encryptionKey this.encryptionKey = encryptionKey
this.enableSync(userIdentifier)
} }
} }
@ -55,6 +70,11 @@ export class PouchdbRepository implements IDatabaseRepository {
if (!this.localPouchDb) { if (!this.localPouchDb) {
return; return;
} }
// Stop remote replication for existing database
if(this.replicationHandler){
this.replicationHandler.cancel()
}
this.localPouchDb.close(); this.localPouchDb.close();
this.localPouchDb = null; this.localPouchDb = null;
return 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];
}
} }