Skip to content

Commit

Permalink
feat(config): replace network management with network store package
Browse files Browse the repository at this point in the history
Also add validated env configs to get essential network store vars

refs #163, #313
  • Loading branch information
ygrishajev committed Oct 8, 2024
1 parent c3e761e commit 3d804d5
Show file tree
Hide file tree
Showing 20 changed files with 116 additions and 188 deletions.
1 change: 1 addition & 0 deletions apps/stats-web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# production
/build
env-config.schema.js

# misc
.DS_Store
Expand Down
1 change: 1 addition & 0 deletions apps/stats-web/env/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_API_BASE_URL=https://console-api.akash.network
1 change: 1 addition & 0 deletions apps/stats-web/env/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_API_BASE_URL=
1 change: 1 addition & 0 deletions apps/stats-web/env/.env.staging
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_API_BASE_URL=https://console-api-mainnet-staging.akash.network
11 changes: 11 additions & 0 deletions apps/stats-web/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
require("@akashnetwork/env-loader");
const { version } = require("./package.json");

try {
const { browserEnvSchema } = require("./env-config.schema");

browserEnvSchema.parse(process.env);
} catch (error) {
if (error.message.includes("Cannot find module")) {
console.warn("No env-config.schema.js found, skipping env validation");
}
}

/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
Expand Down
3 changes: 2 additions & 1 deletion apps/stats-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "0.20.0",
"private": true,
"scripts": {
"build": "next build",
"build": "npm run build-env-schemas && next build",
"build-env-schemas": "tsc src/config/env-config.schema.ts --outDir . --skipLibCheck",
"dev": "next dev",
"format": "prettier --write ./*.{ts,js,json} **/*.{ts,tsx,js,json}",
"lint": "eslint .",
Expand Down
4 changes: 2 additions & 2 deletions apps/stats-web/src/app/(home)/DashboardContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Spinner } from "@akashnetwork/ui/components";
import { Dashboard } from "./Dashboard";

import { Title } from "@/components/Title";
import { useSelectedNetwork } from "@/hooks/useSelectedNetwork";
import { useMarketData } from "@/queries";
import { useDashboardData } from "@/queries/useDashboardData";
import { networkStore } from "@/store/network.store";

