diff --git a/.env.example b/.env.example index 7bf489c..bc411b6 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,7 @@ PORT=8000 MONGO_URI=mongodb://localhost:27017 JWT_SECRET=secret LOGIN_SECRET="login to backend" +DB_NAME="channel4" PRIVATE_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 RPC_URL=https://goerli.infura.io/v3/ -CONTRACT_ADDRESS=0xE2A943638d71c26cEE411d3432a3A95Bd0F4a1a9 \ No newline at end of file +CONTRACT_ADDRESS=0x6189a62161FEDfFeBc5A56ffA419978937618843 \ No newline at end of file diff --git a/.env.local.example b/.env.local.example index b559b13..6a38675 100644 --- a/.env.local.example +++ b/.env.local.example @@ -5,4 +5,4 @@ LOGIN_SECRET="login to backend" DB_NAME="channel4" PRIVATE_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 RPC_URL=http://localhost:8545 -CONTRACT_ADDRESS=0x700b6A60ce7EaaEA56F065753d8dcB9653dbAD35 \ No newline at end of file +CONTRACT_ADDRESS=0x6189a62161FEDfFeBc5A56ffA419978937618843 \ No newline at end of file diff --git a/src/controllers/contract.ts b/src/controllers/contract.ts index 67d6287..4dac4e7 100644 --- a/src/controllers/contract.ts +++ b/src/controllers/contract.ts @@ -38,19 +38,17 @@ const syncDataToSmartContract = async (_req: Request, res: Response) => { // Submit a batch of data to the smart contract to sync try { - /* TODO: uncomment this let tx = await contract.syncState(users, tags, urls, likes); await tx.wait(); - */ } catch (error: any) { console.error("error: ", error); return res.status(500).json({ error: `Failed to sync state: ${error.message}` }); } // Mark all as synced - await UserControl.markSynced(users); - await TagControl.markSynced(tags); - await URLControl.markSynced(urls); + await UserControl.markSynced(users.map(user => user.userAddress as string)); + await TagControl.markSynced(tags.map(tag => tag.name)); + await URLControl.markSynced(urls.map(url => url.url)); await LikeControl.markSynced(); // Return success on syncing smart contract with backend state diff --git a/src/controllers/likes.ts b/src/controllers/likes.ts index 2ad7295..5436620 100644 --- a/src/controllers/likes.ts +++ b/src/controllers/likes.ts @@ -64,6 +64,7 @@ export const handleLike = async (req: Request, res: Response) => { } // update the # of likes on the content + content.syncedToBlockchain = false; content.likes += liked ? 1 : -1; await content.save(); diff --git a/src/controllers/tags.ts b/src/controllers/tags.ts index e31dbd2..5a1bb71 100644 --- a/src/controllers/tags.ts +++ b/src/controllers/tags.ts @@ -1,7 +1,7 @@ import { Request, Response } from 'express'; import { Types } from 'mongoose'; -import { Tag, TagDocument, UserDocument } from '../models/schema'; -import { TagToSync } from '../types/contract'; +import { Tag, TagDocument, URLDocument, UserDocument } from '../models/schema'; +import { Data } from '../types/typechain/Channel4'; export const createTag = async (req: Request, res: Response) => { try { @@ -40,6 +40,7 @@ export const attachURL = async (tags: Types.ObjectId[], urlId: Types.ObjectId) = const tag: TagDocument | null = await Tag.findById(tagId); if (tag) { tag.urls.push(urlId); + tag.syncedToBlockchain = false; await tag.save(); } } @@ -52,29 +53,33 @@ export const detachURL = async (tags: Types.ObjectId[], urlId: Types.ObjectId) = const index = tag.urls.indexOf(urlId); if (index > -1) { tag.urls.splice(index, 1); + tag.syncedToBlockchain = false; await tag.save(); } } } }; -export const getTagsToSync = async () : Promise => { - return Tag.find({ syncedToBlockchain: false }) - .populate({ - path: 'createdBy', - model: 'User', - select: 'walletAddress' - }) - .then(tags => tags.map(tag => { - const createdBy = (tag.createdBy as unknown) as UserDocument - return { - name: tag.name, - createdBy: createdBy.walletAddress, - } - })); +export const getTagsToSync = async () : Promise => { + const tags = await Tag.find({ syncedToBlockchain: false }).populate({ + path: 'createdBy', + model: 'User', + select: 'walletAddress', + }).populate({ + path: 'urls', + model: 'Url', + select: 'title', + }); + return tags.map(tag => { + return { + name: tag.name, + createdBy: (tag.createdBy as unknown as UserDocument).walletAddress, + contentIds: (tag.urls as unknown as URLDocument[]).map(url => url.title), + }; + }); } -export const markSynced = async (tags: TagToSync[]) => { +export const markSynced = async (tags: string[]) => { await Tag.updateMany( { name: { $in: tags } }, { syncedToBlockchain: true } diff --git a/src/controllers/urls.ts b/src/controllers/urls.ts index a7b1988..05e15eb 100644 --- a/src/controllers/urls.ts +++ b/src/controllers/urls.ts @@ -5,7 +5,7 @@ import { shuffle } from '../lib/utils'; import * as TagControl from './tags'; import * as UserControl from './users'; import { UserDocument, TagDocument } from '../models/schema'; -import { UrlToSync } from '../types/contract'; +import { Data } from '../types/typechain/Channel4'; const createURL = async (req: Request, res: Response) => { @@ -101,35 +101,32 @@ const getMixedURLs = async (req: Request, res: Response) => { } }; -const getContentToSync = async (): Promise => { - return await Url.find({ syncedToBlockchain: false }) - .populate({ - path: 'submittedBy', - model: 'User', - select: 'walletAddress' - }) - .populate({ - path: 'tags', - model: 'Tag', - select: 'name' - }) - .then(urls => urls.map(url => { - const userDoc = (url.submittedBy as unknown) as UserDocument - return { - title: url.title, - url: url.url, - submittedBy: userDoc.walletAddress, - tagIds: url.tags.map(tag => { - const tagDoc = (tag as unknown) as TagDocument - return tagDoc.name - }) - } - })); +const getContentToSync = async (): Promise => { + const contents = await Url.find({ syncedToBlockchain: false }).populate({ + path: "submittedBy", + model: "User", + select: "walletAddress", + }).populate({ + path: "tags", + model: "Tag", + select: "name", + }); + return contents.map((content) => { + return { + title: content.title, + url: content.url, + submittedBy: (content.submittedBy as unknown as UserDocument).walletAddress, + likes: content.likes, + tagIds: content.tags.map((tag) => { + return (tag as unknown as TagDocument).name; + }), + }; + }); }; -const markSynced = async (urls: UrlToSync[]) => { +const markSynced = async (urls: string[]) => { await Url.updateMany( - { title: { $in: urls } }, + { url: { $in: urls } }, { syncedToBlockchain: true } ); }; diff --git a/src/controllers/users.ts b/src/controllers/users.ts index 87a5001..2a4e127 100644 --- a/src/controllers/users.ts +++ b/src/controllers/users.ts @@ -3,6 +3,7 @@ const { User } = require("../models/schema"); import { Request, Response } from 'express'; import { Types } from 'mongoose'; import { UserDocument, URLDocument } from '../models/schema'; +import { Data } from '../types/typechain/Channel4'; export const createUser = async (req: Request, res: Response) => { @@ -66,6 +67,7 @@ export const attachURL = async (userId : Types.ObjectId, urlId: Types.ObjectId) const user = await User.findById(userId); if (user) { user.submittedUrls.push(urlId); + user.syncedToBlockchain = false; await user.save(); } }; @@ -76,25 +78,34 @@ export const detachURL = async (userId: Types.ObjectId, urlId: Types.ObjectId) = const index = user.submittedUrls.indexOf(urlId); if (index > -1) { user.submittedUrls.splice(index, 1); + user.syncedToBlockchain = false; await user.save(); } } }; -export const getUsersToSync = async () : Promise => { +export const getUsersToSync = async () : Promise => { const users: UserDocument[] = await User.find({ syncedToBlockchain: false }).populate({ path: 'submittedUrls', model: 'Url', }); - console.log(users); - - return users.map(user => user.walletAddress); + return users.map((user) => { + // TODO: save the contract index of each content + const submittedContent = (user.submittedUrls as unknown as URLDocument[]).map((_url, index) => index); + return { + userAddress: user.walletAddress, + numberOfLikes: user.likedUrls.length, + submittedContent: submittedContent, + registeredAt: Math.floor(user.createdAt.getTime() / 1000), + numberOfLikesInPeriod: 0, + }; + }); } export const markSynced = async (users: string[]) => { await User.updateMany( { walletAddress: { $in: users } }, - { syncedToBlockchain: false } + { syncedToBlockchain: true } ); } // dow we need url param in getNonce? diff --git a/src/types/contract.ts b/src/types/contract.ts index 964e8e6..6b37b74 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -1,22 +1,30 @@ +// These types are generated from the typechain package. Try importing them from there + export interface TagToSync { name: string, createdBy: string, + contentIds: string[], } -export interface LikeToSync { +export interface LikeToSync { url: string, liked: boolean, nonce: number, submittedBy: string, } -export interface UserToSync { - walletAddress: string, +export interface UserToSync { + userAddress: string, + numberOfLikes: number, + submittedUrls: number[], + registeredAt: number, + numberofLikesInPeriod: number, } export interface UrlToSync { title: string, url: string, submittedBy: string, + likes: number, tagIds: string[], } \ No newline at end of file