mirror of https://gitlab.com/ecentrics/concordia
				
				
			
				 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