export const DashboardContainer: React.FunctionComponent = () => {
const { data: dashboardData, isLoading: isLoadingDashboardData } = useDashboardData();
const { data: marketData, isLoading: isLoadingMarketData } = useMarketData();
const selectedNetwork = useSelectedNetwork();
const selectedNetwork = networkStore.useSelectedNetwork();
const isLoading = isLoadingMarketData || isLoadingDashboardData;

return (
Expand Down
3 changes: 2 additions & 1 deletion apps/stats-web/src/components/layout/CustomProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { CustomIntlProvider } from "./CustomIntlProvider";
import { PricingProvider } from "@/context/PricingProvider";
import { customColors } from "@/lib/colors";
import { queryClient } from "@/queries";
import { store } from "@/store/global.store";

function Providers({ children }: React.PropsWithChildren) {
return (
<CustomIntlProvider>
<QueryClientProvider client={queryClient}>
<Provider>
<Provider store={store}>
<ThemeProvider attribute="class" defaultTheme="system" storageKey="theme" enableSystem disableTransitionOnChange>
<CustomSnackbarProvider>
<PricingProvider>
Expand Down
40 changes: 6 additions & 34 deletions apps/stats-web/src/components/layout/NetworkSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,22 @@
"use client";
import React, { useEffect, useState } from "react";
import { MAINNET_ID, Network } from "@akashnetwork/network-store";
import React from "react";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Spinner } from "@akashnetwork/ui/components";

import { setNetworkVersion } from "@/lib/constants";
import { cn } from "@/lib/utils";
import { initiateNetworkData, networks } from "@/store/networkStore";
import { networkStore } from "@/store/network.store";

interface NetworkSelectProps {
className?: string;
}

const NetworkSelect: React.FC<NetworkSelectProps> = ({ className }) => {
const [isLoadingSettings, setIsLoadingSettings] = useState(true);
const [selectedNetworkId, setSelectedNetworkId] = useState<Network["id"]>(MAINNET_ID);

useEffect(() => {
async function init() {
await initiateNetworkData();
setNetworkVersion();

const selectedNetworkId = localStorage.getItem("selectedNetworkId") as Network["id"];
if (selectedNetworkId) {
setSelectedNetworkId(selectedNetworkId);
}

setIsLoadingSettings(false);
}

init();
}, []);

const onSelectNetworkChange = (networkId: Network["id"]) => {
setSelectedNetworkId(networkId);

// Set in the settings and local storage
localStorage.setItem("selectedNetworkId", networkId);
// Reset the ui to reload the settings for the currently selected network

location.reload();
};
const [{ isLoading: isLoadingNetworks, data: networks }] = networkStore.useNetworksStore();
const [selectedNetworkId, setSelectedNetworkId] = networkStore.useSelectedNetworkIdStore({ reloadOnChange: true });

return (
<Select value={selectedNetworkId} disabled={isLoadingSettings} onValueChange={onSelectNetworkChange}>
<Select value={selectedNetworkId} disabled={isLoadingNetworks} onValueChange={setSelectedNetworkId}>
<SelectTrigger className={cn("h-[30px] min-w-[180px] max-w-[200px]", className)}>
{isLoadingSettings && <Spinner size="small" />}
{isLoadingNetworks && <Spinner size="small" />}
<SelectValue placeholder="Select network" />
</SelectTrigger>
<SelectContent>
Expand Down
6 changes: 6 additions & 0 deletions apps/stats-web/src/config/browser-env.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { validateStaticEnvVars } from "./env-config.schema";

export const browserEnvConfig = validateStaticEnvVars({
NEXT_PUBLIC_DEFAULT_NETWORK_ID: process.env.NEXT_PUBLIC_DEFAULT_NETWORK_ID,
NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL
});
29 changes: 29 additions & 0 deletions apps/stats-web/src/config/env-config.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { z } from "zod";

const networkId = z.enum(["mainnet", "sandbox", "testnet"]);
const coercedBoolean = () => z.enum(["true", "false"]).transform(val => val === "true");

export const browserEnvSchema = z.object({
NEXT_PUBLIC_DEFAULT_NETWORK_ID: networkId.optional().default("mainnet"),
NEXT_PUBLIC_API_BASE_URL: z.string().url()
});

export const serverEnvSchema = browserEnvSchema.extend({
MAINTENANCE_MODE: coercedBoolean().optional().default("false"),
BASE_API_MAINNET_URL: z.string().url(),
BASE_API_TESTNET_URL: z.string().url(),
BASE_API_SANDBOX_URL: z.string().url()
});

export type BrowserEnvConfig = z.infer<typeof browserEnvSchema>;
export type ServerEnvConfig = z.infer<typeof serverEnvSchema>;

export const validateStaticEnvVars = (config: Record<string, unknown>) => browserEnvSchema.parse(config);
export const validateRuntimeEnvVars = (config: Record<string, unknown>) => {
if (process.env.NEXT_PHASE === "phase-production-build") {
console.log("Skipping validation of serverEnvConfig during build");
return config as ServerEnvConfig;
} else {
return serverEnvSchema.parse(config);
}
};
5 changes: 5 additions & 0 deletions apps/stats-web/src/config/server-env.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "@akashnetwork/env-loader";

import { validateRuntimeEnvVars } from "./env-config.schema";

export const serverEnvConfig = validateRuntimeEnvVars(process.env);
7 changes: 3 additions & 4 deletions apps/stats-web/src/hooks/useDenom.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useSelectedNetwork } from "./useSelectedNetwork";

import { USDC_IBC_DENOMS } from "@/config/denom.config";
import { networkStore } from "@/store/network.store";

export const useUsdcDenom = () => {
const selectedNetwork = useSelectedNetwork();
return USDC_IBC_DENOMS[selectedNetwork.id];
const selectedNetworkId = networkStore.useSelectedNetworkId();
return USDC_IBC_DENOMS[selectedNetworkId];
};
24 changes: 0 additions & 24 deletions apps/stats-web/src/hooks/useSelectedNetwork.ts

This file was deleted.

34 changes: 4 additions & 30 deletions apps/stats-web/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { MAINNET_ID, SANDBOX_ID, TESTNET_ID } from "@akashnetwork/network-store";
import { SANDBOX_ID, TESTNET_ID } from "@akashnetwork/network-store";

import { networkStore } from "@/store/network.store";

const productionMainnetApiUrl = "https://console-api.akash.network";
const productionTestnetApiUrl = "https://console-api-testnet.akash.network";
Expand Down Expand Up @@ -50,39 +52,11 @@ function getApiUrl() {
if (typeof window === "undefined") return "http://localhost:3080";
if (productionHostnames.includes(window.location?.hostname)) {
try {
const _selectedNetworkId = localStorage.getItem("selectedNetworkId");
return getNetworkBaseApiUrl(_selectedNetworkId);
return getNetworkBaseApiUrl(networkStore.selectedNetworkId);
} catch (e) {
console.error(e);
return productionMainnetApiUrl;
}
}
return "http://localhost:3080";
}

export let selectedNetworkId = "";
export let networkVersion: "v1beta2" | "v1beta3";

export function setNetworkVersion() {
const _selectedNetworkId = localStorage.getItem("selectedNetworkId");

switch (_selectedNetworkId) {
case MAINNET_ID:
networkVersion = "v1beta3";
selectedNetworkId = MAINNET_ID;
break;
case TESTNET_ID:
networkVersion = "v1beta3";
selectedNetworkId = TESTNET_ID;
break;
case SANDBOX_ID:
networkVersion = "v1beta3";
selectedNetworkId = SANDBOX_ID;
break;

default:
networkVersion = "v1beta3";
selectedNetworkId = MAINNET_ID;
break;
}
}
28 changes: 6 additions & 22 deletions apps/stats-web/src/lib/urlUtils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import { selectedNetworkId } from "./constants";

function getSelectedNetworkQueryParam() {
if (selectedNetworkId) {
return selectedNetworkId;
} else if (typeof window !== "undefined") {
return new URLSearchParams(window.location.search).get("network");
}

return undefined;
}
import { networkStore } from "@/store/network.store";

export class UrlService {
static home = () => "/";
static graph = (snapshot: string) => `/graph/${snapshot}`;
static providerGraph = (snapshot: string) => `/provider-graph/${snapshot}`;
static blocks = () => `/blocks`;
static block = (height: number) => `/blocks/${height}${appendSearchParams({ network: getSelectedNetworkQueryParam() as string })}`;
static block = (height: number) => `/blocks/${height}${appendSearchParams({ network: networkStore.selectedNetworkId })}`;
static transactions = () => `/transactions`;
static transaction = (hash: string) => `/transactions/${hash}${appendSearchParams({ network: getSelectedNetworkQueryParam() as string })}`;
static address = (address: string) => `/addresses/${address}${appendSearchParams({ network: getSelectedNetworkQueryParam() as string })}`;
static transaction = (hash: string) => `/transactions/${hash}${appendSearchParams({ network: networkStore.selectedNetworkId })}`;
static address = (address: string) => `/addresses/${address}${appendSearchParams({ network: networkStore.selectedNetworkId })}`;
static addressTransactions = (address: string) => `/addresses/${address}/transactions`;
static addressDeployments = (address: string) => `/addresses/${address}/deployments`;
static deployment = (owner: string, dseq: string) =>
`/addresses/${owner}/deployments/${dseq}${appendSearchParams({ network: getSelectedNetworkQueryParam() as string })}`;
`/addresses/${owner}/deployments/${dseq}${appendSearchParams({ network: networkStore.selectedNetworkId })}`;
static validators = () => "/validators";
static validator = (address: string) => `/validators/${address}${appendSearchParams({ network: getSelectedNetworkQueryParam() as string })}`;
static validator = (address: string) => `/validators/${address}${appendSearchParams({ network: networkStore.selectedNetworkId })}`;
static proposals = () => "/proposals";
static proposal = (id: number) => `/proposals/${id}`;
}
Expand Down Expand Up @@ -64,9 +54,3 @@ export function isValidHttpUrl(str: string): boolean {

return url.protocol === "http:" || url.protocol === "https:";
}

export function handleDocClick(ev: Event, url: string) {
ev.preventDefault();

window.open(url, "_blank");
}
3 changes: 3 additions & 0 deletions apps/stats-web/src/store/global.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createStore } from "jotai";

export const store = createStore();
10 changes: 10 additions & 0 deletions apps/stats-web/src/store/network.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NetworkStore } from "@akashnetwork/network-store";

import { browserEnvConfig } from "@/config/browser-env.config";
import { store } from "@/store/global.store";

export const networkStore = NetworkStore.create({
defaultNetworkId: browserEnvConfig.NEXT_PUBLIC_DEFAULT_NETWORK_ID,
apiBaseUrl: browserEnvConfig.NEXT_PUBLIC_API_BASE_URL,
store
});
67 changes: 0 additions & 67 deletions apps/stats-web/src/store/networkStore.ts

This file was deleted.

Loading

0 comments on commit 3d804d5

Please sign in to comment.