Skip to content

Commit 6f16615

Browse files
authored
Merge pull request #136 from dedis/default-proxy-update
Gets the default proxy with the backend
2 parents f3c332c + 2ccd480 commit 6f16615

File tree

10 files changed

+213
-13
lines changed

10 files changed

+213
-13
lines changed

docs/frontend_doc.md

+18
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,24 @@ Return:
332332
`123.456.9%3A9000`. The native javascript functions `decodeURIComponent` and
333333
`encodeURIComponent` can be used.
334334

335+
## Get the default proxy address
336+
337+
| | |
338+
|--------|---------------------|
339+
| URL | `/api/config/proxy` |
340+
| Method | `GET` |
341+
| Input | |
342+
343+
344+
Return:
345+
346+
`200 OK` `text/plain`
347+
348+
```
349+
http://example.com/
350+
```
351+
352+
335353
# Production settings
336354

337355
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.

web/frontend/src/components/utils/Endpoints.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ export const getProxyAddress = (NodeAddr: string) => `/api/proxies/${encodeURICo
2626
export const getProxiesAddresses = '/api/proxies';
2727

2828
// public information can be directly fetched from dela nodes
29-
export const election = (ElectionID: string) =>
30-
`${process.env.REACT_APP_PROXY}/evoting/elections/${ElectionID}`;
31-
export const elections = `${process.env.REACT_APP_PROXY}/evoting/elections`;
29+
export const election = (proxy: string, ElectionID: string) =>
30+
new URL(`/evoting/elections/${ElectionID}`, proxy).href;
31+
export const elections = (proxy: string) => {
32+
return new URL('/evoting/elections', proxy).href;
33+
};
34+
35+
// get the default proxy address
36+
export const getProxyConfig = '/api/config/proxy';
3237

3338
// To remove
3439
export const ENDPOINT_EVOTING_RESULT = '/api/evoting/result';
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { CheckIcon, PencilIcon, RefreshIcon } from '@heroicons/react/outline';
2+
import { FlashContext, FlashLevel, ProxyContext } from 'index';
3+
import { ChangeEvent, FC, createRef, useContext, useEffect, useState } from 'react';
4+
import * as endpoints from './Endpoints';
5+
6+
const proxyKey = 'proxy';
7+
8+
const ProxyInput: FC = () => {
9+
const fctx = useContext(FlashContext);
10+
const pctx = useContext(ProxyContext);
11+
12+
const [proxy, setProxy] = useState<string>(pctx.getProxy());
13+
const [inputVal, setInputVal] = useState('');
14+
const [inputChanging, setInputChanging] = useState(false);
15+
const [inputWidth, setInputWidth] = useState(0);
16+
17+
const proxyTextRef = createRef<HTMLDivElement>();
18+
19+
const fetchFromBackend = async () => {
20+
try {
21+
const response = await fetch(endpoints.getProxyConfig);
22+
if (!response.ok) {
23+
const js = await response.json();
24+
throw new Error(JSON.stringify(js));
25+
} else {
26+
setProxy(await response.text());
27+
}
28+
} catch (e) {
29+
fctx.addMessage(`Failed to get proxy: ${proxy}`, FlashLevel.Error);
30+
}
31+
};
32+
33+
// update the proxy context and sessionStore each time the proxy changes
34+
useEffect(() => {
35+
sessionStorage.setItem(proxyKey, proxy);
36+
pctx.setProxy(proxy);
37+
setInputVal(proxy);
38+
}, [proxy]);
39+
40+
// function called by the "refresh" button
41+
const getDefault = () => {
42+
fetchFromBackend();
43+
fctx.addMessage('Proxy updated to default', FlashLevel.Info);
44+
};
45+
46+
const updateProxy = () => {
47+
try {
48+
new URL(inputVal);
49+
} catch {
50+
fctx.addMessage('invalid URL', FlashLevel.Error);
51+
return;
52+
}
53+
54+
setInputChanging(false);
55+
setProxy(inputVal);
56+
};
57+
58+
const editProxy = () => {
59+
setInputWidth(proxyTextRef.current.clientWidth);
60+
setInputChanging(true);
61+
};
62+
63+
return (
64+
<div className="flex flex-row items-center">
65+
{inputChanging ? (
66+
<>
67+
<input
68+
value={inputVal}
69+
onChange={(e: ChangeEvent<HTMLInputElement>) => setInputVal(e.target.value)}
70+
className="mt-1 ml-3 border rounded-md p-2"
71+
style={{ width: `${inputWidth + 3}px` }}
72+
/>
73+
<div className="ml-1">
74+
<button className={`border p-1 rounded-md }`} onClick={updateProxy}>
75+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
76+
</button>
77+
</div>
78+
</>
79+
) : (
80+
<>
81+
<div
82+
ref={proxyTextRef}
83+
className="mt-1 ml-3 border border-transparent p-2"
84+
onClick={editProxy}>
85+
{inputVal}
86+
</div>
87+
<div className="">
88+
<button className="hover:text-indigo-500 p-1 rounded-md" onClick={editProxy}>
89+
<PencilIcon className="m-1 h-3 w-3" aria-hidden="true" />
90+
</button>
91+
</div>
92+
</>
93+
)}
94+
<button
95+
onClick={getDefault}
96+
className="flex flex-row items-center hover:text-indigo-500 p-1 rounded-md">
97+
get default
98+
<RefreshIcon className="m-1 h-3 w-3" aria-hidden="true" />
99+
</button>
100+
</div>
101+
);
102+
};
103+
104+
export default ProxyInput;

web/frontend/src/components/utils/useElection.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ import useFetchCall from './useFetchCall';
22
import * as endpoints from './Endpoints';
33
import { useFillElectionInfo } from './FillElectionInfo';
44
import { ID } from 'types/configuration';
5+
import { useContext } from 'react';
6+
import { ProxyContext } from 'index';
57

68
// Custom hook that fetches an election given its id and returns its
79
// different parameters
810
const useElection = (electionID: ID) => {
11+
const pctx = useContext(ProxyContext);
12+
913
const request = {
1014
method: 'GET',
1115
};
12-
const [data, loading, error] = useFetchCall(endpoints.election(electionID), request);
16+
const [data, loading, error] = useFetchCall(
17+
endpoints.election(pctx.getProxy(), electionID),
18+
request
19+
);
1320
const {
1421
status,
1522
setStatus,

web/frontend/src/index.tsx

+46-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import App from 'layout/App';
77
import reportWebVitals from 'reportWebVitals';
88
import ShortUniqueId from 'short-unique-id';
99

10+
import * as endpoints from 'components/utils/Endpoints';
11+
1012
const flashTimeout = 4000;
1113

1214
// By default we load the mock messages when not in production. This is handy
@@ -71,6 +73,28 @@ class FlashMessage {
7173
// the flash context handles flash messages across the app
7274
export const FlashContext = createContext<FlashState>(undefined);
7375

76+
// the proxy state provides the proxy address across all the app
77+
export interface ProxyState {
78+
getProxy(): string;
79+
setProxy(p: string);
80+
}
81+
82+
export class ProxyHolder implements ProxyState {
83+
proxy: string;
84+
85+
getProxy(): string {
86+
return this.proxy;
87+
}
88+
89+
setProxy(p: string) {
90+
this.proxy = p;
91+
}
92+
}
93+
94+
const defaultProxyState = new ProxyHolder();
95+
96+
export const ProxyContext = createContext<ProxyState>(defaultProxyState);
97+
7498
// A small elements to display that the page is loading, should be something
7599
// more elegant in the future and be its own component.
76100
const Loading: FC = () => (
@@ -159,6 +183,22 @@ const AppContainer = () => {
159183
},
160184
};
161185

186+
const setDefaultProxy = async () => {
187+
let proxy = sessionStorage.getItem('proxy');
188+
189+
if (proxy === null) {
190+
const response = await fetch(endpoints.getProxyConfig);
191+
if (!response.ok) {
192+
const js = await response.json();
193+
throw new Error(`Failed to get the default proxy: ${JSON.stringify(js)}`);
194+
}
195+
196+
proxy = await response.text();
197+
}
198+
199+
defaultProxyState.setProxy(proxy);
200+
};
201+
162202
useEffect(() => {
163203
const req = {
164204
method: 'GET',
@@ -182,6 +222,9 @@ const AppContainer = () => {
182222
role: result.role,
183223
});
184224

225+
// wait for the default proxy to be set
226+
await setDefaultProxy();
227+
185228
setContent(<App />);
186229
} catch (e) {
187230
setContent(<Failed>{e.toString()}</Failed>);
@@ -194,7 +237,9 @@ const AppContainer = () => {
194237

195238
return (
196239
<FlashContext.Provider value={flashState}>
197-
<AuthContext.Provider value={auth}>{content}</AuthContext.Provider>
240+
<AuthContext.Provider value={auth}>
241+
<ProxyContext.Provider value={defaultProxyState}>{content}</ProxyContext.Provider>
242+
</AuthContext.Provider>
198243
</FlashContext.Provider>
199244
);
200245
};

web/frontend/src/layout/Footer.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
import ProxyInput from 'components/utils/proxy';
2+
13
const Footer = () => (
24
<div className="flex flex-row border-t justify-center bg-white items-center w-full p-4 text-gray-300 text-xs">
35
<footer>
4-
<div className="max-w-7xl mx-auto py-2 px-4 overflow-hidden sm:px-6 lg:px-8">
6+
<div className="flex flex-row items-center max-w-7xl mx-auto py-2 px-4 overflow-hidden sm:px-6 lg:px-8">
57
<span className="text-gray-400"> &copy; 2022 DEDIS LAB - </span>
68
<a className="text-gray-600" href="https://github.com/dedis/dela">
79
https://github.com/dedis/dela
810
</a>
11+
<div className="px-10">
12+
<ProxyInput />
13+
</div>
914
</div>
1015
</footer>
1116
</div>

web/frontend/src/mocks/handlers.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const SETUP_TIMER = 3000;
4343
const SHUFFLE_TIMER = 2000;
4444
const DECRYPT_TIMER = 4000;
4545

46+
const defaultProxy = 'http://localhost/';
47+
4648
const isAuthorized = (roles: UserRole[]): boolean => {
4749
const id = sessionStorage.getItem('id');
4850
const userRole = mockUserDB.find(({ sciper }) => sciper === id).role;
@@ -91,7 +93,7 @@ export const handlers = [
9193
return res(ctx.status(200));
9294
}),
9395

94-
rest.get(endpoints.elections, async (req, res, ctx) => {
96+
rest.get(endpoints.elections(defaultProxy), async (req, res, ctx) => {
9597
await new Promise((r) => setTimeout(r, RESPONSE_TIME));
9698

9799
return res(
@@ -104,7 +106,7 @@ export const handlers = [
104106
);
105107
}),
106108

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

@@ -418,4 +420,12 @@ export const handlers = [
418420

419421
return res(ctx.status(200), ctx.text('Action successfully done'));
420422
}),
423+
424+
rest.get(endpoints.getProxyConfig, async (req, res, ctx) => {
425+
await new Promise((r) => setTimeout(r, RESPONSE_TIME));
426+
427+
const response = defaultProxy;
428+
429+
return res(ctx.status(200), ctx.text(response));
430+
}),
421431
];

web/frontend/src/pages/election/Index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import * as endpoints from 'components/utils/Endpoints';
77
import Loading from 'pages/Loading';
88
import { LightElectionInfo, Status } from 'types/election';
99
import ElectionTableFilter from './components/ElectionTableFilter';
10-
import { FlashContext, FlashLevel } from 'index';
10+
import { FlashContext, FlashLevel, ProxyContext } from 'index';
1111

1212
const ElectionIndex: FC = () => {
1313
const { t } = useTranslation();
1414
const fctx = useContext(FlashContext);
15+
const pctx = useContext(ProxyContext);
1516

1617
const [statusToKeep, setStatusToKeep] = useState<Status>(null);
1718
const [elections, setElections] = useState<LightElectionInfo[]>(null);
@@ -25,7 +26,7 @@ const ElectionIndex: FC = () => {
2526
},
2627
};
2728

28-
const [data, dataLoading, error] = useFetchCall(endpoints.elections, request);
29+
const [data, dataLoading, error] = useFetchCall(endpoints.elections(pctx.getProxy()), request);
2930

3031
useEffect(() => {
3132
if (error !== null) {

web/frontend/src/pages/election/components/utils/useChangeAction.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ID } from 'types/configuration';
66
import { Action, OngoingAction, Status } from 'types/election';
77
import { pollDKG, pollElection } from './PollStatus';
88
import { NodeStatus } from 'types/node';
9-
import { FlashContext, FlashLevel } from 'index';
9+
import { FlashContext, FlashLevel, ProxyContext } from 'index';
1010
import { useNavigate } from 'react-router';
1111
import { ROUTE_ELECTION_INDEX } from 'Routes';
1212

@@ -65,6 +65,7 @@ const useChangeAction = (
6565

6666
const fctx = useContext(FlashContext);
6767
const navigate = useNavigate();
68+
const pctx = useContext(ProxyContext);
6869

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

166-
pollElection(endpoints.election(electionID), request, match, interval)
167+
pollElection(endpoints.election(pctx.getProxy(), electionID), request, match, interval)
167168
.then(
168169
() => onFullFilled(nextStatus),
169170
(reason: any) => onRejected(reason, previousStatus)

web/frontend/src/pages/election/components/utils/useGetResults.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { ID } from 'types/configuration';
22
import { Results } from 'types/election';
33
import * as endpoints from 'components/utils/Endpoints';
4+
import { useContext } from 'react';
5+
import { ProxyContext } from 'index';
46

57
const useGetResults = () => {
8+
const pctx = useContext(ProxyContext);
9+
610
async function getResults(
711
electionID: ID,
812
setError: React.Dispatch<any>,
@@ -14,7 +18,7 @@ const useGetResults = () => {
1418
};
1519

1620
try {
17-
const response = await fetch(endpoints.election(electionID), request);
21+
const response = await fetch(endpoints.election(pctx.getProxy(), electionID), request);
1822

1923
if (!response.ok) {
2024
throw Error(response.statusText);

0 commit comments

Comments
 (0)