Skip to content

Commit

Permalink
Merge pull request #1593 from kleros/feat/add-authentication-to-file-…
Browse files Browse the repository at this point in the history
…uploads

feat(web): add-authentication-to-file-uploads
  • Loading branch information
alcercu authored May 29, 2024
2 parents a2ed358 + b216109 commit 83e46e3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 56 deletions.
21 changes: 9 additions & 12 deletions web/netlify/functions/uploadToIPFS.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { File, FilebaseClient } from "@filebase/client";
import { Handler } from "@netlify/functions";
import middy from "@middy/core";
import amqp, { Connection } from "amqplib";
import busboy from "busboy";

const { FILEBASE_TOKEN, RABBITMQ_URL, FILEBASE_API_WRAPPER } = process.env;
import { authMiddleware } from "../middleware/authMiddleware";

const { FILEBASE_TOKEN, RABBITMQ_URL } = process.env;
const filebase = new FilebaseClient({ token: FILEBASE_TOKEN ?? "" });

type FormElement =
Expand Down Expand Up @@ -65,24 +67,17 @@ const pinToFilebase = async (data: FormData, operation: string): Promise<Array<s
return cids;
};

export const handler: Handler = async (event) => {
export const uploadToIPFS = async (event) => {
const { queryStringParameters } = event;

if (!queryStringParameters || !queryStringParameters.key || !queryStringParameters.operation) {
if (!queryStringParameters?.operation) {
return {
statusCode: 400,
body: JSON.stringify({ message: "Invalid query parameters" }),
};
}

const { key, operation } = queryStringParameters;

if (key !== FILEBASE_API_WRAPPER) {
return {
statusCode: 403,
body: JSON.stringify({ message: "Invalid API key" }),
};
}
const { operation } = queryStringParameters;

try {
const parsed = await parseMultipart(event);
Expand All @@ -102,3 +97,5 @@ export const handler: Handler = async (event) => {
};
}
};

export const handler = middy(uploadToIPFS).use(authMiddleware());
61 changes: 32 additions & 29 deletions web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { prepareWriteEvidenceModule } from "hooks/contracts/generated";
import { uploadFormDataToIPFS } from "utils/uploadFormDataToIPFS";
import { wrapWithToast, OPTIONS as toastOptions } from "utils/wrapWithToast";

import { EnsureAuth } from "components/EnsureAuth";
import { EnsureChain } from "components/EnsureChain";

const StyledModal = styled(Modal)`
Expand Down Expand Up @@ -67,35 +68,37 @@ const SubmitEvidenceModal: React.FC<{
<ButtonArea>
<Button variant="secondary" disabled={isSending} text="Return" onClick={close} />
<EnsureChain>
<Button
text="Submit"
isLoading={isSending}
disabled={isSending}
onClick={async () => {
setIsSending(true);
toast.info("Uploading to IPFS", toastOptions);
const formData = await constructEvidence(message, file);
uploadFormDataToIPFS(formData)
.then(async (res) => {
const response = await res.json();
if (res.status === 200 && walletClient) {
const cid = response["cids"][0];
const { request } = await prepareWriteEvidenceModule({
functionName: "submitEvidence",
args: [BigInt(evidenceGroup), cid],
});
await wrapWithToast(async () => await walletClient.writeContract(request), publicClient).then(
() => {
setMessage("");
close();
}
);
}
})
.catch()
.finally(() => setIsSending(false));
}}
/>
<EnsureAuth>
<Button
text="Submit"
isLoading={isSending}
disabled={isSending}
onClick={async () => {
setIsSending(true);
toast.info("Uploading to IPFS", toastOptions);
const formData = await constructEvidence(message, file);
uploadFormDataToIPFS(formData)
.then(async (res) => {
const response = await res.json();
if (res.status === 200 && walletClient) {
const cid = response["cids"][0];
const { request } = await prepareWriteEvidenceModule({
functionName: "submitEvidence",
args: [BigInt(evidenceGroup), cid],
});
await wrapWithToast(async () => await walletClient.writeContract(request), publicClient).then(
() => {
setMessage("");
close();
}
);
}
})
.catch()
.finally(() => setIsSending(false));
}}
/>
</EnsureAuth>
</EnsureChain>
</ButtonArea>
</StyledModal>
Expand Down
37 changes: 22 additions & 15 deletions web/src/pages/Resolver/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { landscapeStyle } from "styles/landscapeStyle";
import { responsiveSize } from "styles/responsiveSize";

import ConnectWallet from "components/ConnectWallet";
import { EnsureAuth } from "components/EnsureAuth";
import HeroImage from "components/HeroImage";

import Description from "./Briefing/Description";
Expand Down Expand Up @@ -41,6 +42,10 @@ const ConnectWalletContainer = styled.div`
color: ${({ theme }) => theme.primaryText};
`;

const StyledEnsureAuth = styled(EnsureAuth)`
align-self: center;
`;

const MiddleContentContainer = styled.div`
display: flex;
justify-content: center;
Expand Down Expand Up @@ -71,21 +76,23 @@ const DisputeResolver: React.FC = () => {
<Container>
{isConnected && !isPreviewPage ? <StyledLabel>Start a case</StyledLabel> : null}
{isConnected ? (
<MiddleContentContainer>
{isConnected && !isPreviewPage ? <Timeline /> : null}
<Routes>
<Route index element={<Navigate to="title" replace />} />
<Route path="/title/*" element={<Title />} />
<Route path="/description/*" element={<Description />} />
<Route path="/court/*" element={<Court />} />
<Route path="/category/*" element={<Category />} />
<Route path="/jurors/*" element={<Jurors />} />
<Route path="/voting-options/*" element={<VotingOptions />} />
<Route path="/notable-persons/*" element={<NotablePersons />} />
<Route path="/policy/*" element={<Policy />} />
<Route path="/preview/*" element={<Preview />} />
</Routes>
</MiddleContentContainer>
<StyledEnsureAuth>
<MiddleContentContainer>
{isConnected && !isPreviewPage ? <Timeline /> : null}
<Routes>
<Route index element={<Navigate to="title" replace />} />
<Route path="/title/*" element={<Title />} />
<Route path="/description/*" element={<Description />} />
<Route path="/court/*" element={<Court />} />
<Route path="/category/*" element={<Category />} />
<Route path="/jurors/*" element={<Jurors />} />
<Route path="/voting-options/*" element={<VotingOptions />} />
<Route path="/notable-persons/*" element={<NotablePersons />} />
<Route path="/policy/*" element={<Policy />} />
<Route path="/preview/*" element={<Preview />} />
</Routes>
</MiddleContentContainer>
</StyledEnsureAuth>
) : (
<ConnectWalletContainer>
To create a new dispute, connect first
Expand Down
5 changes: 5 additions & 0 deletions web/src/utils/uploadFormDataToIPFS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { toast } from "react-toastify";
import { OPTIONS } from "utils/wrapWithToast";

export function uploadFormDataToIPFS(formData: FormData, operation = "evidence"): Promise<Response> {
const authToken = sessionStorage.getItem("auth-token")?.replace(/"/g, "");

return toast.promise<Response, Error>(
fetch(`/.netlify/functions/uploadToIPFS?key=kleros-v2&operation=${operation}`, {
method: "POST",
headers: {
"x-auth-token": authToken ?? "",
},
body: formData,
}).then(async (response) => {
if (response.status !== 200) {
Expand Down

0 comments on commit 83e46e3

Please sign in to comment.