mirror of
https://github.com/Discreetly/server.git
synced 2026-01-09 12:37:58 -05:00
Bandada Integration (#35)
This commit is contained in:
@@ -65,4 +65,4 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ model Rooms {
|
||||
membershipType String @default("IDENTITY_LIST")
|
||||
identities String[] @default([])
|
||||
contractAddress String? // RLN_CONTRACT as "chainID:0xADDRESS"
|
||||
bandadaAddress String?
|
||||
bandadaGroupId String?
|
||||
bandadaAddress String? // BANDADA as "url:groupID"
|
||||
bandadaGroupId String? // Bandada Group ID
|
||||
bandadaAPIKey String? // Bandada API Key
|
||||
epochs Epoch[]
|
||||
messages Messages[]
|
||||
claimCodes ClaimCodes[] @relation(fields: [claimCodeIds], references: [id])
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createRoom } from '../src/data/db';
|
||||
|
||||
function main() {
|
||||
createRoom('1 Second Room', 1000, 1, 10).catch((err) => console.warn(err));
|
||||
createRoom('10 Second Room', 10000, 2, 10).catch((err) => console.warn(err));
|
||||
createRoom('100 Second Room', 100000, 10, 10).catch((err) => console.warn(err));
|
||||
async function main(){
|
||||
await createRoom('1 Second Room', 1000, 1, 10, 20, 'PUBLIC');
|
||||
await createRoom('10 Second Room', 10000, 2, 10, 20, 'PUBLIC');
|
||||
await createRoom('100 Second Room', 100000, 10, 10, 20, 'PUBLIC');
|
||||
}
|
||||
|
||||
main();
|
||||
await main();
|
||||
|
||||
@@ -37,6 +37,7 @@ async function verifyProof(msg: MessageI, room: RoomI, epochErrorRange = 5): Pro
|
||||
}
|
||||
|
||||
// Check that the message hash is correct
|
||||
|
||||
if (msgHash !== proof.snarkProof.publicSignals.x) {
|
||||
console.warn(
|
||||
'Message hash incorrect:',
|
||||
|
||||
106
src/data/db.ts
106
src/data/db.ts
@@ -93,28 +93,90 @@ export function updateClaimCode(code: string): Promise<RoomsFromClaimCode> {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateRoomIdentities(idc: string, roomIds: string[]): Promise<any> {
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateRoomIdentities(idc: string, roomIds: string[]): Promise<void> {
|
||||
const identityCommitment = sanitizeIDC(idc);
|
||||
return prisma.rooms
|
||||
.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
})
|
||||
.then((rooms) => {
|
||||
const roomsToUpdate = rooms
|
||||
.filter((room) => !room.identities.includes(idc))
|
||||
.map((room) => room.id);
|
||||
|
||||
if (roomsToUpdate) {
|
||||
return prisma.rooms.updateMany({
|
||||
where: { id: { in: roomsToUpdate } },
|
||||
data: { identities: { push: idc } }
|
||||
});
|
||||
}
|
||||
addIdentityToIdentityListRooms(rooms, identityCommitment);
|
||||
addIdentityToBandadaRooms(rooms, identityCommitment);
|
||||
})
|
||||
.catch((err) => {
|
||||
pp(err, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function addIdentityToIdentityListRooms(rooms, identityCommitment: string): unknown {
|
||||
const identityListRooms = rooms
|
||||
.filter(
|
||||
(room) =>
|
||||
room.membershipType === 'IDENTITY_LIST' && !room.identities.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room.id as string);
|
||||
|
||||
if (identityListRooms.length > 0) {
|
||||
return prisma.rooms.updateMany({
|
||||
where: { id: { in: identityListRooms } },
|
||||
data: { identities: { push: identityCommitment } }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addIdentityToBandadaRooms(rooms, identityCommitment: string): void {
|
||||
const bandadaGroupRooms = rooms
|
||||
.filter(
|
||||
(room) =>
|
||||
room.membershipType === 'BANDADA_GROUP' && !room.identities.includes(identityCommitment)
|
||||
)
|
||||
.map((room) => room as RoomI);
|
||||
|
||||
if (bandadaGroupRooms.length > 0) {
|
||||
bandadaGroupRooms.forEach(async (room) => {
|
||||
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
|
||||
}
|
||||
};
|
||||
await prisma.rooms.updateMany({
|
||||
where: { id: room.id },
|
||||
data: { identities: { push: identityCommitment } }
|
||||
});
|
||||
const url = `https://${room.bandadaAddress}/groups/${room.bandadaGroupId}/members/${identityCommitment}`;
|
||||
fetch(url, requestOptions)
|
||||
.then((res) => {
|
||||
if (res.status == 200) {
|
||||
console.debug(`Successfully added user to Bandada group ${room.bandadaAddress}`);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function findUpdatedRooms(roomIds: string[]): Promise<RoomI[]> {
|
||||
const rooms = await prisma.rooms.findMany({
|
||||
where: { id: { in: roomIds } }
|
||||
@@ -127,7 +189,8 @@ export async function findUpdatedRooms(roomIds: string[]): Promise<RoomI[]> {
|
||||
});
|
||||
}
|
||||
|
||||
export function createSystemMessages(message: string, roomId?: string): Promise<any> {
|
||||
// TODO: Make interface for this return type; which is like a MessageI
|
||||
export function createSystemMessages(message: string, roomId?: string): Promise<unknown> {
|
||||
const query = roomId ? { where: { roomId } } : undefined;
|
||||
return prisma.rooms
|
||||
.findMany(query)
|
||||
@@ -150,6 +213,7 @@ export function createSystemMessages(message: string, roomId?: string): Promise<
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,28 +243,36 @@ export function removeIdentityFromRoom(idc: string, room: RoomI): Promise<void |
|
||||
* @param {number} [approxNumMockUsers=20] - The approximate number of mock users to generate for the room.
|
||||
*/
|
||||
export async function createRoom(
|
||||
name: string,
|
||||
roomName: string,
|
||||
rateLimit = 1000,
|
||||
userMessageLimit = 1,
|
||||
numClaimCodes = 0,
|
||||
approxNumMockUsers = 20,
|
||||
type = 'PUBLIC'
|
||||
type: string,
|
||||
bandadaAddress?: string,
|
||||
bandadaGroupId?: string,
|
||||
bandadaAPIKey?: string,
|
||||
membershipType?: string
|
||||
): Promise<boolean> {
|
||||
const claimCodes: { claimcode: string }[] = genClaimCodeArray(numClaimCodes);
|
||||
console.log(claimCodes);
|
||||
const mockUsers: string[] = genMockUsers(approxNumMockUsers);
|
||||
const roomData = {
|
||||
where: {
|
||||
roomId: genId(serverConfig.id as string, name).toString()
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString()
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
roomId: genId(serverConfig.id as string, name).toString(),
|
||||
name: name,
|
||||
roomId: genId(serverConfig.id as bigint, roomName).toString(),
|
||||
name: roomName,
|
||||
rateLimit: rateLimit,
|
||||
userMessageLimit: userMessageLimit,
|
||||
identities: mockUsers,
|
||||
type,
|
||||
bandadaAddress,
|
||||
bandadaGroupId,
|
||||
bandadaAPIKey,
|
||||
membershipType,
|
||||
claimCodes: {
|
||||
create: claimCodes
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Express, RequestHandler, Request, Response } from "express";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { serverConfig } from "../config/serverConfig";
|
||||
import { pp } from "../utils";
|
||||
import type { Express, RequestHandler, Request, Response } from 'express';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { serverConfig } from '../config/serverConfig';
|
||||
import { pp } from '../utils';
|
||||
import {
|
||||
getRoomByID,
|
||||
getRoomsByIdentity,
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
updateRoomIdentities,
|
||||
findUpdatedRooms,
|
||||
createRoom,
|
||||
createSystemMessages,
|
||||
} from "../data/db";
|
||||
import { RoomI } from "discreetly-interfaces";
|
||||
createSystemMessages
|
||||
} from '../data/db';
|
||||
import { RoomI } from 'discreetly-interfaces';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -29,41 +29,74 @@ function asyncHandler(fn: {
|
||||
}
|
||||
|
||||
export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
app.get(["/", "/api"], (req, res) => {
|
||||
pp("Express: fetching server info");
|
||||
app.get(['/', '/api'], (req, res) => {
|
||||
pp('Express: fetching server info');
|
||||
res.status(200).json(serverConfig);
|
||||
});
|
||||
|
||||
app.get(["/room/:id", "/api/room/:id"], (req, res) => {
|
||||
app.get(['/room/:id', '/api/room/:id'], (req, res) => {
|
||||
if (!req.params.id) {
|
||||
res.status(400).json({ error: "Bad Request" });
|
||||
res.status(400).json({ error: 'Bad Request' });
|
||||
} else {
|
||||
const requestRoomId = req.params.id ?? "0";
|
||||
pp(String("Express: fetching room info for " + req.params.id));
|
||||
const requestRoomId = req.params.id ?? '0';
|
||||
pp(String('Express: fetching room info for ' + req.params.id));
|
||||
getRoomByID(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,
|
||||
name,
|
||||
rateLimit,
|
||||
userMessageLimit,
|
||||
membershipType,
|
||||
identities,
|
||||
contractAddress,
|
||||
bandadaAddress,
|
||||
bandadaGroupId,
|
||||
type
|
||||
} = room || {};
|
||||
const id = String(roomId);
|
||||
const roomResult: RoomI = {
|
||||
id,
|
||||
roomId,
|
||||
name,
|
||||
rateLimit,
|
||||
userMessageLimit,
|
||||
membershipType
|
||||
};
|
||||
// Add null check before accessing properties of room object
|
||||
const { roomId, name, rateLimit, userMessageLimit } = room || {};
|
||||
res.status(200).json({ roomId, name, rateLimit, userMessageLimit });
|
||||
if (membershipType === 'BANDADA_GROUP') {
|
||||
roomResult.bandadaAddress = bandadaAddress;
|
||||
roomResult.bandadaGroupId = bandadaGroupId;
|
||||
}
|
||||
if (membershipType === 'IDENTITY_LIST') {
|
||||
roomResult.identities = identities;
|
||||
}
|
||||
if (type === 'CONTRACT') {
|
||||
roomResult.contractAddress = contractAddress;
|
||||
}
|
||||
res.status(200).json(roomResult);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
});
|
||||
|
||||
app.get(["/rooms/:idc", "/api/rooms/:idc"], async (req, res) => {
|
||||
pp(
|
||||
String("Express: fetching rooms by identityCommitment " + req.params.idc)
|
||||
);
|
||||
res.status(200).json(await getRoomsByIdentity(req.params.idc));
|
||||
});
|
||||
app.get(
|
||||
['/rooms/:idc', '/api/rooms/:idc'],
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
try {
|
||||
pp(String('Express: fetching rooms by identityCommitment ' + req.params.idc));
|
||||
res.status(200).json(await getRoomsByIdentity(req.params.idc));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
interface JoinData {
|
||||
code: string;
|
||||
@@ -71,22 +104,20 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
}
|
||||
|
||||
app.post(
|
||||
["/join", "/api/join"],
|
||||
['/join', '/api/join'],
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
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.log("Invite Code:", code);
|
||||
console.log('Invite Code:', code);
|
||||
|
||||
// Check if claim code is valid and not used before
|
||||
const codeStatus = await findClaimCode(code);
|
||||
if (!codeStatus || codeStatus.claimed) {
|
||||
res.status(400).json({ message: "Claim code already used" });
|
||||
res.status(400).json({ message: 'Claim code already used' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,8 +133,8 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
|
||||
// Return the room ids of the updated rooms
|
||||
res.status(200).json({
|
||||
status: "valid",
|
||||
roomIds: updatedRooms.map((room: RoomI) => room.roomId),
|
||||
status: 'valid',
|
||||
roomIds: updatedRooms.map((room: RoomI) => room.roomId)
|
||||
});
|
||||
})
|
||||
);
|
||||
@@ -115,34 +146,46 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
numClaimCodes?: number;
|
||||
approxNumMockUsers?: number;
|
||||
roomType?: string;
|
||||
bandadaAddress?: string;
|
||||
bandadaAPIKey?: string;
|
||||
bandadaGroupId?: string;
|
||||
membershipType?: string;
|
||||
}
|
||||
|
||||
/* ~~~~ ADMIN ENDPOINTS ~~~~ */
|
||||
app.post(["/room/add", "/api/room/add"], adminAuth, (req, res) => {
|
||||
app.post(['/room/add', '/api/room/add'], adminAuth, (req, res) => {
|
||||
console.log(req.body);
|
||||
const roomMetadata = req.body as addRoomData;
|
||||
console.log(roomMetadata);
|
||||
const roomName = roomMetadata.roomName;
|
||||
const rateLimit = roomMetadata.rateLimit;
|
||||
const userMessageLimit = roomMetadata.userMessageLimit;
|
||||
const numClaimCodes = roomMetadata.numClaimCodes || 0;
|
||||
const numClaimCodes = roomMetadata.numClaimCodes ?? 0;
|
||||
const approxNumMockUsers = roomMetadata.approxNumMockUsers;
|
||||
const type = roomMetadata.roomType;
|
||||
const type = roomMetadata.roomType as unknown as string;
|
||||
const bandadaAddress = roomMetadata.bandadaAddress;
|
||||
const bandadaGroupId = roomMetadata.bandadaGroupId;
|
||||
const bandadaAPIKey = roomMetadata.bandadaAPIKey;
|
||||
const membershipType = roomMetadata.membershipType;
|
||||
createRoom(
|
||||
roomName,
|
||||
rateLimit,
|
||||
userMessageLimit,
|
||||
numClaimCodes,
|
||||
approxNumMockUsers,
|
||||
type
|
||||
type,
|
||||
bandadaAddress,
|
||||
bandadaGroupId,
|
||||
bandadaAPIKey,
|
||||
membershipType
|
||||
)
|
||||
.then((result) => {
|
||||
console.log(result);
|
||||
if (result) {
|
||||
// TODO should return roomID and claim codes if they are generated
|
||||
res.status(200).json({ message: "Room created successfully" });
|
||||
res.status(200).json({ message: 'Room created successfully' });
|
||||
} else {
|
||||
res.status(500).json({ error: "Internal Server Error" });
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -151,26 +194,26 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/room/:id/messages", (req, res) => {
|
||||
app.get('/api/room/:id/messages', (req, res) => {
|
||||
const { id } = req.params;
|
||||
prisma.messages
|
||||
.findMany({
|
||||
where: {
|
||||
roomId: id,
|
||||
},
|
||||
roomId: id
|
||||
}
|
||||
})
|
||||
.then((messages) => {
|
||||
pp("Express: fetching messages for room " + id);
|
||||
pp('Express: fetching messages for room ' + id);
|
||||
res.status(200).json(messages);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
pp(error, "error");
|
||||
res.status(500).send("Error fetching messages");
|
||||
pp(error, 'error');
|
||||
res.status(500).send('Error fetching messages');
|
||||
});
|
||||
});
|
||||
|
||||
app.get(["/logclaimcodes", "/api/logclaimcodes"], adminAuth, (req, res) => {
|
||||
pp("Express: fetching claim codes");
|
||||
app.get(['/logclaimcodes', '/api/logclaimcodes'], adminAuth, (req, res) => {
|
||||
pp('Express: fetching claim codes');
|
||||
prisma.claimCodes
|
||||
.findMany()
|
||||
.then((claimCodes) => {
|
||||
@@ -178,12 +221,12 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal Server Error" });
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
});
|
||||
|
||||
app.get(["/rooms", "/api/rooms"], adminAuth, (req, res) => {
|
||||
pp(String("Express: fetching all rooms"));
|
||||
app.get(['/rooms', '/api/rooms'], adminAuth, (req, res) => {
|
||||
pp(String('Express: fetching all rooms'));
|
||||
prisma.rooms
|
||||
.findMany()
|
||||
.then((rooms) => {
|
||||
@@ -191,20 +234,40 @@ export function initEndpoints(app: Express, adminAuth: RequestHandler) {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal Server Error" });
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/admin/message", adminAuth, async (req, res) => {
|
||||
const { message, roomId } = req.body;
|
||||
pp(String("Express: sending system message: " + message));
|
||||
try {
|
||||
const successMessage = roomId ? "Message sent to room " + roomId : "Messages sent to all rooms";
|
||||
await createSystemMessages(message, roomId);
|
||||
res.status(200).json({ message: successMessage });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal Server Error" });
|
||||
}
|
||||
});
|
||||
app.post(
|
||||
'/admin/message',
|
||||
adminAuth,
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
const { message } = req.body as { message: string };
|
||||
pp(String('Express: sending system message: ' + message));
|
||||
try {
|
||||
await createSystemMessages(message);
|
||||
res.status(200).json({ message: 'Messages sent to all rooms' });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
app.post(
|
||||
'/admin/message/:roomId',
|
||||
adminAuth,
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
const { roomId } = req.params;
|
||||
const { message } = req.body as { message: string };
|
||||
pp(String('Express: sending system message: ' + message + ' to ' + roomId));
|
||||
try {
|
||||
await createSystemMessages(message, roomId);
|
||||
res.status(200).json({ message: 'Message sent to room ' + roomId });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function genMockUsers(numMockUsers: number): string[] {
|
||||
for (let i = 0; i < newNumMockUsers; i++) {
|
||||
mockUsers.push(
|
||||
genId(
|
||||
serverConfig.id as string,
|
||||
serverConfig.id as bigint,
|
||||
// Generates a random string of length 10
|
||||
Math.random()
|
||||
.toString(36)
|
||||
|
||||
@@ -30,8 +30,8 @@ const room = {
|
||||
type: 'PUBLIC'
|
||||
};
|
||||
|
||||
const roomByIdTest = genId(serverConfig.id as string, room.roomName).toString();
|
||||
let testCode = '';
|
||||
const roomByIdTest = genId(serverConfig.id as bigint, room.roomName).toString();
|
||||
let testCode = "";
|
||||
const testIdentity = randBigint();
|
||||
const username = 'admin';
|
||||
const password = process.env.PASSWORD;
|
||||
|
||||
Reference in New Issue
Block a user