Skip to content

Commit d2db818

Browse files
authored
[Faucet] feat: Updated to load configuration during runtime (#2289)
1 parent aa8ebab commit d2db818

File tree

15 files changed

+328
-240
lines changed

15 files changed

+328
-240
lines changed

apps/faucet/.env.sample

Lines changed: 0 additions & 13 deletions
This file was deleted.

apps/faucet/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"react": "^18.3.0",
2525
"react-dom": "^18.3.0",
2626
"react-router-dom": "^6.0.0",
27-
"styled-components": "^6.1.19"
27+
"styled-components": "^6.1.19",
28+
"toml": "^3.0.0"
2829
},
2930
"devDependencies": {
3031
"@babel/plugin-transform-modules-commonjs": "^7.20.11",

apps/faucet/public/config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Override any of the following with custom values:
2+
3+
#base_url = "http://127.0.0.1:5000"
4+
#endpoint = "/api/v1/faucet"
5+
#limit = 1_000_000_000
6+
#turnstile_sitekey = "0x4AAAAAAASomePublicSiteKey"
Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
const { exec } = require("child_process");
2+
const path = require("path");
3+
const toml = require("toml");
4+
const fs = require("fs");
25
require("dotenv").config();
36

4-
const {
5-
NAMADA_INTERFACE_FAUCET_API_URL: apiUrl = "http://localhost:5000",
6-
NAMADA_INTERFACE_PROXY_PORT: proxyPort = "9000",
7-
} = process.env;
7+
const { NAMADA_INTERFACE_PROXY_PORT: proxyPort = "9000" } = process.env;
88

9-
const lcpCommand = `lcp --proxyUrl ${apiUrl} --port ${proxyPort}`;
9+
const runProxy = () => {
10+
const tomlPath = path.join(__dirname, "../public/config.toml");
11+
fs.readFile(tomlPath, "utf-8", (err, tomlData) => {
12+
if (err) {
13+
console.error("Error reading config.toml:", err);
14+
return;
15+
}
1016

11-
if (apiUrl) {
12-
console.log(`Running command '${lcpCommand}'\n`);
13-
console.log("Starting local-cors-proxy for faucet API endpoint");
14-
console.log(`-> ${apiUrl} proxied to http://localhost:${proxyPort}/proxy\n`);
17+
const config = toml.parse(tomlData);
18+
const lcpCommand = `lcp --proxyUrl ${config.base_url || "http://localhost:5000/"} --port ${proxyPort}`;
1519

16-
exec(lcpCommand, (error, stdout, stderr) => {
17-
console.log(stdout);
18-
console.log(stderr);
19-
if (error !== null) {
20-
console.log(`exec error: ${error}`);
21-
}
20+
console.log(`Running command '${lcpCommand}'\n`);
21+
console.log("Starting local-cors-proxy for faucet API endpoint");
22+
console.log(
23+
`-> ${config.base_url} proxied to http://localhost:${proxyPort}/proxy\n`
24+
);
25+
26+
exec(lcpCommand, (error, stdout, stderr) => {
27+
console.log(stdout);
28+
console.log(stderr);
29+
if (error !== null) {
30+
console.log(`exec error: ${error}`);
31+
}
32+
});
2233
});
23-
}
34+
};
35+
36+
runProxy();

apps/faucet/src/App/App.tsx

Lines changed: 11 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,206 +1,27 @@
1-
import { createContext, useEffect, useState } from "react";
2-
import { GoGear } from "react-icons/go";
1+
import { useEffect, useState } from "react";
32
import { ThemeProvider } from "styled-components";
43

5-
import { Alert, Modal } from "@namada/components";
64
import { ColorMode, getTheme } from "@namada/utils";
75

8-
import {
9-
AppContainer,
10-
BackgroundImage,
11-
BottomSection,
12-
ContentContainer,
13-
FaucetContainer,
14-
GlobalStyles,
15-
InfoContainer,
16-
SettingsButton,
17-
SettingsButtonContainer,
18-
TopSection,
19-
} from "App/App.components";
20-
import { FaucetForm } from "App/Faucet";
6+
import { GlobalStyles } from "App/App.components";
217

22-
import { API, toNam } from "utils";
23-
import dotsBackground from "../../public/bg-dots.svg";
24-
import {
25-
AppBanner,
26-
AppHeader,
27-
CallToActionCard,
28-
CardsContainer,
29-
Faq,
30-
} from "./Common";
31-
import { SettingsForm } from "./SettingsForm";
32-
33-
const DEFAULT_URL = "http://localhost:5000";
34-
const DEFAULT_LIMIT = 1_000_000_000;
35-
36-
const {
37-
NAMADA_INTERFACE_FAUCET_API_URL: faucetApiUrl = DEFAULT_URL,
38-
NAMADA_INTERFACE_PROXY: isProxied,
39-
NAMADA_INTERFACE_PROXY_PORT: proxyPort = 9000,
40-
} = process.env;
41-
42-
const baseUrl =
43-
isProxied ? `http://localhost:${proxyPort}/proxy` : faucetApiUrl;
44-
const runFullNodeUrl = "https://docs.namada.net/operators/ledger";
45-
const becomeBuilderUrl = "https://docs.namada.net/integrating-with-namada";
46-
47-
type Settings = {
48-
difficulty?: number;
49-
tokens?: Record<string, string>;
50-
startsAt: number;
51-
startsAtText?: string;
52-
withdrawLimit: number;
53-
};
54-
55-
type AppContext = {
56-
baseUrl: string;
57-
settingsError?: string;
58-
api: API;
59-
isTestnetLive: boolean;
60-
settings: Settings;
61-
setApi: (api: API) => void;
62-
setUrl: (url: string) => void;
63-
setIsModalOpen: (value: boolean) => void;
64-
};
65-
66-
const START_TIME_UTC = 1702918800;
67-
const START_TIME_TEXT = new Date(START_TIME_UTC * 1000).toLocaleString(
68-
"en-gb",
69-
{
70-
timeZone: "UTC",
71-
month: "long",
72-
day: "numeric",
73-
year: "numeric",
74-
hour: "numeric",
75-
minute: "numeric",
76-
}
77-
);
78-
79-
export const AppContext = createContext<AppContext | null>(null);
8+
import { Config, getConfig } from "config";
9+
import { FaucetApp } from "./FaucetApp";
8010

8111
export const App = (): JSX.Element => {
8212
const initialColorMode = "dark";
83-
13+
const [config, setConfig] = useState<Config>();
8414
const [colorMode, _] = useState<ColorMode>(initialColorMode);
85-
const [isTestnetLive, setIsTestnetLive] = useState(true);
86-
const [settings, setSettings] = useState<Settings>({
87-
startsAt: START_TIME_UTC,
88-
startsAtText: `${START_TIME_TEXT} UTC`,
89-
withdrawLimit: toNam(DEFAULT_LIMIT),
90-
});
91-
const [url, setUrl] = useState(localStorage.getItem("baseUrl") || baseUrl);
92-
const [api, setApi] = useState<API>(new API(url));
93-
const [isModalOpen, setIsModalOpen] = useState(false);
94-
const [settingsError, setSettingsError] = useState<string>();
9515
const theme = getTheme(colorMode);
9616

97-
const fetchSettings = async (api: API): Promise<void> => {
98-
const {
99-
difficulty,
100-
tokens_alias_to_address: tokens,
101-
withdraw_limit: withdrawLimit = DEFAULT_LIMIT,
102-
} = await api.settings().catch((e) => {
103-
const message = e.errors?.message;
104-
setSettingsError(`Error requesting settings: ${message?.join(" ")}`);
105-
throw new Error(e);
106-
});
107-
// Append difficulty level and tokens to settings
108-
setSettings({
109-
...settings,
110-
difficulty,
111-
tokens,
112-
withdrawLimit: toNam(withdrawLimit),
113-
});
114-
};
115-
11617
useEffect(() => {
117-
// Sync url to localStorage
118-
localStorage.setItem("baseUrl", url);
119-
const api = new API(url);
120-
setApi(api);
121-
const { startsAt } = settings;
122-
const now = new Date();
123-
const nowUTC = Date.UTC(
124-
now.getUTCFullYear(),
125-
now.getUTCMonth(),
126-
now.getUTCDate(),
127-
now.getUTCHours(),
128-
now.getUTCMinutes()
129-
);
130-
const startsAtToMilliseconds = startsAt * 1000;
131-
if (nowUTC < startsAtToMilliseconds) {
132-
setIsTestnetLive(false);
133-
}
134-
135-
// Fetch settings from faucet API
136-
fetchSettings(api)
137-
.then(() => setSettingsError(undefined))
138-
.catch((e) => setSettingsError(`Failed to load settings! ${e}`));
139-
}, [url]);
18+
getConfig().then((config) => setConfig(config));
19+
}, []);
14020

14121
return (
142-
<AppContext.Provider
143-
value={{
144-
api,
145-
isTestnetLive,
146-
baseUrl: url,
147-
settingsError,
148-
settings,
149-
setApi,
150-
setUrl,
151-
setIsModalOpen,
152-
}}
153-
>
154-
<ThemeProvider theme={theme}>
155-
<GlobalStyles colorMode={colorMode} />
156-
<AppBanner />
157-
<BackgroundImage imageUrl={dotsBackground} />
158-
<AppContainer>
159-
<ContentContainer>
160-
<SettingsButtonContainer>
161-
<SettingsButton
162-
onClick={() => setIsModalOpen(true)}
163-
title="Settings"
164-
>
165-
<GoGear />
166-
</SettingsButton>
167-
</SettingsButtonContainer>
168-
169-
<TopSection>
170-
<AppHeader />
171-
</TopSection>
172-
<FaucetContainer>
173-
{settingsError && (
174-
<InfoContainer>
175-
<Alert type="error">{settingsError}</Alert>
176-
</InfoContainer>
177-
)}
178-
179-
<FaucetForm isTestnetLive={isTestnetLive} />
180-
</FaucetContainer>
181-
{isModalOpen && (
182-
<Modal onClose={() => setIsModalOpen(false)}>
183-
<SettingsForm />
184-
</Modal>
185-
)}
186-
<BottomSection>
187-
<CardsContainer>
188-
<CallToActionCard
189-
description="Contribute to the Namada network's resiliency"
190-
title="RUN A FULL NODE"
191-
href={runFullNodeUrl}
192-
/>
193-
<CallToActionCard
194-
description="Integrate Namada into applications or extend its capabilities"
195-
title="BECOME A BUILDER"
196-
href={becomeBuilderUrl}
197-
/>
198-
</CardsContainer>
199-
<Faq />
200-
</BottomSection>
201-
</ContentContainer>
202-
</AppContainer>
203-
</ThemeProvider>
204-
</AppContext.Provider>
22+
<ThemeProvider theme={theme}>
23+
<GlobalStyles colorMode={colorMode} />
24+
{config && <FaucetApp config={config} />}
25+
</ThemeProvider>
20526
);
20627
};

apps/faucet/src/App/Common/AppBanner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { AppContext } from "App/App";
1+
import { FaucetAppContext } from "App/FaucetApp";
22
import React, { useContext } from "react";
33
import { Banner, BannerContents } from "./Banner.components";
44

55
export const AppBanner: React.FC = () => {
6-
const { isTestnetLive, settings } = useContext(AppContext)!;
6+
const { isTestnetLive, settings } = useContext(FaucetAppContext)!;
77
return (
88
<>
99
{!isTestnetLive && settings?.startsAtText && (

0 commit comments

Comments
 (0)