diff --git a/backend/contract-addresses.json b/backend/contract-addresses.json index ea0f25e..f63f650 100644 --- a/backend/contract-addresses.json +++ b/backend/contract-addresses.json @@ -1,9 +1,9 @@ { "localhost": { - "DAO": "0x4C767e62c92b58B2308E02ba5Cc4A3BD246060ac", - "Work1": "0x39B7522Ee1A5B13aE5580C40114239D4cE0e7D29", - "Onboarding": "0xC0Bb36820Ba891DE4ed6D60f75066805361dbeB8", - "Proposals": "0x268A0A6bB80282542e0Be0864Cfa1c2206c5491F" + "DAO": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce", + "Work1": "0xc72A4CF5499D0de850457C0d4C0a4b8071441C6f", + "Onboarding": "0xadB35d0E9d7B33441C7ED26add5D42F873430790", + "Proposals": "0xE9aa427d985aE50AdD10a3404bF7a1300fa9f667" }, "sepolia": { "DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2", diff --git a/backend/package-lock.json b/backend/package-lock.json index bd0e2e7..83f8e4c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.4.5", "ethers": "^6.12.0", "express": "^4.18.2", + "express-async-errors": "^3.1.1", "level": "^8.0.1", "object-hash": "^3.0.0" }, @@ -1813,6 +1814,14 @@ "node": ">= 0.10.0" } }, + "node_modules/express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", + "peerDependencies": { + "express": "^4.16.2" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/backend/package.json b/backend/package.json index c4bbd5f..6d93fdf 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,6 +15,7 @@ "dotenv": "^16.4.5", "ethers": "^6.12.0", "express": "^4.18.2", + "express-async-errors": "^3.1.1", "level": "^8.0.1", "object-hash": "^3.0.0" }, diff --git a/backend/src/import-from-ss.js b/backend/src/import-from-ss.js index 86ac5a4..95ea3fe 100644 --- a/backend/src/import-from-ss.js +++ b/backend/src/import-from-ss.js @@ -35,12 +35,14 @@ const getContract = (name) => new ethers.Contract( DAOArtifact.abi, signer, ); +const dao = getContract('DAO'); const fetchPaperInfo = async (paperId, retryDelay = 5000) => { const url = `https://api.semanticscholar.org/graph/v1/paper/${paperId}?` + 'fields=title,url,authors,references.title,references.url,references.authors'; let retry = false; let paper; + let requestError; const response = await axios.get(url, { headers: { 'api-key': process.env.SEMANTIC_SCHOLAR_API_KEY, @@ -52,9 +54,11 @@ const fetchPaperInfo = async (paperId, retryDelay = 5000) => { return; } // Some other error occurred - throw new Error(error); + requestError = error; }); - if (retry) { + if (requestError) { + throw requestError; + } else if (retry) { console.log('retry delay (sec):', retryDelay / 1000); await new Promise((resolve) => { setTimeout(resolve, retryDelay); @@ -138,7 +142,6 @@ HREF ${paper.url}`; }; module.exports = async (req, res) => { - const dao = await getContract('DAO'); const { body: { paperId, @@ -188,12 +191,12 @@ module.exports = async (req, res) => { } catch (e) { if (e.reason === 'A post with this contentId already exists') { console.log(`Post already added for paper ${paperId}`); - res.status(204).end(); + res.json({ alreadyAdded: true, postId: hash }); return; } throw e; } console.log(`Added post to blockchain for paper ${paperId}`); - res.status(201).end(); + res.json({ postId: hash }); }; diff --git a/backend/src/index.js b/backend/src/index.js index 6c38449..b0e4685 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -5,6 +5,7 @@ const write = require('./write'); const importFromSS = require('./import-from-ss'); require('dotenv').config(); +require('express-async-errors'); const app = express(); const port = process.env.PORT || 3000; @@ -20,6 +21,14 @@ app.get('*', (req, res) => { res.status(404).json({ errorCode: 404 }); }); +app.use((err, req, res, next) => { + const status = err.response?.status ?? 500; + const message = err.response?.data?.error ?? err.message; + console.error(`error: ${message}`); + res.status(status).send(message); + next(); +}); + app.listen(port, () => { console.log(`Listening on port ${port}`); }); diff --git a/backend/src/read.js b/backend/src/read.js index 05ce051..2a79d56 100644 --- a/backend/src/read.js +++ b/backend/src/read.js @@ -23,8 +23,6 @@ module.exports = async (req, res) => { authors, content, signature, embeddedData, } = data; - console.log({ content, embeddedData }); - // Verify hash const derivedHash = objectHash({ authors, content, signature, embeddedData, diff --git a/ethereum/contract-addresses.json b/ethereum/contract-addresses.json index ea0f25e..f63f650 100644 --- a/ethereum/contract-addresses.json +++ b/ethereum/contract-addresses.json @@ -1,9 +1,9 @@ { "localhost": { - "DAO": "0x4C767e62c92b58B2308E02ba5Cc4A3BD246060ac", - "Work1": "0x39B7522Ee1A5B13aE5580C40114239D4cE0e7D29", - "Onboarding": "0xC0Bb36820Ba891DE4ed6D60f75066805361dbeB8", - "Proposals": "0x268A0A6bB80282542e0Be0864Cfa1c2206c5491F" + "DAO": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce", + "Work1": "0xc72A4CF5499D0de850457C0d4C0a4b8071441C6f", + "Onboarding": "0xadB35d0E9d7B33441C7ED26add5D42F873430790", + "Proposals": "0xE9aa427d985aE50AdD10a3404bF7a1300fa9f667" }, "sepolia": { "DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2", diff --git a/frontend/contract-addresses.json b/frontend/contract-addresses.json index ea0f25e..f63f650 100644 --- a/frontend/contract-addresses.json +++ b/frontend/contract-addresses.json @@ -1,9 +1,9 @@ { "localhost": { - "DAO": "0x4C767e62c92b58B2308E02ba5Cc4A3BD246060ac", - "Work1": "0x39B7522Ee1A5B13aE5580C40114239D4cE0e7D29", - "Onboarding": "0xC0Bb36820Ba891DE4ed6D60f75066805361dbeB8", - "Proposals": "0x268A0A6bB80282542e0Be0864Cfa1c2206c5491F" + "DAO": "0x4774670f82A590e7eD6072bd7098836B06FFd8ce", + "Work1": "0xc72A4CF5499D0de850457C0d4C0a4b8071441C6f", + "Onboarding": "0xadB35d0E9d7B33441C7ED26add5D42F873430790", + "Proposals": "0xE9aa427d985aE50AdD10a3404bF7a1300fa9f667" }, "sepolia": { "DAO": "0x8e5bd58B2ca8910C5F9be8de847d6883B15c60d2", diff --git a/frontend/src/App.css b/frontend/src/App.css index da16e05..86d9ede 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,3 +1,7 @@ .post-content { white-space: pre-line; +} + +.input-paper-id { + width: 30em !important; } \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d844b45..2728b05 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -25,6 +25,7 @@ import AddPostModal from './components/posts/AddPostModal'; import ViewPostModal from './components/posts/ViewPostModal'; import Post from './utils/Post'; import Proposals from './components/Proposals'; +import Import from './components/Import'; import getAddressName from './utils/get-address-name'; function App() { @@ -573,6 +574,9 @@ function App() { + + + )} diff --git a/frontend/src/components/Import.jsx b/frontend/src/components/Import.jsx new file mode 100644 index 0000000..8822188 --- /dev/null +++ b/frontend/src/components/Import.jsx @@ -0,0 +1,42 @@ +import { useState } from 'react'; +import Button from 'react-bootstrap/Button'; +import Form from 'react-bootstrap/Form'; +import axios from 'axios'; + +function Import() { + const [paperId, setPaperId] = useState(); + const [status, setStatus] = useState(''); + + const handleImport = async () => { + setStatus(`Importing paper ${paperId}...`); + const { data: { alreadyAdded, postId } } = await axios.post('/api/importFromSemanticScholar', { paperId }) + .catch((error) => { + setStatus(`Error: ${error.response?.data ?? error.message}`); + }); + if (alreadyAdded) { + setStatus(`Paper ${paperId} was already imported as post ${postId}`); + } else { + setStatus(`Imported paper ${paperId} as post ${postId}`); + } + }; + + return ( + <> +

Semantic Scholar Import

+
+ + Paper ID + setPaperId(e.target.value)} + /> + +
+ +

{status}

+ + ); +} + +export default Import; diff --git a/frontend/src/utils/Post.js b/frontend/src/utils/Post.js index 58151f3..7697fd0 100644 --- a/frontend/src/utils/Post.js +++ b/frontend/src/utils/Post.js @@ -1,5 +1,4 @@ import axios from 'axios'; -// trailing slash is deliberate, to differentiate this package from the core node module import { Buffer } from 'buffer'; import { recoverPersonalSignature } from '@metamask/eth-sig-util'; import objectHash from 'object-hash/dist/object_hash';