Skip to content

Commit

Permalink
Merge pull request #1133 from dm3-org/1069-handle-same-address-with-d…
Browse files Browse the repository at this point in the history
…ifferent-domains/profiles

1069 handle same address with different domains/profiles
  • Loading branch information
AlexNi245 authored Aug 7, 2024
2 parents 94dc2e9 + ae68638 commit 1afd162
Show file tree
Hide file tree
Showing 44 changed files with 672 additions and 205 deletions.
8 changes: 8 additions & 0 deletions packages/backend/migrations/20240731092314_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:
- Added the required column `encryptedProfileLocation` to the `Conversation` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Conversation" ADD COLUMN "encryptedProfileLocation" TEXT NOT NULL;
2 changes: 2 additions & 0 deletions packages/backend/migrations/20240806084251_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EncryptedMessage" ALTER COLUMN "encryptedEnvelopContainer" SET DEFAULT '';
17 changes: 9 additions & 8 deletions packages/backend/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ generator client {
model EncryptedMessage {
id String @id
createdAt DateTime @default(now())
encryptedEnvelopContainer String
encryptedEnvelopContainer String @default("")
encryptedContactName String
conversationId String
conversation Conversation @relation(fields: [conversationId], references: [id])
Expand All @@ -22,13 +22,14 @@ model EncryptedMessage {
}

model Conversation {
id String @id @default(uuid())
updatedAt DateTime @default(now())
encryptedContactName String
Message EncryptedMessage[]
Account Account @relation(fields: [accountId], references: [id])
accountId String
isHidden Boolean @default(false)
id String @id @default(uuid())
updatedAt DateTime @default(now())
encryptedContactName String
encryptedProfileLocation String
Message EncryptedMessage[]
Account Account @relation(fields: [accountId], references: [id])
accountId String
isHidden Boolean @default(false)
}

model Account {
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/persistence/getDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface IDatabase extends IAccountDatabase {
addConversation: (
ensName: string,
encryptedContactName: string,
encryptedProfileLocation: string,
) => Promise<boolean>;
getConversationList: (
ensName: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@ import { PrismaClient } from '@prisma/client';
import { getOrCreateAccount } from './utils/getOrCreateAccount';
import { getOrCreateConversation } from './utils/getOrCreateConversation';
export const addConversation =
(db: PrismaClient) => async (ensName: string, contactName: string) => {
(db: PrismaClient) =>
async (
ensName: string,
contactName: string,
encryptedProfileLocation: string,
) => {
try {
const account = await getOrCreateAccount(db, ensName);
await getOrCreateConversation(db, account.id, contactName);
await getOrCreateConversation(
db,
account.id,
contactName,
encryptedProfileLocation,
);
return true;
} catch (e) {
console.log('addConversation error ', e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ export type ConversationRecord = {
previewMessage: string | null;
//The time the conversation was last updated
updatedAt: Date;
//This field can be used by the client to store information about the contacts TLD name
encryptedProfileLocation: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ export const getConversationList =
previewMessage:
previewMessages[idx]?.encryptedEnvelopContainer ?? null,
updatedAt: c.updatedAt,
encryptedProfileLocation: c.encryptedProfileLocation,
}));
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const getOrCreateConversation = async (
db: PrismaClient,
accountId: string,
encryptedContactName: string,
encryptedProfileLocation: string = '',
) => {
//Check if conversation already exists
const conversation = await db.conversation.findFirst({
Expand All @@ -20,6 +21,7 @@ export const getOrCreateConversation = async (
return await db.conversation.create({
data: {
accountId,
encryptedProfileLocation,
encryptedContactName,
//Internal field to order conversations properly
//Will set whenever a conversation is created or a message is added
Expand Down
26 changes: 26 additions & 0 deletions packages/backend/src/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,32 @@ describe('Storage', () => {
expect(body[0].contact).toEqual(aliceId);
expect(body.length).toBe(1);
});
it('can add conversation with encryptedProfileLocation', async () => {
const aliceId = 'alice.eth';

const { status } = await request(app)
.post(`/new/bob.eth/addConversation`)
.set({
authorization: 'Bearer ' + token,
})
.send({
encryptedContactName: aliceId,
encryptedProfileLocation: '123',
});
expect(status).toBe(200);

const { body } = await request(app)
.get(`/new/bob.eth/getConversations`)
.set({
authorization: 'Bearer ' + token,
})
.send();

expect(status).toBe(200);
expect(body[0].contact).toEqual(aliceId);
expect(body[0].encryptedProfileLocation).toEqual('123');
expect(body.length).toBe(1);
});
it('handle duplicates add conversation', async () => {
const aliceId = 'alice.eth';
const ronId = 'ron.eth';
Expand Down
6 changes: 5 additions & 1 deletion packages/backend/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,16 +196,20 @@ export default (
);

router.post('/new/:ensName/addConversation', async (req, res, next) => {
const { encryptedContactName } = req.body;
const { encryptedContactName, encryptedProfileLocation } = req.body;
if (!encryptedContactName) {
res.status(400).send('invalid schema');
return;
}

//Param encryptedProfileLocation is optional, hence the default value is an empty string
const _encryptedProfileLocation = encryptedProfileLocation || '';
try {
const ensName = normalizeEnsName(req.params.ensName);
const success = await db.addConversation(
ensName,
encryptedContactName,
_encryptedProfileLocation,
);
if (success) {
return res.send();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { stringify } from '@dm3-org/dm3-lib-shared';
import { Redis, RedisPrefix } from '../getDatabase';
import { getMessages } from './getMessages';

Expand Down Expand Up @@ -33,7 +34,7 @@ export function syncAcknowledge(redis: Redis) {
//remove the message from the sorted set
const res = await redis.zRem(
RedisPrefix.Conversation + conversationId,
JSON.stringify(message),
stringify(message),
);
//returns true if the message is removed successfully
return !!res;
Expand Down
5 changes: 0 additions & 5 deletions packages/lib/delivery/src/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,13 @@ export async function checkToken(
return false;
}

console.debug('checkToken - ensName', ensName);
console.debug('checkToken - session', session);

// check jwt for validity
try {
// will throw if signature is invalid or exp is in the past
const jwtPayload = verify(token, serverSecret, {
algorithms: ['HS256'],
});

console.debug('checkToken - jwtPayload', jwtPayload);

// check if payload is well formed
if (
typeof jwtPayload === 'string' ||
Expand Down
7 changes: 6 additions & 1 deletion packages/lib/shared/src/IBackendConnector.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
export interface IBackendConnector {
addConversation(ensName: string, encryptedContactName: string): void;
addConversation(
ensName: string,
encryptedContactName: string,
encryptedProfileLocation: string,
): void;
getConversations(
ensName: string,
size: number,
offset: number,
): Promise<
{
contact: string;
encryptedProfileLocation: string;
previewMessage: string;
updatedAt: Date;
}[]
Expand Down
29 changes: 24 additions & 5 deletions packages/lib/storage/src/new/cloudStorage/getCloudStorage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IBackendConnector } from '@dm3-org/dm3-lib-shared';
import { IBackendConnector, stringify } from '@dm3-org/dm3-lib-shared';
import { MessageRecord } from '../chunkStorage/ChunkStorageTypes';
import {
Encryption,
Expand All @@ -13,13 +13,23 @@ export const getCloudStorage = (
ensName: string,
encryption: Encryption,
): StorageAPI => {
const _addConversation = async (contactEnsName: string) => {
const _addConversation = async (
contactEnsName: string,
contactProfileLocation: string[],
) => {
const encryptedContactName = await encryption.encryptSync(
contactEnsName,
);

const encryptedProfileLocation = await encryption.encryptSync(
stringify(contactProfileLocation),
);

console.log('add contact ', contactEnsName, contactProfileLocation);
return await backendConnector.addConversation(
ensName,
encryptedContactName,
encryptedProfileLocation,
);
};

Expand All @@ -34,14 +44,23 @@ export const getCloudStorage = (
conversations.map(
async ({
contact,
encryptedProfileLocation,
previewMessage,
updatedAt,
}: {
contact: string;
encryptedProfileLocation: string;
previewMessage: string | null;
updatedAt: Date;
}) => ({
contactEnsName: await encryption.decryptSync(contact),
contactProfileLocation: encryptedProfileLocation
? JSON.parse(
await encryption.decryptSync(
encryptedProfileLocation,
),
)
: [],
isHidden: false,
messageCounter: 0,
previewMessage: previewMessage
Expand Down Expand Up @@ -119,7 +138,7 @@ export const getCloudStorage = (
contactEnsName,
);
const encryptedEnvelopContainer = await encryption.encryptAsync(
JSON.stringify(envelop),
stringify(envelop),
);

//The client defines the createdAt timestamp for the message so it can be used to sort the messages
Expand Down Expand Up @@ -150,7 +169,7 @@ export const getCloudStorage = (
async (storageEnvelopContainer: StorageEnvelopContainer) => {
const encryptedEnvelopContainer =
await encryption.encryptAsync(
JSON.stringify(storageEnvelopContainer),
stringify(storageEnvelopContainer),
);
//The client defines the createdAt timestamp for the message so it can be used to sort the messages
const createdAt = Date.now();
Expand Down Expand Up @@ -190,7 +209,7 @@ export const getCloudStorage = (
async (storageEnvelopContainer: StorageEnvelopContainer) => {
const encryptedEnvelopContainer =
await encryption.encryptAsync(
JSON.stringify(storageEnvelopContainer),
stringify(storageEnvelopContainer),
);
return {
encryptedEnvelopContainer,
Expand Down
9 changes: 7 additions & 2 deletions packages/lib/storage/src/new/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export interface StorageAPI {
) => Promise<void>;
getNumberOfMessages: (contactEnsName: string) => Promise<number>;
getNumberOfConverations: () => Promise<number>;
addConversation: (contactEnsName: string) => Promise<void>;
addConversation: (
contactEnsName: string,
contactProfileLocation: string[],
) => Promise<void>;
addMessage: (
contactEnsName: string,
envelop: StorageEnvelopContainer,
Expand All @@ -46,8 +49,10 @@ export interface HaltedStorageEnvelopContainer {
}

export interface Conversation {
//the contactEnsName is the ensName of the contact
//the contactEnsName is the ensName of the contact used as the id of the conversation
contactEnsName: string;
//The contact might have certain tld associated with it
contactProfileLocation: string[];
//the previewMessage is the last message of the conversation
previewMessage?: StorageEnvelopContainer;
//isHidden is a flag to hide the conversation from the conversation list
Expand Down
Loading

0 comments on commit 1afd162

Please sign in to comment.