mirror of https://gitlab.com/ecentrics/concordia
Ezerous
4 years ago
20 changed files with 2053 additions and 1589 deletions
@ -1,3 +1,11 @@ |
|||
export const FORUM_CONTRACT = 'Forum'; |
|||
export const POST_VOTING_CONTRACT = 'PostVoting'; |
|||
export const VOTING_CONTRACT = 'Voting'; |
|||
|
|||
const CONTRACTS = [ |
|||
FORUM_CONTRACT, |
|||
POST_VOTING_CONTRACT, |
|||
VOTING_CONTRACT, |
|||
]; |
|||
|
|||
export default CONTRACTS; |
|||
|
@ -0,0 +1,67 @@ |
|||
import { |
|||
REACT_APP_CONCORDIA_HOST_DEFAULT, |
|||
REACT_APP_CONCORDIA_PORT_DEFAULT, |
|||
REACT_APP_CONTRACTS_SUPPLIER_HOST_DEFAULT, |
|||
REACT_APP_CONTRACTS_SUPPLIER_PORT_DEFAULT, |
|||
REACT_APP_CONTRACTS_VERSION_HASH_DEFAULT, |
|||
} from '../constants/configuration/defaults'; |
|||
import CONTRACTS from '../constants/contracts/ContractNames'; |
|||
|
|||
function getContractsDownloadRequest() { |
|||
const CONTRACTS_SUPPLIER_HOST = process.env.REACT_APP_CONTRACTS_SUPPLIER_HOST |
|||
|| REACT_APP_CONTRACTS_SUPPLIER_HOST_DEFAULT; |
|||
const CONTRACTS_SUPPLIER_PORT = process.env.REACT_APP_CONTRACTS_SUPPLIER_PORT |
|||
|| REACT_APP_CONTRACTS_SUPPLIER_PORT_DEFAULT; |
|||
const CONTRACTS_VERSION_HASH = process.env.REACT_APP_CONTRACTS_VERSION_HASH |
|||
|| REACT_APP_CONTRACTS_VERSION_HASH_DEFAULT; |
|||
const HOST = process.env.REACT_APP_CONCORDIA_HOST || REACT_APP_CONCORDIA_HOST_DEFAULT; |
|||
const PORT = process.env.REACT_APP_CONCORDIA_PORT || REACT_APP_CONCORDIA_PORT_DEFAULT; |
|||
|
|||
const xhrRequest = new XMLHttpRequest(); |
|||
|
|||
xhrRequest.open('GET', |
|||
`http://${CONTRACTS_SUPPLIER_HOST}:${CONTRACTS_SUPPLIER_PORT}/contracts/${CONTRACTS_VERSION_HASH}`, |
|||
false); |
|||
xhrRequest.setRequestHeader('Access-Control-Allow-Origin', `${HOST}:${PORT}`); |
|||
xhrRequest.setRequestHeader('Access-Control-Allow-Credentials', 'true'); |
|||
|
|||
return xhrRequest; |
|||
} |
|||
|
|||
function validateRemoteContracts(remoteContracts) { |
|||
if (remoteContracts.length !== CONTRACTS.length) { |
|||
throw new Error(`Version mismatch detected. Artifacts brought ${remoteContracts.length} contracts but app
|
|||
expected ${CONTRACTS.length}`);
|
|||
} |
|||
|
|||
const contractsPresentStatus = CONTRACTS.map((contract) => ({ |
|||
contract, |
|||
present: remoteContracts.includes((remoteContract) => remoteContract.contractName === contract), |
|||
})); |
|||
|
|||
if (contractsPresentStatus.reduce((accumulator, contract) => accumulator && contract.present, true)) { |
|||
throw new Error(`Contracts missing from artifacts. Supplier didn't bring ${contractsPresentStatus |
|||
.filter((contractPresentStatus) => contractPresentStatus.present === false) |
|||
.map((contractPresentStatus) => contractPresentStatus.contract) |
|||
.join(', ')}.`);
|
|||
} |
|||
} |
|||
|
|||
const downloadContractArtifactsSync = () => { |
|||
const xhrRequest = getContractsDownloadRequest(); |
|||
|
|||
xhrRequest.send(null); |
|||
|
|||
if (xhrRequest.status === 200) { |
|||
const contractsRawData = xhrRequest.responseText; |
|||
const remoteContracts = JSON.parse(contractsRawData); |
|||
|
|||
validateRemoteContracts(remoteContracts); |
|||
|
|||
return remoteContracts; |
|||
} |
|||
|
|||
throw new Error(`Remote contract artifacts download request failed!\n${xhrRequest.responseText}`); |
|||
}; |
|||
|
|||
export default downloadContractArtifactsSync; |
@ -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': 'warn', |
|||
'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'], |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
@ -0,0 +1,24 @@ |
|||
# Node |
|||
/node_modules |
|||
|
|||
# IDE |
|||
.DS_Store |
|||
.idea |
|||
|
|||
# Build Directories |
|||
/build |
|||
/src/build |
|||
|
|||
# Logs |
|||
/log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
|
|||
# Misc |
|||
.env.local |
|||
.env.development.local |
|||
.env.test.local |
|||
.env.production.local |
|||
|
|||
contracts-uploads |
@ -0,0 +1,26 @@ |
|||
{ |
|||
"name": "concordia-contracts-provider", |
|||
"description": "A server that provides built contracts for Concordia.", |
|||
"version": "0.1.0", |
|||
"private": true, |
|||
"main": "src/index.js", |
|||
"scripts": { |
|||
"start": "node -r esm src/index.js" |
|||
}, |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"cors": "^2.8.5", |
|||
"esm": "~3.2.25", |
|||
"express": "^4.17.1", |
|||
"lodash": "^4.17.20", |
|||
"multer": "^1.4.2", |
|||
"multiparty": "^4.2.2" |
|||
}, |
|||
"devDependencies": { |
|||
"eslint": "^7.19.0", |
|||
"eslint-config-airbnb": "^18.2.1", |
|||
"eslint-plugin-jsx-a11y": "^6.4.1", |
|||
"eslint-plugin-react": "^7.22.0", |
|||
"eslint-plugin-react-hooks": "^4.2.0" |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
import path from 'path'; |
|||
|
|||
const PROVIDER_PORT = '8400'; |
|||
const UPLOAD_CONTRACTS_DIRECTORY = path.join(__dirname, '..', 'contracts-uploads'); |
|||
const CORS_ALLOWED_ORIGINS = ['http://127.0.0.1:7000', 'http://localhost:7000']; |
|||
|
|||
export default { |
|||
port: PROVIDER_PORT, |
|||
uploadsDirectory: UPLOAD_CONTRACTS_DIRECTORY, |
|||
corsAllowedOrigins: CORS_ALLOWED_ORIGINS, |
|||
}; |
@ -0,0 +1,31 @@ |
|||
import * as fs from 'fs'; |
|||
import path from 'path'; |
|||
import { getStorageLocation, getTagsDirectory } from '../utils/storageUtils'; |
|||
|
|||
const downloadContracts = async (req, res) => { |
|||
const { params: { hash: hashOrTag } } = req; |
|||
let directoryPath = getStorageLocation(hashOrTag); |
|||
|
|||
if (!fs.existsSync(directoryPath)) { |
|||
const tagsDirectory = getTagsDirectory(); |
|||
|
|||
if (fs.existsSync(tagsDirectory)) { |
|||
const tagFilePath = path.join(tagsDirectory, hashOrTag); |
|||
const tagReference = fs.readFileSync(tagFilePath, 'utf-8'); |
|||
|
|||
directoryPath = getStorageLocation(tagReference); |
|||
} |
|||
} |
|||
|
|||
const contracts = []; |
|||
|
|||
fs.readdirSync(directoryPath).forEach((contractFilename) => { |
|||
const rawContractData = fs.readFileSync(path.join(`${directoryPath}/${contractFilename}`), 'utf-8'); |
|||
const contractJson = JSON.parse(rawContractData); |
|||
contracts.push(contractJson); |
|||
}); |
|||
|
|||
res.send(contracts); |
|||
}; |
|||
|
|||
export default downloadContracts; |
@ -0,0 +1,37 @@ |
|||
import path from 'path'; |
|||
import fs from 'fs'; |
|||
import upload from '../middleware/upload'; |
|||
import { getTagsDirectory } from '../utils/storageUtils'; |
|||
|
|||
const addOrTransferTag = (tag, hash) => { |
|||
const tagsDirectory = getTagsDirectory(); |
|||
const tagFilePath = path.join(tagsDirectory, tag); |
|||
|
|||
fs.mkdirSync(tagsDirectory, { recursive: true }); |
|||
fs.writeFileSync(tagFilePath, hash); |
|||
}; |
|||
|
|||
const uploadContracts = async (req, res) => { |
|||
try { |
|||
await upload(req, res); |
|||
|
|||
const { body: { tag } } = req; |
|||
const { params: { hash } } = req; |
|||
|
|||
if (tag) { |
|||
addOrTransferTag(tag, hash); |
|||
} |
|||
|
|||
if (req.files.length <= 0) { |
|||
return res.send('You must select at least 1 file.'); |
|||
} |
|||
|
|||
return res.send('Files have been uploaded.'); |
|||
} catch (error) { |
|||
console.log(error); |
|||
|
|||
return res.send(`Error when trying upload many files: ${error}`); |
|||
} |
|||
}; |
|||
|
|||
export default uploadContracts; |
@ -0,0 +1,25 @@ |
|||
import express from 'express'; |
|||
import cors from 'cors'; |
|||
import initRoutes from './routes/web'; |
|||
import constants from './constants'; |
|||
|
|||
const PROVIDER_PORT = process.env.CONTRACTS_PROVIDER_PORT || constants.port; |
|||
const ALLOWED_ORIGINS = process.env.CORS_ALLOWED_ORIGINS |
|||
? process.env.CORS_ALLOWED_ORIGINS.split(';') |
|||
: constants.corsAllowedOrigins; |
|||
|
|||
const app = express(); |
|||
|
|||
const corsOptions = { |
|||
origin: ALLOWED_ORIGINS, |
|||
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
|
|||
}; |
|||
|
|||
app.use(express.urlencoded({ extended: true })); |
|||
app.use(cors(corsOptions)); |
|||
|
|||
initRoutes(app); |
|||
|
|||
app.listen(PROVIDER_PORT, () => { |
|||
console.log(`Contracts provider listening at http://127.0.0.1:${PROVIDER_PORT}`); |
|||
}); |
@ -0,0 +1,30 @@ |
|||
import * as util from 'util'; |
|||
import * as fs from 'fs'; |
|||
import multer from 'multer'; |
|||
import { getStorageLocation } from '../utils/storageUtils'; |
|||
|
|||
const storage = multer.diskStorage({ |
|||
destination: (req, file, callback) => { |
|||
const { params: { hash } } = req; |
|||
const contractsPath = getStorageLocation(hash); |
|||
|
|||
fs.mkdirSync(contractsPath, { recursive: true }); |
|||
callback(null, contractsPath); |
|||
}, |
|||
filename: (req, file, callback) => { |
|||
const match = ['application/json']; |
|||
|
|||
if (match.indexOf(file.mimetype) === -1) { |
|||
const message = `<strong>${file.originalname}</strong> is invalid. Only JSON files are accepted.`; |
|||
return callback(message, null); |
|||
} |
|||
|
|||
const filename = `${file.originalname}`; |
|||
callback(null, filename); |
|||
}, |
|||
}); |
|||
|
|||
const uploadFiles = multer({ storage }).array('contracts'); |
|||
const uploadFilesMiddleware = util.promisify(uploadFiles); |
|||
|
|||
export default uploadFilesMiddleware; |
@ -0,0 +1,14 @@ |
|||
import express from 'express'; |
|||
import downloadContracts from '../controllers/download'; |
|||
import uploadContracts from '../controllers/upload'; |
|||
|
|||
const router = express.Router(); |
|||
|
|||
const routes = (app) => { |
|||
router.get('/contracts/:hash', downloadContracts); |
|||
router.post('/contracts/:hash', uploadContracts); |
|||
|
|||
return app.use('/', router); |
|||
}; |
|||
|
|||
export default routes; |
@ -0,0 +1,17 @@ |
|||
import path from 'path'; |
|||
import constants from '../constants'; |
|||
|
|||
export const getStorageLocation = (hash) => { |
|||
const UPLOADS_DIRECTORY = process.env.UPLOAD_CONTRACTS_DIRECTORY || constants.uploadsDirectory; |
|||
|
|||
if (hash) { |
|||
return path.join(UPLOADS_DIRECTORY, hash); |
|||
} |
|||
|
|||
return UPLOADS_DIRECTORY; |
|||
}; |
|||
|
|||
export const getTagsDirectory = () => { |
|||
const uploadsPath = getStorageLocation(); |
|||
return path.join(uploadsPath, '/tags'); |
|||
}; |
@ -0,0 +1,32 @@ |
|||
const path = require('path'); |
|||
const unirest = require('unirest'); |
|||
const { contracts } = require('../index'); |
|||
const defaults = require('../constants/config/defaults'); |
|||
|
|||
const uploadContractsToProviderUnirest = (versionHash, tag) => { |
|||
const CONTRACTS_PROVIDER_HOST = process.env.CONTRACTS_PROVIDER_HOST || defaults.contractsProviderHost; |
|||
const CONTRACTS_PROVIDER_PORT = process.env.CONTRACTS_PROVIDER_PORT || defaults.contractsProviderPort; |
|||
|
|||
const uploadPath = `http://${CONTRACTS_PROVIDER_HOST}:${CONTRACTS_PROVIDER_PORT}/contracts/${versionHash}`; |
|||
const request = unirest('POST', uploadPath) |
|||
.field('tag', tag); |
|||
|
|||
contracts |
|||
.forEach((contract) => request |
|||
.attach('contracts', path.join(__dirname, '../', 'build/', `${contract.contractName}.json`))); |
|||
|
|||
console.log(`Uploading to ${uploadPath}`); |
|||
request.end((res) => { |
|||
if (res.error) { |
|||
throw new Error(`Failed to upload contracts to provider: ${res.error}`); |
|||
} |
|||
|
|||
console.log('Contracts uploaded to provider.'); |
|||
}); |
|||
}; |
|||
|
|||
const main = () => { |
|||
uploadContractsToProviderUnirest(process.argv[2], process.argv[3]); |
|||
}; |
|||
|
|||
main(); |
File diff suppressed because it is too large
Loading…
Reference in new issue