Creating replication documents when needed

This commit is contained in:
Eric van der Vlist 2023-03-02 20:24:04 +01:00
parent b7dd6aa15a
commit d6538843d2
5 changed files with 135 additions and 1 deletions

View File

@ -19,6 +19,8 @@ import { isFunction } from 'lodash';
import { userId } from '~/lib/user-id';
import { userExists } from '~/lib/user-exists';
import { update } from '~/lib/update';
import { replicationDocument } from '~/lib/replication-document';
import { get } from '~/lib/get';
interface Props {
values?: () => any;
@ -114,6 +116,10 @@ const User: Component<Props> = (props) => {
database,
};
const currentUserId = userId(data('username'), data('database'));
const credentials = adminCredentials();
const adminUser = credentials?.username || '';
for (let i = 0; i < updatedSubscriptions.length; i++) {
let subscription = updatedSubscriptions[i];
if (!isIn(subscription.username, previousSubscriptions())) {
@ -122,6 +128,24 @@ const User: Component<Props> = (props) => {
addSubscriptionFactory(subscription.username),
defaultUserDocument
);
const otherUserId = userId(subscription.username, data('database'));
const otherUserPassword = (await get(otherUserId)).password;
let repDocument = replicationDocument(
currentUserId,
data('password'),
otherUserId,
otherUserPassword,
adminUser
);
await put(repDocument._id, repDocument, false, '_replicator');
repDocument = replicationDocument(
otherUserId,
otherUserPassword,
currentUserId,
data('password'),
adminUser
);
await put(repDocument._id, repDocument, false, '_replicator');
console.log({
caller: 'User / submitHandler / new subscription',
username: subscription.username,
@ -139,6 +163,7 @@ const User: Component<Props> = (props) => {
removeSubscriptionFactory(subscription.username),
defaultUserDocument
);
console.log({
caller: 'User / submitHandler / deleted subscription',
username: subscription.username,
@ -269,7 +294,7 @@ const User: Component<Props> = (props) => {
<Progress.Root
value={progress()}
minValue={0}
maxValue={10}
maxValue={3}
getValueLabel={({ value, max }) =>
`${value} of ${max} tasks completed`
}

View File

@ -0,0 +1,4 @@
import { toHex } from './to-hex';
export const getDbUrlForUser = (user: { username: string; hostname: string }) =>
`https://${user.hostname}/userdb-${toHex(user.username)}`;

View File

@ -0,0 +1,51 @@
import { parseUserId } from './user-id';
import { getDbUrlForUser } from './get-db-url-for-user';
export const replicationDocument = (
userFromId: string,
userFromPassword: string,
userToId: string,
userToPassword: string,
adminUser: string
) => {
const userFrom = parseUserId(userFromId);
const userTo = parseUserId(userToId);
const document = {
_id: `${userFromId}=>${userToId}`,
user_ctx: {
name: adminUser,
roles: ['_admin', '_reader', '_writer'],
},
source: {
url: getDbUrlForUser(userFrom),
auth: {
basic: {
username: userFrom.username,
password: userFromPassword,
},
},
},
target: {
url: getDbUrlForUser(userTo),
auth: {
basic: {
username: userTo.username,
password: userToPassword,
},
},
},
selector: {
to: {
$elemMatch: {
$eq: userToId,
},
},
},
create_target: false,
continuous: true,
owner: adminUser,
};
return document;
};

24
src/lib/update.ts Normal file
View File

@ -0,0 +1,24 @@
import { get } from './get';
import { put } from './put';
export const update = async (
id: string,
updater: (doc: any) => any,
defaultDocument: any,
db = 'dyomedea_users'
) => {
const previous = await get(id, db);
const newDocument = updater(previous || defaultDocument);
console.log({
caller: 'update',
id,
defaultDocument,
db,
previous,
newDocument,
});
return await put(id, newDocument, false, db);
};

30
src/lib/user-exists.ts Normal file
View File

@ -0,0 +1,30 @@
import { adminCredentials } from '~/components/credentials';
import { headersWithAuth } from './headers-with-auth';
export const userExists = async (username: string) => {
const credentials = adminCredentials();
if (!credentials) {
return null;
}
const { database } = credentials;
const headers = headersWithAuth();
if (!headers) {
return null;
}
try {
const response = await fetch(
`${database}/_users/org.couchdb.user:${username}`,
{
method: 'HEAD',
mode: 'cors',
headers,
}
);
return response.status === 200;
} catch (error) {
console.error({ caller: 'userExists', error });
return null;
}
};