mirror of https://gitlab.com/ecentrics/concordia
Apostolos Fanakis
4 years ago
9 changed files with 743 additions and 339 deletions
@ -0,0 +1,60 @@ |
|||
module.exports = { |
|||
env: { |
|||
browser: true, |
|||
es6: true, |
|||
jest: true, |
|||
}, |
|||
extends: [ |
|||
'plugin:react/recommended', |
|||
'airbnb', |
|||
], |
|||
globals: { |
|||
Atomics: 'readonly', |
|||
SharedArrayBuffer: 'readonly', |
|||
}, |
|||
parser: 'babel-eslint', |
|||
parserOptions: { |
|||
ecmaFeatures: { |
|||
jsx: true, |
|||
}, |
|||
ecmaVersion: 2018, |
|||
sourceType: 'module', |
|||
}, |
|||
plugins: [ |
|||
'react', |
|||
'react-hooks', |
|||
], |
|||
rules: { |
|||
'react/jsx-props-no-spreading': 'off', |
|||
'import/extensions': 'off', |
|||
'react/jsx-indent': [ |
|||
'error', |
|||
4, |
|||
{ |
|||
checkAttributes: true, |
|||
indentLogicalExpressions: true, |
|||
}, |
|||
], |
|||
'react/require-default-props': 'off', |
|||
'react/prop-types': 'off', |
|||
'react-hooks/rules-of-hooks': 'error', |
|||
'react-hooks/exhaustive-deps': 'error', |
|||
'max-len': ['warn', { code: 120, tabWidth: 4 }], |
|||
'no-unused-vars': 'warn', |
|||
'no-console': 'off', |
|||
'no-shadow': 'warn', |
|||
'no-multi-str': 'warn', |
|||
'jsx-a11y/label-has-associated-control': [2, { |
|||
labelAttributes: ['label'], |
|||
controlComponents: ['Input'], |
|||
depth: 3, |
|||
}], |
|||
}, |
|||
settings: { |
|||
'import/resolver': { |
|||
node: { |
|||
extensions: ['.js', '.jsx'], |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
@ -1,57 +1,61 @@ |
|||
import express from 'express'; |
|||
import _ from 'lodash'; |
|||
import isReachable from 'is-reachable'; |
|||
|
|||
import { API_PORT, RENDEZVOUS_URL, WEB3_PROVIDER_URL } from './constants'; |
|||
|
|||
const POLLING_INTERVAL = 1000; |
|||
|
|||
let app; |
|||
let responseBody = { |
|||
ipfs:{id:"", localAddresses:[], peers:[], totalPeers:0, repoStats:{}}, |
|||
orbit:{identity:{}, databases:[]}, |
|||
web3:{url:WEB3_PROVIDER_URL, reachable: false}, |
|||
rendezvous:{url:RENDEZVOUS_URL, reachable: false}, |
|||
timestamp:0 |
|||
const responseBody = { |
|||
ipfs: { |
|||
id: '', localAddresses: [], peers: [], totalPeers: 0, repoStats: {}, |
|||
}, |
|||
orbit: { identity: {}, databases: [] }, |
|||
web3: { url: WEB3_PROVIDER_URL, reachable: false }, |
|||
rendezvous: { url: RENDEZVOUS_URL, reachable: false }, |
|||
timestamp: 0, |
|||
}; |
|||
|
|||
export function startAPI(orbit){ |
|||
app = express(); |
|||
app.get('/', async (req, res) => { |
|||
res.send(responseBody); |
|||
}); |
|||
async function getStats(orbit) { |
|||
try { |
|||
// eslint-disable-next-line no-underscore-dangle
|
|||
const ipfs = orbit._ipfs; |
|||
const { id } = await ipfs.id(); |
|||
const peers = await ipfs.swarm.peers(); |
|||
const localAddresses = await ipfs.swarm.localAddrs(); |
|||
const repoStats = await ipfs.stats.repo(); |
|||
const uniquePeers = _.uniqBy(peers, 'peer'); |
|||
const orbitIdentity = orbit.identity; |
|||
const databases = Object.keys(orbit.stores); |
|||
const isWeb3Reachable = await isReachable(WEB3_PROVIDER_URL); |
|||
const isRendezvousReachable = await isReachable(RENDEZVOUS_URL); |
|||
const timestamp = +new Date(); |
|||
|
|||
app.listen(API_PORT, () => { |
|||
console.log(`Pinner API at http://localhost:${API_PORT}!`); |
|||
}); |
|||
setInterval(getStats, POLLING_INTERVAL, orbit); |
|||
responseBody.ipfs.id = id; |
|||
responseBody.ipfs.peers = uniquePeers; |
|||
responseBody.ipfs.totalPeers = uniquePeers.length; |
|||
responseBody.ipfs.localAddresses = localAddresses; |
|||
responseBody.ipfs.repoStats = repoStats; |
|||
responseBody.orbit.identity = orbitIdentity; |
|||
responseBody.orbit.databases = databases; |
|||
responseBody.web3.reachable = isWeb3Reachable; |
|||
responseBody.rendezvous.reachable = isRendezvousReachable; |
|||
responseBody.timestamp = timestamp; |
|||
} catch (err) { |
|||
console.error('Error while getting stats:', err); |
|||
} |
|||
} |
|||
|
|||
async function getStats(orbit) { |
|||
try { |
|||
const ipfs = orbit._ipfs; |
|||
const {id} = await ipfs.id(); |
|||
const peers = await ipfs.swarm.peers(); |
|||
const localAddresses = await ipfs.swarm.localAddrs(); |
|||
const repoStats = await ipfs.stats.repo(); |
|||
const uniquePeers = _.uniqBy(peers, 'peer'); |
|||
const orbitIdentity = orbit.identity; |
|||
const databases = Object.keys(orbit.stores); |
|||
const isWeb3Reachable = await isReachable(WEB3_PROVIDER_URL); |
|||
const isRendezvousReachable = await isReachable(RENDEZVOUS_URL); |
|||
const timestamp = + new Date(); |
|||
|
|||
responseBody.ipfs.id = id; |
|||
responseBody.ipfs.peers = uniquePeers; |
|||
responseBody.ipfs.totalPeers = uniquePeers.length; |
|||
responseBody.ipfs.localAddresses = localAddresses; |
|||
responseBody.ipfs.repoStats = repoStats; |
|||
responseBody.orbit.identity = orbitIdentity; |
|||
responseBody.orbit.databases = databases; |
|||
responseBody.web3.reachable = isWeb3Reachable; |
|||
responseBody.rendezvous.reachable = isRendezvousReachable; |
|||
responseBody.timestamp = timestamp; |
|||
} catch (err) { |
|||
console.error('Error while getting stats:', err) |
|||
} |
|||
} |
|||
const startAPI = (orbit) => { |
|||
const app = express(); |
|||
app.get('/', async (req, res) => { |
|||
res.send(responseBody); |
|||
}); |
|||
|
|||
app.listen(API_PORT, () => { |
|||
console.log(`Pinner API at http://localhost:${API_PORT}!`); |
|||
}); |
|||
|
|||
setInterval(getStats, POLLING_INTERVAL, orbit); |
|||
}; |
|||
|
|||
export default startAPI; |
|||
|
@ -1,13 +1,17 @@ |
|||
import breezeOptions, {RENDEZVOUS_URL} from 'concordia-app/src/options/breezeOptions'; |
|||
import breezeOptions, { RENDEZVOUS_URL } from 'concordia-app/src/options/breezeOptions'; |
|||
import { WEB3_HOST_DEFAULT, WEB3_PORT_DEFAULT } from 'concordia-app/src/constants/configuration/defaults'; |
|||
import path from 'path'; |
|||
|
|||
const { WEB3_HOST, WEB3_PORT } = process.env; |
|||
|
|||
const API_PORT = process.env.PINNER_API_PORT || 4444; |
|||
|
|||
const WEB3_PROVIDER_URL = (WEB3_HOST !== undefined && WEB3_PORT !== undefined) |
|||
? `ws://${WEB3_HOST}:${WEB3_PORT}` |
|||
: `ws://${WEB3_HOST_DEFAULT}:${WEB3_PORT_DEFAULT}`; |
|||
? `ws://${WEB3_HOST}:${WEB3_PORT}` |
|||
: `ws://${WEB3_HOST_DEFAULT}:${WEB3_PORT_DEFAULT}`; |
|||
|
|||
export const swarmAddresses = breezeOptions.ipfs.config.Addresses.Swarm; |
|||
|
|||
export {API_PORT, WEB3_PROVIDER_URL, RENDEZVOUS_URL}; |
|||
export const ORBIT_DIRECTORY_DEFAULT = path.join(__dirname, '..', 'orbitdb'); |
|||
|
|||
export { API_PORT, WEB3_PROVIDER_URL, RENDEZVOUS_URL }; |
|||
|
@ -1,60 +1,80 @@ |
|||
import Web3 from 'web3'; |
|||
import Contract from 'web3-eth-contract'; |
|||
import IPFS from 'ipfs'; |
|||
import { forumContract } from 'concordia-contracts'; |
|||
import { contracts } from 'concordia-contracts'; |
|||
import { FORUM_CONTRACT } from 'concordia-app/src/constants/contracts/ContractNames'; |
|||
import { createOrbitInstance, getPeerDatabases, openKVDBs } from './utils/orbitUtils'; |
|||
import ipfsOptions from './options/ipfsOptions'; |
|||
import { WEB3_PROVIDER_URL } from './constants'; |
|||
import { startAPI } from './app'; |
|||
import startAPI from './app'; |
|||
|
|||
process.on('unhandledRejection', error => { |
|||
process.on('unhandledRejection', (error) => { |
|||
// This happens when attempting to initialize without any available Swarm addresses (e.g. Rendezvous)
|
|||
if(error.code === 'ERR_NO_VALID_ADDRESSES'){ |
|||
if (error.code === 'ERR_NO_VALID_ADDRESSES') { |
|||
console.error('unhandledRejection', error.message); |
|||
process.exit(1); |
|||
} |
|||
|
|||
// Don't swallow other errors
|
|||
console.error(error); |
|||
throw error; |
|||
}); |
|||
|
|||
async function main () { |
|||
const getDeployedContract = async (web3) => { |
|||
const forumContract = contracts.find((contract) => contract.contractName === FORUM_CONTRACT); |
|||
|
|||
return web3.eth.net.getId() |
|||
.then((networkId) => forumContract.networks[networkId].address) |
|||
.then((contractAddress) => { |
|||
Contract.setProvider(WEB3_PROVIDER_URL); |
|||
const contract = new Contract(forumContract.abi, contractAddress); |
|||
|
|||
return { contract, contractAddress }; |
|||
}); |
|||
}; |
|||
|
|||
// Open & replicate databases of existing users
|
|||
const openExistingUsersDatabases = async (contract, orbit) => contract.methods.getUserAddresses().call() |
|||
.then((userAddresses) => getPeerDatabases(orbit, userAddresses)) |
|||
.then((peerDBs) => openKVDBs(orbit, peerDBs)); |
|||
|
|||
const handleWeb3LogEvent = (web3, eventJsonInterface, orbit) => (error, result) => { |
|||
if (!error) { |
|||
const eventObj = web3.eth.abi.decodeLog( |
|||
eventJsonInterface.inputs, |
|||
result.data, |
|||
result.topics.slice(1), |
|||
); |
|||
const userAddress = eventObj[1]; |
|||
console.log('User signed up:', userAddress); |
|||
getPeerDatabases(orbit, [userAddress]) |
|||
.then((peerDBs) => openKVDBs(orbit, peerDBs)); |
|||
} |
|||
}; |
|||
|
|||
const main = async () => { |
|||
console.log('Initializing...'); |
|||
const web3 = new Web3(new Web3.providers.WebsocketProvider(WEB3_PROVIDER_URL)); |
|||
const networkId = await web3.eth.net.getId(); |
|||
|
|||
const contractAddress = forumContract.networks[networkId].address; |
|||
|
|||
Contract.setProvider(WEB3_PROVIDER_URL); |
|||
const contract = new Contract(forumContract.abi, contractAddress); |
|||
|
|||
const ipfs = await IPFS.create(ipfsOptions); |
|||
const orbit = await createOrbitInstance(ipfs, contractAddress); |
|||
|
|||
// Open & replicate databases of existing users
|
|||
const userAddresses = await contract.methods.getUserAddresses().call(); |
|||
const peerDBs = await getPeerDatabases(orbit, userAddresses); |
|||
await openKVDBs(orbit, peerDBs); |
|||
|
|||
// Listen for new users and subscribe to their databases
|
|||
const eventJsonInterface = web3.utils._.find( |
|||
contract._jsonInterface, |
|||
obj => obj.name === "UserSignedUp" && obj.type === 'event' |
|||
); |
|||
web3.eth.subscribe('logs', { |
|||
address: contractAddress, |
|||
topics: [eventJsonInterface.signature] |
|||
}, function(error, result){ |
|||
if (!error) { |
|||
const eventObj = web3.eth.abi.decodeLog( |
|||
eventJsonInterface.inputs, |
|||
result.data, |
|||
result.topics.slice(1) |
|||
) |
|||
const userAddress = eventObj[1]; |
|||
console.log(`User signed up:`, userAddress); |
|||
getPeerDatabases(orbit, [userAddress]).then(peerDBs => openKVDBs(orbit, peerDBs)); |
|||
} |
|||
}); |
|||
|
|||
startAPI(orbit); |
|||
} |
|||
|
|||
getDeployedContract(web3) |
|||
.then(({ contract, contractAddress }) => IPFS.create(ipfsOptions) |
|||
.then((ipfs) => createOrbitInstance(ipfs, contractAddress)) |
|||
.then((orbit) => openExistingUsersDatabases(contract, orbit) |
|||
.then(() => { |
|||
// Listen for new users and subscribe to their databases
|
|||
const eventJsonInterface = web3.utils._.find( |
|||
// eslint-disable-next-line no-underscore-dangle
|
|||
contract._jsonInterface, |
|||
(obj) => obj.name === 'UserSignedUp' && obj.type === 'event', |
|||
); |
|||
|
|||
web3.eth.subscribe('logs', { |
|||
address: contractAddress, |
|||
topics: [eventJsonInterface.signature], |
|||
}, handleWeb3LogEvent(web3, eventJsonInterface, orbit)); |
|||
|
|||
startAPI(orbit); |
|||
}))); |
|||
}; |
|||
|
|||
main(); |
|||
|
@ -1,21 +1,22 @@ |
|||
import libp2pBundle from './libp2pBundle' |
|||
import libp2pBundle from './libp2pBundle'; |
|||
import { swarmAddresses } from '../constants'; |
|||
|
|||
export default { |
|||
repo: 'ipfs', |
|||
config: { |
|||
Addresses: { |
|||
Swarm: swarmAddresses |
|||
}, |
|||
repo: 'ipfs', |
|||
config: { |
|||
Profile: 'server', |
|||
Addresses: { |
|||
Swarm: swarmAddresses, |
|||
}, |
|||
libp2p: libp2pBundle, |
|||
EXPERIMENTAL: { |
|||
pubsub: true, |
|||
}, |
|||
preload: { |
|||
enabled: false, |
|||
}, |
|||
init: { |
|||
emptyRepo: true, |
|||
}, |
|||
} |
|||
}, |
|||
libp2p: libp2pBundle, |
|||
EXPERIMENTAL: { |
|||
pubsub: true, |
|||
}, |
|||
preload: { |
|||
enabled: false, |
|||
}, |
|||
init: { |
|||
emptyRepo: true, |
|||
}, |
|||
}; |
|||
|
File diff suppressed because it is too large
Loading…
Reference in new issue