Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gets the default proxy with the backend #136

Merged
merged 4 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/frontend_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,24 @@ Return:
`123.456.9%3A9000`. The native javascript functions `decodeURIComponent` and
`encodeURIComponent` can be used.

## Get the default proxy address

| | |
|--------|-----------------|
| URL | `/config/proxy` |
| Method | `GET` |
| Input | |


Return:

`200 OK` `text/plain`

```
http://example.com/
```


# Production settings

The two followings things that will be shown here is how to have https on the different webpages and how to make the app run on a on server.
Expand Down
2 changes: 1 addition & 1 deletion web/backend/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ app.use(express.urlencoded({ extended: true }));
// This endpoint allows anyone to get a "default" proxy. Clients can still use
// the proxy of their choice thought.
app.get('/api/config/proxy', (req, res) => {
res.status(200).send(process.env.FRONT_END_URL)
res.status(200).send(process.env.FRONT_END_URL);
});

const usersDB = lmdb.open<'admin' | 'operator', number>({
Expand Down
11 changes: 8 additions & 3 deletions web/frontend/src/components/utils/Endpoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ export const getProxyAddress = (NodeAddr: string) => `/api/proxies/${encodeURICo
export const getProxiesAddresses = '/api/proxies';

// public information can be directly fetched from dela nodes
export const election = (ElectionID: string) =>
`${process.env.REACT_APP_PROXY}/evoting/elections/${ElectionID}`;
export const elections = `${process.env.REACT_APP_PROXY}/evoting/elections`;
export const election = (proxy: string, ElectionID: string) =>
new URL(`/evoting/elections/${ElectionID}`, proxy).href;
export const elections = (proxy: string) => {
return new URL('/evoting/elections', proxy).href;
};

// get the default proxy address
export const getProxyConfig = '/api/config/proxy';

// To remove
export const ENDPOINT_EVOTING_RESULT = '/api/evoting/result';
103 changes: 103 additions & 0 deletions web/frontend/src/components/utils/proxy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { CheckIcon, PencilIcon, RefreshIcon } from '@heroicons/react/outline';
import { FlashContext, FlashLevel, ProxyContext } from 'index';
import { ChangeEvent, FC, createRef, useContext, useEffect, useState } from 'react';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import * as endpoints from './Endpoints';

const proxyKey = 'proxy';

const ProxyInput: FC = () => {
const fctx = useContext(FlashContext);
const pctx = useContext(ProxyContext);

const [proxy, setProxy] = useState<string>(pctx.getProxy());
const [inputVal, setInputVal] = useState('');
const [inputChanging, setInputChanging] = useState(false);
const [inputWidth, setInputWidth] = useState(0);

const proxyTextRef = createRef<HTMLDivElement>();

const fetchFromBackend = async () => {
try {
const response = await fetch('/api/config/proxy');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const response = await fetch('/api/config/proxy');
const response = await fetch(endpoints.getProxyConfig);

if (!response.ok) {
const js = await response.json();
throw new Error(JSON.stringify(js));
} else {
setProxy(await response.text());
}
} catch (e) {
fctx.addMessage(`Failed to get proxy: ${proxy}`, FlashLevel.Error);
}
};

// update the proxy context and sessionStore each time the proxy changes
useEffect(() => {
sessionStorage.setItem(proxyKey, proxy);
pctx.setProxy(proxy);
setInputVal(proxy);
}, [proxy]);

// function called by the "refresh" button
const getDefault = () => {
fetchFromBackend();
fctx.addMessage('Proxy updated to default', FlashLevel.Info);
};

const updateProxy = () => {
try {
new URL(inputVal);
} catch {
fctx.addMessage('invalid URL', FlashLevel.Error);
return;
}

setInputChanging(false);
setProxy(inputVal);
};

const editProxy = () => {
setInputWidth(proxyTextRef.current.clientWidth);
setInputChanging(true);
};

return (
<div className="flex flex-row items-center">
{inputChanging ? (
<>
<input
value={inputVal}
onChange={(e: ChangeEvent<HTMLInputElement>) => setInputVal(e.target.value)}
className="mt-1 ml-3 border rounded-md p-2"
style={{ width: `${inputWidth + 3}px` }}
/>
<div className="ml-1">
<button className={`border p-1 rounded-md }`} onClick={updateProxy}>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</button>
</div>
</>
) : (
<>
<div
ref={proxyTextRef}
className="mt-1 ml-3 border border-transparent p-2"
onClick={editProxy}>
{inputVal}
</div>
<div className="">
<button className="hover:text-indigo-500 p-1 rounded-md" onClick={editProxy}>
<PencilIcon className="m-1 h-3 w-3" aria-hidden="true" />
</button>
</div>
</>
)}
<button
onClick={getDefault}
className="flex flex-row items-center hover:text-indigo-500 p-1 rounded-md">
get default
<RefreshIcon className="m-1 h-3 w-3" aria-hidden="true" />
</button>
</div>
);
};

export default ProxyInput;
9 changes: 8 additions & 1 deletion web/frontend/src/components/utils/useElection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import useFetchCall from './useFetchCall';
import * as endpoints from './Endpoints';
import { useFillElectionInfo } from './FillElectionInfo';
import { ID } from 'types/configuration';
import { useContext } from 'react';
import { ProxyContext } from 'index';

// Custom hook that fetches an election given its id and returns its
// different parameters
const useElection = (electionID: ID) => {
const pctx = useContext(ProxyContext);

const request = {
method: 'GET',
};
const [data, loading, error] = useFetchCall(endpoints.election(electionID), request);
const [data, loading, error] = useFetchCall(
endpoints.election(pctx.getProxy(), electionID),
request
);
const {
status,
setStatus,
Expand Down
45 changes: 44 additions & 1 deletion web/frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ class FlashMessage {
// the flash context handles flash messages across the app
export const FlashContext = createContext<FlashState>(undefined);

// the proxy state provides the proxy address across all the app
export interface ProxyState {
getProxy(): string;
setProxy(p: string);
}

export class ProxyHolder implements ProxyState {
proxy: string;

getProxy(): string {
return this.proxy;
}

setProxy(p: string) {
this.proxy = p;
}
}

const defaultProxyState = new ProxyHolder();

export const ProxyContext = createContext<ProxyState>(defaultProxyState);

// A small elements to display that the page is loading, should be something
// more elegant in the future and be its own component.
const Loading: FC = () => (
Expand Down Expand Up @@ -159,6 +181,22 @@ const AppContainer = () => {
},
};

const setDefaultProxy = async () => {
let proxy = sessionStorage.getItem('proxy');

if (proxy === null) {
const response = await fetch('/api/config/proxy');
Copy link
Contributor

@cmsigrist cmsigrist Jun 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

endpoints.getProxyConfig can also be used here :D

if (!response.ok) {
const js = await response.json();
throw new Error(`Failed to get the default proxy: ${JSON.stringify(js)}`);
}

proxy = await response.text();
}

defaultProxyState.setProxy(proxy);
};

useEffect(() => {
const req = {
method: 'GET',
Expand All @@ -182,6 +220,9 @@ const AppContainer = () => {
role: result.role,
});

// wait for the default proxy to be set
await setDefaultProxy();

setContent(<App />);
} catch (e) {
setContent(<Failed>{e.toString()}</Failed>);
Expand All @@ -194,7 +235,9 @@ const AppContainer = () => {

return (
<FlashContext.Provider value={flashState}>
<AuthContext.Provider value={auth}>{content}</AuthContext.Provider>
<AuthContext.Provider value={auth}>
<ProxyContext.Provider value={defaultProxyState}>{content}</ProxyContext.Provider>
</AuthContext.Provider>
</FlashContext.Provider>
);
};
Expand Down
7 changes: 6 additions & 1 deletion web/frontend/src/layout/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import ProxyInput from 'components/utils/proxy';

const Footer = () => (
<div className="flex flex-row border-t justify-center bg-white items-center w-full p-4 text-gray-300 text-xs">
<footer>
<div className="max-w-7xl mx-auto py-2 px-4 overflow-hidden sm:px-6 lg:px-8">
<div className="flex flex-row items-center max-w-7xl mx-auto py-2 px-4 overflow-hidden sm:px-6 lg:px-8">
<span className="text-gray-400"> &copy; 2022 DEDIS LAB - </span>
<a className="text-gray-600" href="https://github.com/dedis/dela">
https://github.com/dedis/dela
</a>
<div className="px-10">
<ProxyInput />
</div>
</div>
</footer>
</div>
Expand Down
14 changes: 12 additions & 2 deletions web/frontend/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const SETUP_TIMER = 4000;
const SHUFFLE_TIMER = 2000;
const DECRYPT_TIMER = 8000;

const defaultProxy = 'http://localhost/';

const isAuthorized = (roles: UserRole[]): boolean => {
const id = sessionStorage.getItem('id');
const userRole = mockUserDB.find(({ sciper }) => sciper === id).role;
Expand Down Expand Up @@ -91,7 +93,7 @@ export const handlers = [
return res(ctx.status(200));
}),

rest.get(endpoints.elections, async (req, res, ctx) => {
rest.get(endpoints.elections(defaultProxy), async (req, res, ctx) => {
await new Promise((r) => setTimeout(r, RESPONSE_TIME));

return res(
Expand All @@ -104,7 +106,7 @@ export const handlers = [
);
}),

rest.get(endpoints.election(':ElectionID'), async (req, res, ctx) => {
rest.get(endpoints.election(defaultProxy, ':ElectionID'), async (req, res, ctx) => {
const { ElectionID } = req.params;
await new Promise((r) => setTimeout(r, RESPONSE_TIME));

Expand Down Expand Up @@ -408,4 +410,12 @@ export const handlers = [

return res(ctx.status(200), ctx.text('Action successfully done'));
}),

rest.get(endpoints.getProxyConfig, async (req, res, ctx) => {
await new Promise((r) => setTimeout(r, RESPONSE_TIME));

const response = defaultProxy;

return res(ctx.status(200), ctx.text(response));
}),
];
5 changes: 3 additions & 2 deletions web/frontend/src/pages/election/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import * as endpoints from 'components/utils/Endpoints';
import Loading from 'pages/Loading';
import { LightElectionInfo, Status } from 'types/election';
import ElectionTableFilter from './components/ElectionTableFilter';
import { FlashContext, FlashLevel } from 'index';
import { FlashContext, FlashLevel, ProxyContext } from 'index';

const ElectionIndex: FC = () => {
const { t } = useTranslation();
const fctx = useContext(FlashContext);
const pctx = useContext(ProxyContext);

const [statusToKeep, setStatusToKeep] = useState<Status>(null);
const [elections, setElections] = useState<LightElectionInfo[]>(null);
Expand All @@ -24,7 +25,7 @@ const ElectionIndex: FC = () => {
},
};

const [data, dataLoading, error] = useFetchCall(endpoints.elections, request);
const [data, dataLoading, error] = useFetchCall(endpoints.elections(pctx.getProxy()), request);

useEffect(() => {
if (error !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ID } from 'types/configuration';
import { Action, OngoingAction, Status } from 'types/election';
import { pollDKG, pollElection } from './PollStatus';
import { NodeStatus } from 'types/node';
import { FlashContext, FlashLevel } from 'index';
import { FlashContext, FlashLevel, ProxyContext } from 'index';
import { useNavigate } from 'react-router';
import { ROUTE_ELECTION_INDEX } from 'Routes';

Expand Down Expand Up @@ -65,6 +65,7 @@ const useChangeAction = (

const fctx = useContext(FlashContext);
const navigate = useNavigate();
const pctx = useContext(ProxyContext);

const modalClose = (
<ConfirmModal
Expand Down Expand Up @@ -163,7 +164,7 @@ const useChangeAction = (
// We stop polling when the status has changed to nextStatus
const match = (s: Status) => s === nextStatus;

pollElection(endpoints.election(electionID), request, match, interval)
pollElection(endpoints.election(pctx.getProxy(), electionID), request, match, interval)
.then(
() => onFullFilled(nextStatus),
(reason: any) => onRejected(reason, previousStatus)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { ID } from 'types/configuration';
import { Results } from 'types/election';
import * as endpoints from 'components/utils/Endpoints';
import { useContext } from 'react';
import { ProxyContext } from 'index';

const useGetResults = () => {
const pctx = useContext(ProxyContext);

async function getResults(
electionID: ID,
setError: React.Dispatch<any>,
Expand All @@ -14,7 +18,7 @@ const useGetResults = () => {
};

try {
const response = await fetch(endpoints.election(electionID), request);
const response = await fetch(endpoints.election(pctx.getProxy(), electionID), request);

if (!response.ok) {
throw Error(response.statusText);
Expand Down