From 01f8d9884892ac3727ffb10ff24778872a76c428 Mon Sep 17 00:00:00 2001 From: Juan Miguel Sanchez Mola Date: Sun, 26 May 2024 18:45:31 -0400 Subject: [PATCH 01/56] Connection --- rair-front/package.json | 5 +- rair-front/src/App.tsx | 21 +++++- rair-front/src/hooks/useConnectUser.tsx | 7 +- rair-front/src/utils/sockets.ts | 19 ++++++ rair-node/bin/index.js | 87 ++++++++++++------------- 5 files changed, 87 insertions(+), 52 deletions(-) create mode 100644 rair-front/src/utils/sockets.ts diff --git a/rair-front/package.json b/rair-front/package.json index 92b7e9e1b..34cd010c1 100644 --- a/rair-front/package.json +++ b/rair-front/package.json @@ -58,6 +58,7 @@ "react-ga": "^3.3.1", "react-helmet-async": "^2.0.3", "react-hook-form": "^7.49.0", + "react-hot-toast": "^2.4.1", "react-modal": "^3.16.1", "react-moment": "^1.1.3", "react-multi-carousel": "^2.8.4", @@ -72,7 +73,7 @@ "redux": "^4.2.1", "redux-saga": "^1.2.3", "slick-carousel": "^1.8.1", - "socket.io-client": "^4.7.2", + "socket.io-client": "^4.7.5", "styled-components": "^5.3.3", "sweetalert2": "^11.10.1", "sweetalert2-react-content": "^5.0.7", @@ -98,7 +99,7 @@ "eslint-plugin-react-refresh": "^0.4.4", "eslint-plugin-simple-import-sort": "^10.0.0", "typescript": "^5.4.2", - "vite": "^5.1.6", + "vite": "^5.2.11", "vite-plugin-node-polyfills": "^0.21.0" } } diff --git a/rair-front/src/App.tsx b/rair-front/src/App.tsx index 0f682af9b..ad7e3b367 100644 --- a/rair-front/src/App.tsx +++ b/rair-front/src/App.tsx @@ -1,5 +1,6 @@ //@ts-nocheck import { Fragment, useCallback, useEffect, useState } from 'react'; +import { Toaster } from 'react-hot-toast'; import { useDispatch, useSelector } from 'react-redux'; import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; // React Redux types @@ -134,8 +135,13 @@ function App() { const navigate = useNavigate(); // Redux - const { primaryColor, textColor, backgroundImage, backgroundImageEffect } = - useSelector((store) => store.colorStore); + const { + primaryColor, + textColor, + backgroundImage, + backgroundImageEffect, + secondaryColor + } = useSelector((store) => store.colorStore); const { adminRights, loggedIn } = useSelector( (store) => store.userStore ); @@ -322,6 +328,17 @@ function App() { primaryColor={primaryColor} backgroundImage={hotDropsVar === 'true' ? '' : backgroundImage}>
+ {carousel && !isIframePage ? ( { try { @@ -64,7 +65,7 @@ const getCoingeckoRates = async () => { const useConnectUser = () => { const dispatch = useDispatch(); - const { adminRights, loginProcess, loggedIn, loginType } = useSelector< + const { adminRights, loginProcess, loggedIn } = useSelector< RootState, TUsersInitialState >((store) => store.userStore); @@ -379,6 +380,7 @@ const useConnectUser = () => { dispatch(dispatchItem); }); dispatch(setLogInStatus(true)); + sockets.nodeSocket.connect(); } } dispatch(setLoginProcessStatus(false)); @@ -439,6 +441,7 @@ const useConnectUser = () => { const logoutUser = useCallback(async () => { const { success } = await rFetch('/api/auth/logout'); if (success) { + sockets.nodeSocket.disconnect(); dispatch(getTokenComplete(null)); dispatch(setUserAddress(undefined)); dispatch(setAdminRights(false)); @@ -449,7 +452,7 @@ const useConnectUser = () => { dispatch(setChainId(import.meta.env.VITE_DEFAULT_BLOCKCHAIN)); navigate('/'); } - }, [dispatch, navigate, loginType]); + }, [dispatch, navigate]); return { connectUserData, diff --git a/rair-front/src/utils/sockets.ts b/rair-front/src/utils/sockets.ts new file mode 100644 index 000000000..5fde3a0a9 --- /dev/null +++ b/rair-front/src/utils/sockets.ts @@ -0,0 +1,19 @@ +import toast from 'react-hot-toast'; +import { io } from 'socket.io-client'; + +const socketIo = io(import.meta.env.VITE_NODE_SOCKET_URI, { + autoConnect: false +}); + +const events = { + connect: () => toast('Connected'), + message: toast +}; + +Object.keys(events).forEach((event) => { + socketIo.on(event, events[event]); +}); + +export default { + nodeSocket: socketIo +}; diff --git a/rair-node/bin/index.js b/rair-node/bin/index.js index 33edcae18..7318026c1 100644 --- a/rair-node/bin/index.js +++ b/rair-node/bin/index.js @@ -4,7 +4,8 @@ const bodyParser = require('body-parser'); const cookieParser = require('cookie-parser'); const fs = require('fs'); const cors = require('cors'); -const Socket = require('socket.io'); +const { createServer } = require("http"); +const { Server } = require("socket.io"); const morgan = require('morgan'); const session = require('express-session'); const RedisStorage = require('connect-redis')(session); @@ -47,11 +48,17 @@ async function main() { await mongoConnectionManager.getMongooseConnection(); const app = express(); + const httpServer = createServer(app); /* CORS */ const origin = `https://${process.env.SERVICE_HOST}`; - + app.use(cors({ origin })); + const socketIo = new Server(httpServer, { + cors: { + origin: '*' + } + }); const hls = await StartHLS(); @@ -67,6 +74,26 @@ async function main() { // connect redisService context.redis.redisService = redisService(context); + const sessionMiddleware = session({ + store: new RedisStorage({ + client, + // config.session.ttl was removed from here and used + // for maxAge, because when maxAge used it will have no effect. + }), + secret: config.session.secret, + saveUninitialized: true, + resave: true, + proxy: config.production, + rolling: true, + cookie: { + sameSite: config.production ? 'none' : 'lax', + path: '/', + httpOnly: config.production, + secure: config.production, + maxAge: (`${config.session.ttl}` * 60 * 60 * 1000), // TTL * hour + }, + }); + await seedDB(context); app.use(morgan('dev')); @@ -74,27 +101,10 @@ async function main() { app.use(bodyParser.json({ limit: '50mb' })); app.use(cookieParser()); app.set('trust proxy', 1); - app.use( - session({ - store: new RedisStorage({ - client, - // config.session.ttl was removed from here and used - // for maxAge, because when maxAge used it will have no effect. - }), - secret: config.session.secret, - saveUninitialized: true, - resave: true, - proxy: config.production, - rolling: true, - cookie: { - sameSite: config.production ? 'none' : 'lax', - path: '/', - httpOnly: config.production, - secure: config.production, - maxAge: (`${config.session.ttl}` * 60 * 60 * 1000), // TTL * hour - }, - }), - ); + + app.use(sessionMiddleware); + socketIo.engine.use(sessionMiddleware); + app.use('/stream', streamRoute(context)); app.use( '/api', @@ -106,33 +116,18 @@ async function main() { ); app.use(mainErrorHandler); - const server = app.listen(config.port, () => { - log.info(`Rairnode server listening at http://localhost:${config.port}`); - }); - - const io = Socket(server); - const sockets = {}; - - io.on('connection', (socket) => { - log.info(`Client connected: ${socket.id}`); - socket.on('init', (sessionId) => { - log.info(`Opened connection: ${sessionId}`); + socketIo.on('connection', (socket) => { + log.info('SOCKET: a user connected'); + socket.emit("message", "Welcome!"); - sockets[sessionId] = socket.id; - app.set('sockets', sockets); - }); - - socket.on('end', (sessionId) => { - delete sockets[sessionId]; - - socket.disconnect(0); - app.set('sockets', sockets); - - log.info(`Close connection ${sessionId}`); + socket.on('disconnect', () => { + log.info('SOCKET: user disconnected'); }); }); - app.set('io', io); + httpServer.listen(config.port, () => { + log.info(`Rairnode server listening at http://localhost:${config.port}`); + }); } (async () => { From 77620868779e816cd77eeb731d21735fe9bba16d Mon Sep 17 00:00:00 2001 From: Juan Miguel Sanchez Mola Date: Sun, 26 May 2024 22:31:42 -0400 Subject: [PATCH 02/56] Progress bar --- .../adminViews/BatchERC20Transfer.tsx | 1 - .../adminViews/ImportAndTransfer.tsx | 34 +++- .../adminViews/ImportExternalContracts.tsx | 162 +++--------------- .../src/components/adminViews/UserManager.tsx | 101 +++++++++++ .../components/adminViews/transferTokens.tsx | 4 - rair-front/src/hooks/useConnectUser.tsx | 4 + rair-front/src/utils/sockets.ts | 5 +- rair-front/vite.config.ts | 2 +- rair-node/bin/api/auth/auth.Service.js | 3 +- .../bin/api/contracts/contracts.Service.js | 7 +- .../api/transactions/transactions.Service.js | 4 +- rair-node/bin/index.js | 21 +-- .../integrations/ethers/importContractData.js | 33 +++- rair-node/package.json | 2 +- rair-sync/bin/routes/api/v1/transactions.js | 2 +- 15 files changed, 210 insertions(+), 175 deletions(-) create mode 100644 rair-front/src/components/adminViews/UserManager.tsx diff --git a/rair-front/src/components/adminViews/BatchERC20Transfer.tsx b/rair-front/src/components/adminViews/BatchERC20Transfer.tsx index b99fdab1c..0c471da72 100644 --- a/rair-front/src/components/adminViews/BatchERC20Transfer.tsx +++ b/rair-front/src/components/adminViews/BatchERC20Transfer.tsx @@ -140,7 +140,6 @@ const BatchERC20Transfer = () => { return (
-
ERC20 tool
{({ getRootProps, getInputProps, isDragActive }) => (
diff --git a/rair-front/src/components/adminViews/ImportAndTransfer.tsx b/rair-front/src/components/adminViews/ImportAndTransfer.tsx index d7995e005..6749a3b98 100644 --- a/rair-front/src/components/adminViews/ImportAndTransfer.tsx +++ b/rair-front/src/components/adminViews/ImportAndTransfer.tsx @@ -1,12 +1,34 @@ -import React from 'react' -import ImportExternalContract from './ImportExternalContracts' -import TransferTokens from './transferTokens' +import BatchERC20Transfer from './BatchERC20Transfer'; +import BlockChainSwitcher from './BlockchainSwitcher'; +import ImportExternalContract from './ImportExternalContracts'; +import TransferTokens from './transferTokens'; +import UserManager from './userManager'; const ImportAndTransfer = () => { return (
+
+ +
+
+ Manual token transfer +
+
+
+ Import external contract +
+
+
+ User manager + +
+
+
+ ERC20 transfer tool + +
- ) -} -export default ImportAndTransfer \ No newline at end of file + ); +}; +export default ImportAndTransfer; diff --git a/rair-front/src/components/adminViews/ImportExternalContracts.tsx b/rair-front/src/components/adminViews/ImportExternalContracts.tsx index b2303f7e9..ae132f245 100644 --- a/rair-front/src/components/adminViews/ImportExternalContracts.tsx +++ b/rair-front/src/components/adminViews/ImportExternalContracts.tsx @@ -1,11 +1,8 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; -import axios from 'axios'; import { utils } from 'ethers'; import { isAddress } from 'ethers/lib/utils'; -import BatchERC20Transfer from './BatchERC20Transfer'; - import { diamondFactoryAbi } from '../../contracts'; import { RootState } from '../../ducks'; import { ColorStoreType } from '../../ducks/colors/colorStore.types'; @@ -15,18 +12,10 @@ import useWeb3Tx from '../../hooks/useWeb3Tx'; import chainData from '../../utils/blockchainData'; import { validateInteger } from '../../utils/metamaskUtils'; import { rFetch } from '../../utils/rFetch'; +import sockets from '../../utils/sockets'; import InputField from '../common/InputField'; import InputSelect from '../common/InputSelect'; -type userType = { - publicAddress: string; - nickName: string; - creationDate: Date; - email: string; - _id: string; - blocked: string; -}; - const ImportExternalContract = () => { const [selectedContract, setSelectedContract] = useState(''); const [resultData, setResultData] = useState(''); @@ -36,14 +25,23 @@ const ImportExternalContract = () => { const [owner, setOwner] = useState(''); const [sendingData, setSendingData] = useState(false); const [limit, setLimit] = useState(0); - const [currentTokens /*, setCurrentTokens*/] = useState(); - const [totalTokens /*, setTotalTokens */] = useState(); - const [sessionId, setSessionId] = useState(''); - const [userList, setUserList] = useState([]); + const [progress, setProgress] = useState(); const reactSwal = useSwal(); const { web3TxHandler, correctBlockchain, web3Switch } = useWeb3Tx(); + useEffect(() => { + sockets.nodeSocket.on('importProgress', ({ progress, message }) => { + console.info(progress, message); + setSendingData(true); + setProgress(progress); + setResultData(message); + if (progress === 100) { + setSendingData(false); + } + }); + }, []); + const blockchainOptions = Object.keys(chainData) .filter((chain) => chainData[chain].disabled !== true) .map((blockchainId) => { @@ -61,54 +59,13 @@ const ImportExternalContract = () => { ColorStoreType >((store) => store.colorStore); - useEffect(() => { - const sessionId = Math.random().toString(36).slice(2, 9); - setSessionId(sessionId); - /* - Disabled until sessions are implemented - - const so = io(`/`, { - transports: ['websocket'], - protocols: ['http'], - path: '/socket' - }); - console.info(so); - - so.emit('init', sessionId); - - so.on('importReport', (data) => { - const { current, total } = data; - setResultData(`${current} of ${total}`); - setCurrentTokens(current); - setTotalTokens(total); - }); - - return () => { - so.removeListener('importReport'); - so.emit('end', sessionId); - }; - */ - }, []); - - const getUserData = useCallback(async () => { - const { success, data } = await rFetch('/api/users/list'); - if (success) { - setUserList(data); - } - }, []); - - useEffect(() => { - getUserData(); - }, [getUserData]); - const callImport = async () => { if (!validateInteger(limit)) { return; } setSendingData(true); - reactSwal.fire('Importing contract', 'Please wait', 'info'); - const { success, result } = await rFetch(`/api/contracts/import/`, { + const { success } = await rFetch(`/api/contracts/import/`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -117,19 +74,14 @@ const ImportExternalContract = () => { networkId: selectedBlockchain, contractAddress: selectedContract.toLowerCase(), limit: limit, - contractCreator: owner.toLowerCase(), - socketSessionId: sessionId + contractCreator: owner.toLowerCase() }) }); - setSendingData(false); if (success) { reactSwal.fire( - 'Success', - `Imported ${result.numberOfTokensAdded} tokens from ${result.contract.title}`, - 'success' - ); - setResultData( - `Imported ${result.numberOfTokensAdded} tokens from ${result.contract.title}` + 'Importing contract', + 'You can navigate away while the tokens are being imported', + 'info' ); } }; @@ -162,7 +114,6 @@ const ImportExternalContract = () => { return (
-

Import non-RAIR Contract

{
{resultData}

- {totalTokens && } + {progress && }
-
-
-
MANAGE USER
- -
- - - - - - - - - {userList.map((user, index) => { - return ( - - - - - - - - ); - })} - -
- Date CreatedUsernamePublic AddressEmail
- - {user.creationDate.toString()}{user.nickName}{user.publicAddress}{user.email}
-
); }; diff --git a/rair-front/src/components/adminViews/UserManager.tsx b/rair-front/src/components/adminViews/UserManager.tsx new file mode 100644 index 000000000..ad26e074a --- /dev/null +++ b/rair-front/src/components/adminViews/UserManager.tsx @@ -0,0 +1,101 @@ +import { useCallback, useEffect, useState } from 'react'; +import axios from 'axios'; + +import { rFetch } from '../../utils/rFetch'; + +type userType = { + publicAddress: string; + nickName: string; + creationDate: Date; + email: string; + _id: string; + blocked: string; +}; +const UserManager = () => { + const [userList, setUserList] = useState([]); + + const getUserData = useCallback(async () => { + const { success, data } = await rFetch('/api/users/list'); + if (success) { + setUserList(data); + } + }, []); + + useEffect(() => { + getUserData(); + }, [getUserData]); + + return ( + <> +
+ +
+ + + + + + + + + {userList.map((user, index) => { + return ( + + + + + + + + ); + })} + +
+ Date CreatedUsernamePublic AddressEmail
+ + {user.creationDate.toString()}{user.nickName}{user.publicAddress}{user.email}
+ + ); +}; +export default UserManager; diff --git a/rair-front/src/components/adminViews/transferTokens.tsx b/rair-front/src/components/adminViews/transferTokens.tsx index 9e9dba1b8..69e2531aa 100644 --- a/rair-front/src/components/adminViews/transferTokens.tsx +++ b/rair-front/src/components/adminViews/transferTokens.tsx @@ -213,10 +213,6 @@ const TransferTokens = () => { return (
-
- -
TRANSFER NFTs
-
+ + {categoryOptions?.find((cat) => cat.value === category)?.label} +
- {uploadProgress > 0 ? ( - - ) : ( - - )} -
- {offerName !== '' ? ( +
+ {offerName !== '' && ( <> - {offerName} ({demo ? 'Demo' : 'Unlockable'}) -
+ Unlocked by: {offerName} ({demo ? 'Demo' : 'Unlockable'}) - ) : ( - <> )} -
- + {uploading ? ( +
+ + {socketMessage} +
+ ) : ( + <> +
+ +
+
+ +
+
+ +
+
+ +
+ + )}
); diff --git a/rair-front/src/components/NotFound/NotFound.css b/rair-front/src/components/NotFound/NotFound.css index 8b541f60c..b66bff74d 100644 --- a/rair-front/src/components/NotFound/NotFound.css +++ b/rair-front/src/components/NotFound/NotFound.css @@ -15,23 +15,6 @@ .not-found-page h3 span { font-size: 100px; - background: linear-gradient( - 96.34deg, - #725bdb 0%, - #805fda 10.31%, - #8c63da 20.63%, - #9867d9 30.94%, - #a46bd9 41.25%, - #af6fd8 51.56%, - #af6fd8 51.56%, - #bb73d7 61.25%, - #c776d7 70.94%, - #d27ad6 80.62%, - #dd7ed6 90.31%, - #e882d5 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; line-height: 45px; font-weight: 700; } diff --git a/rair-front/src/components/adminViews/ImportExternalContracts.tsx b/rair-front/src/components/adminViews/ImportExternalContracts.tsx index b623a9d01..ef39be045 100644 --- a/rair-front/src/components/adminViews/ImportExternalContracts.tsx +++ b/rair-front/src/components/adminViews/ImportExternalContracts.tsx @@ -31,14 +31,10 @@ const ImportExternalContract = () => { const { web3TxHandler, correctBlockchain, web3Switch } = useWeb3Tx(); useEffect(() => { - const report = ({ - progress, - message, - contractAddress, - blockchain, - creator, - limit - }) => { + const report = ({ socketData }) => { + const { message, data } = socketData; + const [progress, contractAddress, blockchain, creator, limit] = data; + setSendingData(true); setProgress(progress); setResultData(message); diff --git a/rair-front/src/components/creatorStudio/FixedBottomNavigation.tsx b/rair-front/src/components/creatorStudio/FixedBottomNavigation.tsx index e72597517..3f48216ea 100644 --- a/rair-front/src/components/creatorStudio/FixedBottomNavigation.tsx +++ b/rair-front/src/components/creatorStudio/FixedBottomNavigation.tsx @@ -42,16 +42,11 @@ const FixedBottomNavigation: React.FC = ({ }) .map((item, index) => { return ( -
+
-
- {/*
- -
*/} -
- {item.file.type.split('/')[0] === 'video' && ( -
-
-
- Title -
- -
-
-
- Category -
- -
- {item.file.type} -
-
- Offer -
- -
-
-
- Storage -
- -
-
-
- Description -
-