mirror of
https://github.com/Discreetly/server.git
synced 2026-01-09 12:37:58 -05:00
refactored db and crypto folders/exports
This commit is contained in:
4
src/crypto/index.ts
Normal file
4
src/crypto/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './rateCommitmentHasher';
|
||||
export * from './shamirRecovery';
|
||||
export * from './signalHash';
|
||||
export * from './verifier';
|
||||
@@ -1,7 +1,8 @@
|
||||
import { poseidon2 } from 'poseidon-lite/poseidon2';
|
||||
|
||||
function getRateCommitmentHash(identityCommitment: bigint, userMessageLimit: number | bigint) {
|
||||
export function getRateCommitmentHash(
|
||||
identityCommitment: bigint,
|
||||
userMessageLimit: number | bigint
|
||||
): bigint {
|
||||
return poseidon2([identityCommitment, userMessageLimit]);
|
||||
}
|
||||
|
||||
export default getRateCommitmentHash;
|
||||
|
||||
@@ -6,7 +6,11 @@ import { calculateSignalHash } from './signalHash';
|
||||
|
||||
const v = new RLNVerifier(vkey);
|
||||
|
||||
async function verifyProof(msg: MessageI, room: RoomI, epochErrorRange = 5): Promise<boolean> {
|
||||
export async function verifyProof(
|
||||
room: RoomI,
|
||||
msg: MessageI,
|
||||
epochErrorRange = 5
|
||||
): Promise<boolean> {
|
||||
if (!msg.roomId || !msg.message || !msg.proof || !msg.epoch) {
|
||||
console.warn('Missing required fields:', msg);
|
||||
return false;
|
||||
@@ -62,5 +66,3 @@ async function verifyProof(msg: MessageI, room: RoomI, epochErrorRange = 5): Pro
|
||||
// Check that the proof is correct
|
||||
return v.verifyProof(rlnIdentifier, proof);
|
||||
}
|
||||
|
||||
export default verifyProof;
|
||||
|
||||
451
src/data/db.ts
451
src/data/db.ts
@@ -1,451 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { genId } from 'discreetly-interfaces';
|
||||
import type { RoomI } from 'discreetly-interfaces';
|
||||
import { serverConfig } from '../config/serverConfig';
|
||||
import { genMockUsers, genClaimCodeArray, pp } from '../utils';
|
||||
import getRateCommitmentHash from '../crypto/rateCommitmentHasher';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
interface CodeStatus {
|
||||
claimed: boolean;
|
||||
roomIds: string[];
|
||||
}
|
||||
|
||||
interface RoomsFromClaimCode {
|
||||
roomIds: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a room by id
|
||||
* @param {string} id The id of the room to get
|
||||
* @returns {Promise<RoomI | null>}The room, or null if it doesn't exist
|
||||
*/
|
||||
|
||||
export async function getRoomByID(id: string): Promise<RoomI | null> {
|
||||
const room = await prisma.rooms
|
||||
.findUnique({
|
||||
where: {
|
||||
roomId: id
|
||||
},
|
||||
// Filter out the information we want from the room
|
||||
select: {
|
||||
id: true,
|
||||
roomId: true,
|
||||
name: true,
|
||||
identities: true,
|
||||
rateLimit: true,
|
||||
userMessageLimit: true,
|
||||
membershipType: true,
|
||||
contractAddress: true,
|
||||
bandadaAddress: true,
|
||||
bandadaGroupId: true,
|
||||
type: true
|
||||
}
|
||||
})
|
||||
.then((room) => {
|
||||
return room;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
throw err; // Add this line to throw the error
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
if (room) {
|
||||
resolve(room as RoomI);
|
||||
}
|
||||
reject('Room not found');
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Need to create a system here where the client needs to provide a
|
||||
proof they know the secrets to some Identity Commitment with a unix epoch
|
||||
time stamp to prevent replay attacks
|
||||
|
||||
https://github.com/Discreetly/IdentityCommitmentNullifierCircuit <- Circuit and JS to do this
|
||||
*/
|
||||
|
||||
/**
|
||||
* This function takes in an identity and returns the rooms the identity is in.
|
||||
* @param identity - the identity of a user
|
||||
* @returns an array of roomIds
|
||||
*/
|
||||
|
||||
export async function getRoomsByIdentity(identity: string): Promise<string[]> {
|
||||
const r: string[] = [];
|
||||
try {
|
||||
const rooms = await prisma.rooms.findMany({
|
||||
where: {
|
||||
semaphoreIdentities: {
|
||||
has: identity
|
||||
}
|
||||
}
|
||||
});
|
||||
rooms.forEach((room) => {
|
||||
r.push(room.roomId);
|
||||
});
|
||||
return r;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a claim code in the database.
|
||||
*
|
||||
* @param {string} code - The code to find.
|
||||
* @returns {Promise<CodeStatus | null>} - The claim code, if found.
|
||||
*/
|
||||
|
||||
export function findClaimCode(code: string): Promise<CodeStatus | null> {
|
||||
return prisma.claimCodes.findUnique({
|
||||
where: { claimcode: code }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the claim_code table to mark the given code as claimed.
|
||||
* @param {string} code - The code to update
|
||||
* @returns {Promise<RoomsFromClaimCode>} - The rooms associated with the claim code
|
||||
*/
|
||||
|
||||
export function updateClaimCode(code: string): Promise<RoomsFromClaimCode> {
|
||||
return prisma.claimCodes.update({
|
||||
where: { claimcode: code },
|
||||
data: { claimed: true }
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
The sanitizeIDC function takes a string and returns a string.
|
||||
The string is converted to a BigInt and then back to a string.
|
||||
If the string has no loss of precision, it is returned.
|
||||
Otherwise, an error is thrown.
|
||||
*/
|
||||
|
||||
function sanitizeIDC(idc: string): string {
|
||||
try {
|
||||
const tempBigInt = BigInt(idc);
|
||||
const tempString = tempBigInt.toString();
|
||||
if (idc === tempString) {
|
||||
return idc;
|
||||
} else {
|
||||
throw new Error('Invalid IDC provided.');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error('Invalid IDC provided.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This code updates the identity commitments of a list of rooms.
|
||||
* It adds the identity commitment to the identity list of each room,
|
||||
* and also adds it to the bandada of each room. The identity commitment is
|
||||
* sanitized before being added to the database.
|
||||
* @param idc - The identity commitment of the user
|
||||
* @param roomIds - The list of roomIds that the user is in
|
||||
* @returns {Promise<void>} - A promise that resolves when the update is complete
|
||||
*/
|
||||
|
||||
export async function updateRoomIdentities(
|
||||
idc: string,
|
||||
roomIds: string[]
|
||||
): Promise<string[] | void> {
|
||||
const identityCommitment = sanitizeIDC(idc);
|
||||
return await prisma.rooms
|
||||
.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
})
|
||||
.then(async (rooms) => {
|
||||
const identityRooms = await addIdentityToIdentityListRooms(rooms, identityCommitment);
|
||||
const bandadaRooms = await addIdentityToBandadaRooms(rooms, identityCommitment);
|
||||
return [...identityRooms, ...bandadaRooms] as string[];
|
||||
})
|
||||
.catch((err) => {
|
||||
pp(err, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user's identity commitment to the semaphoreIdentities list and adds their rate commitment to the identities list for each of the identity list rooms that they are in.
|
||||
* @param {rooms} - The list of rooms that the user is in
|
||||
* @param {string} identityCommitment - The user's identity commitment
|
||||
* @return {string[]} addedRooms - The list of rooms that the user was added to
|
||||
*/
|
||||
async function addIdentityToIdentityListRooms(
|
||||
rooms,
|
||||
identityCommitment: string
|
||||
): Promise<string[]> {
|
||||
const identityListRooms = rooms
|
||||
.filter(
|
||||
(room: RoomI) =>
|
||||
room.membershipType === 'IDENTITY_LIST' &&
|
||||
!room.semaphoreIdentities?.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room.id as string);
|
||||
|
||||
const addedRooms: string[] = [];
|
||||
|
||||
const promises = identityListRooms.map(async (roomId) => {
|
||||
const room = rooms.find((r) => r.id === roomId);
|
||||
if (room) {
|
||||
try {
|
||||
await prisma.rooms.update({
|
||||
where: { id: roomId },
|
||||
data: {
|
||||
identities: {
|
||||
push: getRateCommitmentHash(
|
||||
BigInt(identityCommitment),
|
||||
BigInt((room.userMessageLimit as number) ?? 1)
|
||||
).toString()
|
||||
},
|
||||
semaphoreIdentities: { push: identityCommitment }
|
||||
}
|
||||
});
|
||||
console.debug(`Successfully added user to Identity List room ${room.roomId}`);
|
||||
addedRooms.push(roomId as string);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
return addedRooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* This code adds a new identity commitment to the list of identities in a bandada room.
|
||||
* First we get the list of bandada rooms that contain the identity commitment.
|
||||
* Then we iterate over the list of rooms and add the identity commitment to each room.
|
||||
* After that we update the list of identities in each room in the database.
|
||||
* Finally, we send a POST request to the bandada server to add the identity to the group.
|
||||
* @param {RoomI[]} rooms - The list of rooms that contain the identity commitment.
|
||||
* @param {string} identityCommitment - The identity commitment to be added to the bandada room.
|
||||
* @return {string[]} addedRooms - The list of rooms that the user was added to
|
||||
*/
|
||||
|
||||
async function addIdentityToBandadaRooms(rooms, identityCommitment: string): Promise<string[]> {
|
||||
const bandadaGroupRooms = rooms
|
||||
.filter(
|
||||
(room: RoomI) =>
|
||||
room.membershipType === 'BANDADA_GROUP' &&
|
||||
!room.semaphoreIdentities?.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room as RoomI);
|
||||
|
||||
const addedRooms: string[] = [];
|
||||
|
||||
if (bandadaGroupRooms.length > 0) {
|
||||
const promises = bandadaGroupRooms.map(async (room) => {
|
||||
const rateCommitment = getRateCommitmentHash(
|
||||
BigInt(identityCommitment),
|
||||
BigInt((room.userMessageLimit as number) ?? 1)
|
||||
).toString();
|
||||
|
||||
if (!room.bandadaAPIKey) {
|
||||
console.error('API key is missing for room:', room);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': room.bandadaAPIKey
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
await prisma.rooms.update({
|
||||
where: { id: room.id },
|
||||
data: {
|
||||
identities: {
|
||||
push: rateCommitment
|
||||
},
|
||||
semaphoreIdentities: { push: identityCommitment }
|
||||
}
|
||||
});
|
||||
|
||||
const url = `https://${room.bandadaAddress}/groups/${room.bandadaGroupId}/members/${rateCommitment}`;
|
||||
const response = await fetch(url, requestOptions);
|
||||
console.log(response);
|
||||
if (response.status == 201) {
|
||||
console.debug(`Successfully added user to Bandada group ${room.bandadaAddress}`);
|
||||
addedRooms.push(room.id as string);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
return addedRooms;
|
||||
}
|
||||
/**
|
||||
* This function is used to find rooms that have been updated
|
||||
* It is used in the findUpdatedRooms function
|
||||
* It is important because it allows the user to see which rooms have been updated
|
||||
* @param {string[]} roomIds - The list of roomIds that the user is in
|
||||
* @returns {Promise<RoomI[]>} - A promise that resolves to a list of rooms
|
||||
*/
|
||||
|
||||
export async function findUpdatedRooms(roomIds: string[]): Promise<RoomI[]> {
|
||||
const rooms = await prisma.rooms.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
if (rooms) {
|
||||
resolve(rooms as RoomI[]);
|
||||
}
|
||||
reject('No rooms found');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates a system message in a room.
|
||||
* The message will be the same in all rooms if no roomId is passed.
|
||||
* If a roomId is passed, the message will be created in that room.
|
||||
* @param {string} message - The message to be created
|
||||
* @param {string} roomId - The roomId to create the message in
|
||||
*/
|
||||
export function createSystemMessages(message: string, roomId?: string): Promise<unknown> {
|
||||
const query = roomId ? { where: { roomId } } : undefined;
|
||||
return prisma.rooms
|
||||
.findMany(query)
|
||||
.then((rooms) => {
|
||||
if (roomId && rooms.length === 0) {
|
||||
return Promise.reject('Room not found');
|
||||
}
|
||||
const createMessages = rooms.map((room) => {
|
||||
return prisma.messages.create({
|
||||
data: {
|
||||
message,
|
||||
roomId: room.roomId,
|
||||
messageId: '0',
|
||||
proof: JSON.stringify({})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(createMessages);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
export interface BandadaRoom extends RoomI {
|
||||
bandadaAPIKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes in an identity and a room and removes the identity from the room
|
||||
* by setting its semaphoreIdentities to 0n and identities to 0n
|
||||
* @param {string} idc - The identity of the user
|
||||
* @param {RoomI} room - The room to remove the identity from
|
||||
* @returns {Promise<void | RoomI>} - A promise that resolves to the room
|
||||
*/
|
||||
|
||||
export function removeIdentityFromRoom(idc: string, room: RoomI): Promise<void | RoomI> {
|
||||
const updateSemaphoreIdentities =
|
||||
room.semaphoreIdentities?.map((identity) => (identity === idc ? '0' : (identity as string))) ??
|
||||
[];
|
||||
|
||||
const rateCommitmentsToUpdate = getRateCommitmentHash(
|
||||
BigInt(idc),
|
||||
BigInt(room.userMessageLimit!)
|
||||
).toString();
|
||||
|
||||
const updatedRateCommitments =
|
||||
room.identities?.map((limiter) =>
|
||||
limiter == rateCommitmentsToUpdate ? '0' : (limiter as string)
|
||||
) ?? [];
|
||||
|
||||
return prisma.rooms
|
||||
.update({
|
||||
where: { id: room.id },
|
||||
data: {
|
||||
identities: updatedRateCommitments,
|
||||
semaphoreIdentities: updateSemaphoreIdentities
|
||||
}
|
||||
})
|
||||
.then((room) => {
|
||||
return room as RoomI;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new room with the given name and optional parameters.
|
||||
* @param {string} name - The name of the room.
|
||||
* @param {number} [rateLimit=1000] - The length of an epoch in milliseconds
|
||||
* @param {number} [userMessageLimit=1] - The message limit per user per epoch
|
||||
* @param {number} [numClaimCodes=0] - The number of claim codes to generate for the room.
|
||||
* @param {number} [approxNumMockUsers=20] - The approximate number of mock users to generate for the room.
|
||||
* @param {string} [type='IDENTITY_LIST'] - The type of room to create.
|
||||
* @param {string} [bandadaAddress] - The address of the bandada server.
|
||||
* @param {string} [bandadaGroupId] - The id of the bandada group.
|
||||
* @param {string} [bandadaAPIKey] - The API key for the bandada server.
|
||||
* @param {string} [membershipType] - The membership type of the room.
|
||||
* @returns {Promise<boolean>} - A promise that resolves to true if the room was created successfully.
|
||||
*/
|
||||
export async function createRoom(
|
||||
roomName: string,
|
||||
rateLimit = 1000,
|
||||
userMessageLimit = 1,
|
||||
numClaimCodes = 0,
|
||||
approxNumMockUsers = 20,
|
||||
type: string,
|
||||
bandadaAddress?: string,
|
||||
bandadaGroupId?: string,
|
||||
bandadaAPIKey?: string,
|
||||
membershipType?: string
|
||||
): Promise<boolean> {
|
||||
const claimCodes: { claimcode: string }[] = genClaimCodeArray(numClaimCodes);
|
||||
const mockUsers: string[] = genMockUsers(approxNumMockUsers);
|
||||
const identityCommitments: string[] = mockUsers.map((user) =>
|
||||
getRateCommitmentHash(BigInt(user), BigInt(userMessageLimit)).toString()
|
||||
);
|
||||
const roomData = {
|
||||
where: {
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString()
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString(),
|
||||
name: roomName,
|
||||
rateLimit: rateLimit,
|
||||
userMessageLimit: userMessageLimit,
|
||||
semaphoreIdentities: mockUsers,
|
||||
identities: identityCommitments,
|
||||
type,
|
||||
bandadaAddress,
|
||||
bandadaGroupId,
|
||||
bandadaAPIKey,
|
||||
membershipType,
|
||||
claimCodes: {
|
||||
create: claimCodes
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return await prisma.rooms
|
||||
.upsert(roomData)
|
||||
.then(() => {
|
||||
return true;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
137
src/data/db/create.ts
Normal file
137
src/data/db/create.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { getRateCommitmentHash, genId, MessageI } from 'discreetly-interfaces';
|
||||
import { serverConfig } from '../../config/serverConfig';
|
||||
import { genClaimCodeArray, genMockUsers } from '../../utils';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* Creates a new room with the given name and optional parameters.
|
||||
* @param {string} name - The name of the room.
|
||||
* @param {number} [rateLimit=1000] - The length of an epoch in milliseconds
|
||||
* @param {number} [userMessageLimit=1] - The message limit per user per epoch
|
||||
* @param {number} [numClaimCodes=0] - The number of claim codes to generate for the room.
|
||||
* @param {number} [approxNumMockUsers=20] - The approximate number of mock users to generate for the room.
|
||||
* @param {string} [type='IDENTITY_LIST'] - The type of room to create.
|
||||
* @param {string} [bandadaAddress] - The address of the bandada server.
|
||||
* @param {string} [bandadaGroupId] - The id of the bandada group.
|
||||
* @param {string} [bandadaAPIKey] - The API key for the bandada server.
|
||||
* @param {string} [membershipType] - The membership type of the room.
|
||||
* @returns {Promise<boolean>} - A promise that resolves to true if the room was created successfully.
|
||||
*/
|
||||
export async function createRoom(
|
||||
roomName: string,
|
||||
rateLimit = 1000,
|
||||
userMessageLimit = 1,
|
||||
numClaimCodes = 0,
|
||||
approxNumMockUsers = 20,
|
||||
type: string,
|
||||
bandadaAddress?: string,
|
||||
bandadaGroupId?: string,
|
||||
bandadaAPIKey?: string,
|
||||
membershipType?: string
|
||||
): Promise<boolean> {
|
||||
const claimCodes: { claimcode: string }[] = genClaimCodeArray(numClaimCodes);
|
||||
const mockUsers: string[] = genMockUsers(approxNumMockUsers);
|
||||
const identityCommitments: string[] = mockUsers.map((user) =>
|
||||
getRateCommitmentHash(BigInt(user), BigInt(userMessageLimit)).toString()
|
||||
);
|
||||
const roomData = {
|
||||
where: {
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString()
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString(),
|
||||
name: roomName,
|
||||
rateLimit: rateLimit,
|
||||
userMessageLimit: userMessageLimit,
|
||||
semaphoreIdentities: mockUsers,
|
||||
identities: identityCommitments,
|
||||
type,
|
||||
bandadaAddress,
|
||||
bandadaGroupId,
|
||||
bandadaAPIKey,
|
||||
membershipType,
|
||||
claimCodes: {
|
||||
create: claimCodes
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return await prisma.rooms
|
||||
.upsert(roomData)
|
||||
.then(() => {
|
||||
return true;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates a system message in a room.
|
||||
* The message will be the same in all rooms if no roomId is passed.
|
||||
* If a roomId is passed, the message will be created in that room.
|
||||
* @param {string} message - The message to be created
|
||||
* @param {string} roomId - The roomId to create the message in
|
||||
*/
|
||||
export function createSystemMessages(message: string, roomId?: string): Promise<unknown> {
|
||||
const query = roomId ? { where: { roomId } } : undefined;
|
||||
return prisma.rooms
|
||||
.findMany(query)
|
||||
.then((rooms) => {
|
||||
if (roomId && rooms.length === 0) {
|
||||
return Promise.reject('Room not found');
|
||||
}
|
||||
const createMessages = rooms.map((room) => {
|
||||
return prisma.messages.create({
|
||||
data: {
|
||||
message,
|
||||
roomId: room.roomId,
|
||||
messageId: '0',
|
||||
proof: JSON.stringify({})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(createMessages);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to a room.
|
||||
* @param {string} roomId - The ID of the room to add the message to.
|
||||
* @param {MessageI} message - The message to add to the room.
|
||||
* @returns {Promise<unknown>} - A promise that resolves when the message has been added to the room.
|
||||
*/
|
||||
export function createMessageInRoom(roomId: string, message: MessageI): Promise<unknown> {
|
||||
if (!message.epoch) {
|
||||
throw new Error('Epoch not provided');
|
||||
}
|
||||
return prisma.rooms.update({
|
||||
where: {
|
||||
roomId: roomId
|
||||
},
|
||||
data: {
|
||||
epochs: {
|
||||
create: {
|
||||
epoch: String(message.epoch),
|
||||
messages: {
|
||||
create: {
|
||||
message: message.message ? String(message.message) : '',
|
||||
messageId: message.messageId ? message.messageId.toString() : '',
|
||||
proof: JSON.stringify(message.proof),
|
||||
roomId: roomId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
135
src/data/db/find.ts
Normal file
135
src/data/db/find.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { MessageI, RoomI } from 'discreetly-interfaces';
|
||||
import { CodeStatus } from '../../types/';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* Gets a room by id
|
||||
* @param {string} id The id of the room to get
|
||||
* @returns {Promise<RoomI | null>}The room, or null if it doesn't exist
|
||||
*/
|
||||
export async function findRoomById(id: string): Promise<RoomI | null> {
|
||||
const room = await prisma.rooms
|
||||
.findUnique({
|
||||
where: {
|
||||
roomId: id
|
||||
},
|
||||
// Filter out the information we want from the room
|
||||
select: {
|
||||
id: true,
|
||||
roomId: true,
|
||||
name: true,
|
||||
identities: true,
|
||||
rateLimit: true,
|
||||
userMessageLimit: true,
|
||||
membershipType: true,
|
||||
contractAddress: true,
|
||||
bandadaAddress: true,
|
||||
bandadaGroupId: true,
|
||||
type: true
|
||||
}
|
||||
})
|
||||
.then((room) => {
|
||||
return room;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
throw err; // Add this line to throw the error
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
if (room) {
|
||||
resolve(room as RoomI);
|
||||
}
|
||||
reject('Room not found');
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Need to create a system here where the client needs to provide a
|
||||
proof they know the secrets to some Identity Commitment with a unix epoch
|
||||
time stamp to prevent replay attacks
|
||||
|
||||
https://github.com/Discreetly/IdentityCommitmentNullifierCircuit <- Circuit and JS to do this
|
||||
*/
|
||||
/**
|
||||
* This function takes in an identity and returns the rooms the identity is in.
|
||||
* @param identity - the identity of a user
|
||||
* @returns an array of roomIds
|
||||
*/
|
||||
export async function findRoomsByIdentity(identity: string): Promise<string[]> {
|
||||
const r: string[] = [];
|
||||
try {
|
||||
const rooms = await prisma.rooms.findMany({
|
||||
where: {
|
||||
semaphoreIdentities: {
|
||||
has: identity
|
||||
}
|
||||
}
|
||||
});
|
||||
rooms.forEach((room) => {
|
||||
r.push(room.roomId);
|
||||
});
|
||||
return r;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a claim code in the database.
|
||||
*
|
||||
* @param {string} code - The code to find.
|
||||
* @returns {Promise<CodeStatus | null>} - The claim code, if found.
|
||||
*/
|
||||
export function findClaimCode(code: string): Promise<CodeStatus | null> {
|
||||
return prisma.claimCodes.findUnique({
|
||||
where: { claimcode: code }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to find rooms that have been updated
|
||||
* It is used in the findUpdatedRooms function
|
||||
* It is important because it allows the user to see which rooms have been updated
|
||||
* @param {string[]} roomIds - The list of roomIds that the user is in
|
||||
* @returns {Promise<RoomI[]>} - A promise that resolves to a list of rooms
|
||||
*/
|
||||
export async function findUpdatedRooms(roomIds: string[]): Promise<RoomI[]> {
|
||||
const rooms = await prisma.rooms.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
if (rooms) {
|
||||
resolve(rooms as RoomI[]);
|
||||
}
|
||||
reject('No rooms found');
|
||||
});
|
||||
}
|
||||
|
||||
export async function findRoomWithMessageId(
|
||||
roomId: string,
|
||||
message: MessageI
|
||||
): Promise<MessageI | null> {
|
||||
try {
|
||||
const room = await prisma.rooms.findFirst({
|
||||
where: { roomId },
|
||||
include: {
|
||||
epochs: {
|
||||
where: { epoch: String(message.epoch) },
|
||||
include: {
|
||||
messages: {
|
||||
where: { messageId: message.messageId }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!room) {
|
||||
return null;
|
||||
}
|
||||
return room.epochs[0].messages[0];
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
4
src/data/db/index.ts
Normal file
4
src/data/db/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './create';
|
||||
export * from './find';
|
||||
export * from './update';
|
||||
export * from './remove';
|
||||
51
src/data/db/remove.ts
Normal file
51
src/data/db/remove.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { RoomI, getRateCommitmentHash } from 'discreetly-interfaces';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* This function takes in an identity and a room and removes the identity from the room
|
||||
* by setting its semaphoreIdentities to 0n and identities to 0n
|
||||
* @param {string} idc - The identity of the user
|
||||
* @param {RoomI} room - The room to remove the identity from
|
||||
* @returns {Promise<void | RoomI>} - A promise that resolves to the room
|
||||
*/
|
||||
export function removeIdentityFromRoom(idc: string, room: RoomI): Promise<void | RoomI> {
|
||||
const updateSemaphoreIdentities =
|
||||
room.semaphoreIdentities?.map((identity) => (identity === idc ? '0' : (identity as string))) ??
|
||||
[];
|
||||
|
||||
const rateCommitmentsToUpdate = getRateCommitmentHash(
|
||||
BigInt(idc),
|
||||
BigInt(room.userMessageLimit!)
|
||||
).toString();
|
||||
|
||||
const updatedRateCommitments =
|
||||
room.identities?.map((limiter) =>
|
||||
limiter == rateCommitmentsToUpdate ? '0' : (limiter as string)
|
||||
) ?? [];
|
||||
|
||||
return prisma.rooms
|
||||
.update({
|
||||
where: { id: room.id },
|
||||
data: {
|
||||
identities: updatedRateCommitments,
|
||||
semaphoreIdentities: updateSemaphoreIdentities
|
||||
}
|
||||
})
|
||||
.then((room) => {
|
||||
return room as RoomI;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
export function removeRoom(roomId: string) {
|
||||
console.warn('removeRoom not implemented', roomId);
|
||||
//TODO removeRoom function
|
||||
}
|
||||
|
||||
export function removeMessage(roomId: string, messageId: string) {
|
||||
console.warn('removeMessage not implemented', roomId, messageId);
|
||||
//TODO removeMessage function
|
||||
}
|
||||
174
src/data/db/update.ts
Normal file
174
src/data/db/update.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { sanitizeIDC } from '../utils';
|
||||
import { RoomI } from 'discreetly-interfaces';
|
||||
import { getRateCommitmentHash } from '../../crypto';
|
||||
import { pp } from '../../utils';
|
||||
import { RoomWithSecretsI, RoomsFromClaimCode } from '../../types/';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* This code updates the identity commitments of a list of rooms.
|
||||
* It adds the identity commitment to the identity list of each room,
|
||||
* and also adds it to the bandada of each room. The identity commitment is
|
||||
* sanitized before being added to the database.
|
||||
* @param idc - The identity commitment of the user
|
||||
* @param roomIds - The list of roomIds that the user is in
|
||||
* @returns {Promise<void>} - A promise that resolves when the update is complete
|
||||
*/
|
||||
export async function updateRoomIdentities(
|
||||
idc: string,
|
||||
roomIds: string[]
|
||||
): Promise<string[] | void> {
|
||||
try {
|
||||
const identityCommitment: string = sanitizeIDC(idc);
|
||||
const rooms: RoomWithSecretsI[] | null = (await prisma.rooms.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
})) as RoomWithSecretsI[];
|
||||
|
||||
if (!rooms) {
|
||||
throw new Error('No rooms found with the provided IDs');
|
||||
}
|
||||
|
||||
const identityRooms: string[] = await addIdentityToIdentityListRooms(rooms, identityCommitment);
|
||||
const bandadaRooms: string[] = await addIdentityToBandadaRooms(rooms, identityCommitment);
|
||||
|
||||
return [...identityRooms, ...bandadaRooms];
|
||||
} catch (err) {
|
||||
pp(err, 'error');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the claim_code table to mark the given code as claimed.
|
||||
* @param {string} code - The code to update
|
||||
* @returns {Promise<RoomsFromClaimCode>} - The rooms associated with the claim code
|
||||
*/
|
||||
export function updateClaimCode(code: string): Promise<RoomsFromClaimCode> {
|
||||
return prisma.claimCodes.update({
|
||||
where: { claimcode: code },
|
||||
data: { claimed: true }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user's identity commitment to the semaphoreIdentities list and adds their rate commitment to the identities list for each of the identity list rooms that they are in.
|
||||
* @param {rooms} - The list of rooms that the user is in
|
||||
* @param {string} identityCommitment - The user's identity commitment
|
||||
* @return {string[]} addedRooms - The list of rooms that the user was added to
|
||||
*/
|
||||
export async function addIdentityToIdentityListRooms(
|
||||
rooms: RoomI[] | RoomWithSecretsI[],
|
||||
identityCommitment: string
|
||||
): Promise<string[]> {
|
||||
const identityListRooms = rooms
|
||||
.filter(
|
||||
(room: RoomI) =>
|
||||
room.membershipType === 'IDENTITY_LIST' &&
|
||||
!room.semaphoreIdentities?.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room.roomId as string);
|
||||
|
||||
const addedRooms: string[] = [];
|
||||
|
||||
const promises = identityListRooms.map(async (roomId) => {
|
||||
const room = rooms.find((r) => r.roomId === roomId);
|
||||
if (room) {
|
||||
try {
|
||||
await prisma.rooms.update({
|
||||
where: { id: roomId },
|
||||
data: {
|
||||
identities: {
|
||||
push: getRateCommitmentHash(
|
||||
BigInt(identityCommitment),
|
||||
BigInt(room.userMessageLimit! ?? 1)
|
||||
).toString()
|
||||
},
|
||||
semaphoreIdentities: { push: identityCommitment }
|
||||
}
|
||||
});
|
||||
console.debug(`Successfully added user to Identity List room ${room.roomId}`);
|
||||
addedRooms.push(roomId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
return addedRooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* This code adds a new identity commitment to the list of identities in a bandada room.
|
||||
* First we get the list of bandada rooms that contain the identity commitment.
|
||||
* Then we iterate over the list of rooms and add the identity commitment to each room.
|
||||
* After that we update the list of identities in each room in the database.
|
||||
* Finally, we send a POST request to the bandada server to add the identity to the group.
|
||||
* @param {RoomI[]} rooms - The list of rooms that contain the identity commitment.
|
||||
* @param {string} identityCommitment - The identity commitment to be added to the bandada room.
|
||||
* @return {string[]} addedRooms - The list of rooms that the user was added to
|
||||
*/
|
||||
export async function addIdentityToBandadaRooms(
|
||||
rooms: RoomWithSecretsI[],
|
||||
identityCommitment: string
|
||||
): Promise<string[]> {
|
||||
const bandadaGroupRooms = rooms
|
||||
.filter(
|
||||
(room: RoomI) =>
|
||||
room.membershipType === 'BANDADA_GROUP' &&
|
||||
!room.semaphoreIdentities?.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room);
|
||||
|
||||
const addedRooms: string[] = [];
|
||||
|
||||
if (bandadaGroupRooms.length > 0) {
|
||||
const promises = bandadaGroupRooms.map(async (room) => {
|
||||
const rateCommitment = getRateCommitmentHash(
|
||||
BigInt(identityCommitment),
|
||||
BigInt(room.userMessageLimit! ?? 1)
|
||||
).toString();
|
||||
|
||||
if (!room.bandadaAPIKey) {
|
||||
console.error('API key is missing for room:', room);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': room.bandadaAPIKey
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
await prisma.rooms.update({
|
||||
where: { id: room.id },
|
||||
data: {
|
||||
identities: {
|
||||
push: rateCommitment
|
||||
},
|
||||
semaphoreIdentities: { push: identityCommitment }
|
||||
}
|
||||
});
|
||||
|
||||
const url = `https://${room.bandadaAddress}/groups/${room.bandadaGroupId}/members/${rateCommitment}`;
|
||||
const response = await fetch(url, requestOptions);
|
||||
console.log(response);
|
||||
if (response.status == 201) {
|
||||
console.debug(`Successfully added user to Bandada group ${room.bandadaAddress}`);
|
||||
addedRooms.push(room.id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
return addedRooms;
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import { removeIdentityFromRoom } from './db';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { createMessageInRoom, findRoomWithMessageId, removeIdentityFromRoom } from './db/';
|
||||
import { MessageI, RoomI } from 'discreetly-interfaces';
|
||||
import { shamirRecovery, getIdentityCommitmentFromSecret } from '../crypto/shamirRecovery';
|
||||
import { RLNFullProof } from 'rlnjs';
|
||||
import verifyProof from '../crypto/verifier';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
import { verifyProof } from '../crypto/';
|
||||
|
||||
interface CollisionCheckResult {
|
||||
collision: boolean;
|
||||
@@ -22,98 +19,46 @@ interface CollisionCheckResult {
|
||||
*/
|
||||
|
||||
async function checkRLNCollision(roomId: string, message: MessageI): Promise<CollisionCheckResult> {
|
||||
return new Promise((res) => {
|
||||
prisma.rooms
|
||||
.findFirst({
|
||||
where: { roomId },
|
||||
include: {
|
||||
epochs: {
|
||||
where: { epoch: String(message.epoch) },
|
||||
include: {
|
||||
messages: {
|
||||
where: { messageId: message.messageId }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((oldMessage) => {
|
||||
if (!message.proof) {
|
||||
throw new Error('Proof not provided');
|
||||
}
|
||||
const oldMessage: MessageI | null = await findRoomWithMessageId(roomId, message);
|
||||
|
||||
if (
|
||||
!oldMessage ||
|
||||
!oldMessage?.epochs[0]?.messages ||
|
||||
!oldMessage?.epochs[0]?.messages[0] ||
|
||||
!oldMessage?.epochs[0]?.messages[0]?.proof
|
||||
) {
|
||||
console.debug('No collision', oldMessage);
|
||||
res({ collision: false } as CollisionCheckResult);
|
||||
} else {
|
||||
const oldMessageProof = JSON.parse(
|
||||
oldMessage.epochs[0].messages[0].proof
|
||||
) as RLNFullProof;
|
||||
const oldMessagex2 = BigInt(oldMessageProof.snarkProof.publicSignals.x);
|
||||
const oldMessagey2 = BigInt(oldMessageProof.snarkProof.publicSignals.y);
|
||||
|
||||
let proof: RLNFullProof;
|
||||
|
||||
if (typeof message.proof === 'string') {
|
||||
proof = JSON.parse(message.proof) as RLNFullProof;
|
||||
} else {
|
||||
proof = message.proof;
|
||||
}
|
||||
const [x1, y1] = [
|
||||
BigInt(proof.snarkProof.publicSignals.x),
|
||||
BigInt(proof.snarkProof.publicSignals.y)
|
||||
];
|
||||
const [x2, y2] = [oldMessagex2, oldMessagey2];
|
||||
|
||||
const secret = shamirRecovery(x1, x2, y1, y2);
|
||||
|
||||
res({
|
||||
collision: true,
|
||||
secret,
|
||||
oldMessage: oldMessage.epochs[0].messages[0] as unknown as MessageI
|
||||
} as CollisionCheckResult);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to a room.
|
||||
* @param {string} roomId - The ID of the room to add the message to.
|
||||
* @param {MessageI} message - The message to add to the room.
|
||||
* @returns {Promise<unknown>} - A promise that resolves when the message has been added to the room.
|
||||
*/
|
||||
|
||||
function addMessageToRoom(roomId: string, message: MessageI): Promise<unknown> {
|
||||
if (!message.epoch) {
|
||||
throw new Error('Epoch not provided');
|
||||
if (!message.proof) {
|
||||
throw new Error('Proof not provided');
|
||||
}
|
||||
return prisma.rooms.update({
|
||||
where: {
|
||||
roomId: roomId
|
||||
},
|
||||
data: {
|
||||
epochs: {
|
||||
create: {
|
||||
epoch: String(message.epoch),
|
||||
messages: {
|
||||
create: {
|
||||
message: message.message ? String(message.message) : '',
|
||||
messageId: message.messageId ? message.messageId.toString() : '',
|
||||
proof: JSON.stringify(message.proof),
|
||||
roomId: roomId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldMessage?.proof) {
|
||||
console.debug('No collision', oldMessage);
|
||||
return { collision: false } as CollisionCheckResult;
|
||||
} else {
|
||||
let oldMessageProof: RLNFullProof;
|
||||
if (typeof oldMessage.proof === 'string') {
|
||||
oldMessageProof = JSON.parse(oldMessage.proof) as RLNFullProof;
|
||||
} else {
|
||||
oldMessageProof = oldMessage.proof;
|
||||
}
|
||||
});
|
||||
const oldMessagex2 = BigInt(oldMessageProof.snarkProof.publicSignals.x);
|
||||
const oldMessagey2 = BigInt(oldMessageProof.snarkProof.publicSignals.y);
|
||||
|
||||
let proof: RLNFullProof;
|
||||
|
||||
if (typeof message.proof === 'string') {
|
||||
proof = JSON.parse(message.proof) as RLNFullProof;
|
||||
} else {
|
||||
proof = message.proof;
|
||||
}
|
||||
const [x1, y1] = [
|
||||
BigInt(proof.snarkProof.publicSignals.x),
|
||||
BigInt(proof.snarkProof.publicSignals.y)
|
||||
];
|
||||
const [x2, y2] = [oldMessagex2, oldMessagey2];
|
||||
|
||||
const secret = shamirRecovery(x1, x2, y1, y2);
|
||||
|
||||
return {
|
||||
collision: true,
|
||||
secret,
|
||||
oldMessage: oldMessage
|
||||
} as CollisionCheckResult;
|
||||
}
|
||||
}
|
||||
|
||||
export interface validateMessageResult {
|
||||
@@ -130,7 +75,7 @@ async function handleCollision(
|
||||
const roomId = room.roomId.toString();
|
||||
if (!collisionResult.collision) {
|
||||
try {
|
||||
await addMessageToRoom(roomId, message);
|
||||
await createMessageInRoom(roomId, message);
|
||||
console.debug(
|
||||
`Message added to room: ${
|
||||
typeof message.message === 'string'
|
||||
@@ -167,7 +112,7 @@ export async function validateMessage(
|
||||
message: MessageI
|
||||
): Promise<validateMessageResult> {
|
||||
const roomId = room.roomId.toString();
|
||||
const validProof = await verifyProof(message, room);
|
||||
const validProof: boolean = await verifyProof(room, message);
|
||||
if (validProof) {
|
||||
const collisionResult = await checkRLNCollision(roomId, message);
|
||||
const result = await handleCollision(room, message, collisionResult);
|
||||
|
||||
23
src/data/utils.ts
Normal file
23
src/data/utils.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* The sanitizeIDC function takes a string and returns a string.
|
||||
* The string is converted to a BigInt and then back to a string.
|
||||
* If the string has no loss of precision, it is returned.
|
||||
* Otherwise, an error is thrown.
|
||||
*
|
||||
* @param {string} idc - The string to be sanitized.
|
||||
* @returns {string} - The sanitized string if it has no loss of precision.
|
||||
* @throws {Error} - Throws an error if the string cannot be converted to a BigInt or if it loses precision.
|
||||
*/
|
||||
export function sanitizeIDC(idc: string): string {
|
||||
try {
|
||||
const tempBigInt = BigInt(idc);
|
||||
const tempString = tempBigInt.toString();
|
||||
if (idc === tempString) {
|
||||
return idc;
|
||||
} else {
|
||||
throw new Error('Invalid IDC provided.');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error('Invalid IDC provided.');
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,15 @@ import { PrismaClient } from '@prisma/client';
|
||||
import { serverConfig } from '../config/serverConfig';
|
||||
import { genClaimCodeArray, pp } from '../utils';
|
||||
import {
|
||||
getRoomByID,
|
||||
getRoomsByIdentity,
|
||||
findRoomById,
|
||||
findRoomsByIdentity,
|
||||
findClaimCode,
|
||||
updateClaimCode,
|
||||
updateRoomIdentities,
|
||||
findUpdatedRooms,
|
||||
createRoom,
|
||||
createSystemMessages
|
||||
} from '../data/db';
|
||||
} from '../data/db/';
|
||||
import { MessageI, RoomI } from 'discreetly-interfaces';
|
||||
import { RLNFullProof } from 'rlnjs';
|
||||
|
||||
@@ -47,14 +47,11 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
} else {
|
||||
const requestRoomId = req.params.id ?? '0';
|
||||
pp(String('Express: fetching room info for ' + req.params.id));
|
||||
getRoomByID(requestRoomId)
|
||||
findRoomById(requestRoomId)
|
||||
.then((room: RoomI) => {
|
||||
if (!room) {
|
||||
// This is set as a timeout to prevent someone from trying to brute force room ids
|
||||
setTimeout(
|
||||
() => res.status(500).json({ error: 'Internal Server Error' }),
|
||||
1000
|
||||
);
|
||||
setTimeout(() => res.status(500).json({ error: 'Internal Server Error' }), 1000);
|
||||
} else {
|
||||
const {
|
||||
roomId,
|
||||
@@ -103,7 +100,7 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
['/rooms/:idc', '/api/rooms/:idc'],
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
try {
|
||||
res.status(200).json(await getRoomsByIdentity(req.params.idc));
|
||||
res.status(200).json(await findRoomsByIdentity(req.params.idc));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
@@ -135,9 +132,7 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
const parsedBody: JoinData = req.body as JoinData;
|
||||
|
||||
if (!parsedBody.code || !parsedBody.idc) {
|
||||
res
|
||||
.status(400)
|
||||
.json({ message: '{code: string, idc: string} expected' });
|
||||
res.status(400).json({ message: '{code: string, idc: string} expected' });
|
||||
}
|
||||
const { code, idc } = parsedBody;
|
||||
console.debug('Invite Code:', code);
|
||||
@@ -162,7 +157,9 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
roomIds: updatedRooms.map((room: RoomI) => room.roomId)
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({ message: `No rooms found or identity already exists in ${String(roomIds)}` });
|
||||
res
|
||||
.status(400)
|
||||
.json({ message: `No rooms found or identity already exists in ${String(roomIds)}` });
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -320,30 +317,33 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
return await prisma.rooms.findMany(query).then((rooms) => {
|
||||
const roomIds = rooms.map((room) => room.id);
|
||||
const createCodes = codes.map((code) => {
|
||||
return prisma.claimCodes.create({
|
||||
data: {
|
||||
claimcode: code.claimcode,
|
||||
claimed: false,
|
||||
roomIds: roomIds
|
||||
}
|
||||
}).then((newCode) => {
|
||||
const updatePromises = rooms.map((room) => {
|
||||
return prisma.rooms.update({
|
||||
where: {
|
||||
roomId: room.roomId
|
||||
},
|
||||
data: {
|
||||
claimCodeIds: {
|
||||
push: newCode.id
|
||||
return prisma.claimCodes
|
||||
.create({
|
||||
data: {
|
||||
claimcode: code.claimcode,
|
||||
claimed: false,
|
||||
roomIds: roomIds
|
||||
}
|
||||
})
|
||||
.then((newCode) => {
|
||||
const updatePromises = rooms.map((room) => {
|
||||
return prisma.rooms.update({
|
||||
where: {
|
||||
roomId: room.roomId
|
||||
},
|
||||
data: {
|
||||
claimCodeIds: {
|
||||
push: newCode.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return Promise.all(updatePromises);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
return Promise.all(updatePromises);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(createCodes)
|
||||
@@ -368,50 +368,46 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
* }
|
||||
*/
|
||||
|
||||
app.post(
|
||||
['/room/:roomId/addcode', '/api/room/:roomId/addcode'],
|
||||
adminAuth,
|
||||
(req, res) => {
|
||||
const { roomId } = req.params;
|
||||
const { numCodes } = req.body as { numCodes: number };
|
||||
const codes = genClaimCodeArray(numCodes);
|
||||
app.post(['/room/:roomId/addcode', '/api/room/:roomId/addcode'], adminAuth, (req, res) => {
|
||||
const { roomId } = req.params;
|
||||
const { numCodes } = req.body as { numCodes: number };
|
||||
const codes = genClaimCodeArray(numCodes);
|
||||
|
||||
prisma.rooms
|
||||
.findUnique({
|
||||
where: { roomId: roomId },
|
||||
include: { claimCodes: true }
|
||||
})
|
||||
.then((room) => {
|
||||
if (!room) {
|
||||
res.status(404).json({ error: 'Room not found' });
|
||||
return;
|
||||
}
|
||||
// Map over the codes array and create a claim code for each code
|
||||
const createCodes = codes.map((code) => {
|
||||
return prisma.claimCodes.create({
|
||||
data: {
|
||||
claimcode: code.claimcode,
|
||||
claimed: false,
|
||||
rooms: {
|
||||
connect: {
|
||||
roomId: roomId
|
||||
}
|
||||
prisma.rooms
|
||||
.findUnique({
|
||||
where: { roomId: roomId },
|
||||
include: { claimCodes: true }
|
||||
})
|
||||
.then((room) => {
|
||||
if (!room) {
|
||||
res.status(404).json({ error: 'Room not found' });
|
||||
return;
|
||||
}
|
||||
// Map over the codes array and create a claim code for each code
|
||||
const createCodes = codes.map((code) => {
|
||||
return prisma.claimCodes.create({
|
||||
data: {
|
||||
claimcode: code.claimcode,
|
||||
claimed: false,
|
||||
rooms: {
|
||||
connect: {
|
||||
roomId: roomId
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(createCodes);
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ message: 'Claim codes added successfully' });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return Promise.all(createCodes);
|
||||
})
|
||||
.then(() => {
|
||||
res.status(200).json({ message: 'Claim codes added successfully' });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
});
|
||||
|
||||
// This code fetches the claim codes from the database.
|
||||
|
||||
|
||||
@@ -1 +1,16 @@
|
||||
import { RoomI } from 'discreetly-interfaces';
|
||||
|
||||
export interface CodeStatus {
|
||||
claimed: boolean;
|
||||
roomIds: string[];
|
||||
}
|
||||
|
||||
export interface RoomsFromClaimCode {
|
||||
roomIds: string[];
|
||||
}
|
||||
|
||||
export interface RoomWithSecretsI extends RoomI {
|
||||
bandadaAPIKey: string;
|
||||
}
|
||||
|
||||
export type userCountI = Record<string, number>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MessageI, RoomI } from 'discreetly-interfaces';
|
||||
import { Socket, Server as SocketIOServer } from 'socket.io';
|
||||
import { getRoomByID, createSystemMessages } from '../data/db';
|
||||
import { findRoomById, createSystemMessages } from '../data/db/';
|
||||
import { pp } from '../utils';
|
||||
import { validateMessage } from '../data/messages';
|
||||
import type { validateMessageResult } from '../data/messages';
|
||||
@@ -12,7 +12,7 @@ export function websocketSetup(io: SocketIOServer) {
|
||||
|
||||
socket.on('validateMessage', async (msg: MessageI) => {
|
||||
try {
|
||||
const room: RoomI | null = await getRoomByID(String(msg.roomId));
|
||||
const room: RoomI | null = await findRoomById(String(msg.roomId));
|
||||
if (!room) {
|
||||
pp('INVALID ROOM', 'warn');
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ const testIdentity = randBigint();
|
||||
const username = 'admin';
|
||||
const password = process.env.PASSWORD;
|
||||
|
||||
describe('Endpoints should all work hopefully', () => {
|
||||
describe('Endpoints should all work', () => {
|
||||
test('It should respond with server info', async () => {
|
||||
await request(_app)
|
||||
.get('/')
|
||||
|
||||
Reference in New Issue
Block a user