Skip to content

Commit

Permalink
feat(env): implement unified file loading in console-web
Browse files Browse the repository at this point in the history
refs #313
  • Loading branch information
ygrishajev committed Sep 5, 2024
1 parent 1fec106 commit 20ebd93
Show file tree
Hide file tree
Showing 103 changed files with 762 additions and 551 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
**/.env.*.local
**/.env.*.test

apps/deploy-web/.env*
apps/indexer/.env*
apps/landing/.env*
apps/provider-console/.env*
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ build.ps1
.yarn-integrity

# dotenv environment variable files
apps/deploy-web/.env
apps/indexer/.env
apps/provider-console/.env
apps/provider-proxy/.env
Expand Down
2 changes: 2 additions & 0 deletions apps/deploy-web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ sentry.properties
/playwright-report/
/blob-report/
/playwright/.cache/

env-config.schema.js
48 changes: 30 additions & 18 deletions apps/deploy-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,33 @@ The website should be accessible: [http://localhost:3000/](http://localhost:3000

## Environment Variables

When running the api locally the following environment variables can be set in a `.env.local` file.

It is possible to run the website locally without any environment variables, but the login feature will be unavailable.

|Name|Value|Note|
|-|-|-
|NEXT_PUBLIC_GA_MEASUREMENT_ID|ex: `G-87H3KK3D`|Google Analytics ID
|NEXT_PUBLIC_SENTRY_DSN|ex: `"https://[email protected]/1234"`|[Sentry DSN](https://docs.sentry.io/product/sentry-basics/dsn-explainer/) used when initializing Sentry in [sentry.client.config.js](./sentry.client.config.js) and [sentry.server.config.js](./sentry.server.config.js)
|AUTH0_SECRET||
|AUTH0_BASE_URL||
|AUTH0_ISSUER_BASE_URL||
|AUTH0_CLIENT_ID||
|AUTH0_CLIENT_SECRET||
|AUTH0_AUDIENCE||
|AUTH0_SCOPE||
|AUTH0_M2M_DOMAIN||
|AUTH0_M2M_CLIENT_ID||
|AUTH0_M2M_CLIENT_SECRET||
### Overview
Environment variables in this Next.js app follow the standard Next.js behavior, as documented in the [Next.js environment variables documentation](https://nextjs.org/docs/basic-features/environment-variables). This means that files like `.env.local` or `.env.production` will be automatically loaded based on the environment in which the app is running.

However, we have extended this functionality to support more granular environment-specific configurations. Environment variables are stored in the `./env` directory, where multiple `.env` files exist for different deployment environments (stages):

- `.env` - Loaded for any environment
- `.env.production` - Loaded for the production stage
- `.env.staging` - Loaded for the staging stage

### How Environment Variables Are Loaded
We use **dotenvx** to manage and load environment variables. This allows us to take advantage of its features, such as **variable interpolation** (i.e., using other environment variables within variable values).

### Validation with Zod
Environment variables are validated using **Zod** schemas, ensuring that all required variables are present and have valid values. The validation logic can be found in the file `src/config/env-config.schema.ts`.

We use two separate Zod schemas:
- **Static Build-Time Schema**: Validates variables at build time. If any variables are missing or invalid during the build process, the build will fail.
- **Dynamic Server Runtime Schema**: Validates variables at server startup. If any variables are missing or invalid at this stage, the server will fail to start.

This validation ensures that both build and runtime configurations are secure and complete before the app runs.

### App Configuration
App configurations, including environment variables, are located in the `src/config` directory. In our setup:
- **Environment configs** are handled separately from **hardcoded configs**.
- Hardcoded configs are organized by domain to maintain a clear structure and separation of concerns.

### Sample Environment Variables
All environment variables required for the app, along with their expected structure and types, can be found in the `env/.env.sample` file. This sample file serves as a template for setting up your environment variables and ensures that all necessary variables are accounted for in each environment.

By organizing environment variables and configuration this way, we ensure a consistent, safe, and scalable approach to managing different deployment environments.
26 changes: 0 additions & 26 deletions apps/deploy-web/env.config.js

This file was deleted.

2 changes: 2 additions & 0 deletions apps/deploy-web/env/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
AUTH0_AUDIENCE=https://api.cloudmos.io
AUTH0_SCOPE=openid profile email offline_access
17 changes: 17 additions & 0 deletions apps/deploy-web/env/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
AUTH0_BASE_URL=https://console.akash.network
AUTH0_ISSUER_BASE_URL=https://auth.cloudmos.io

NEXT_PUBLIC_BILLING_ENABLED=false
NEXT_PUBLIC_MANAGED_WALLET_NETWORK_ID=mainnet
NEXT_PUBLIC_MANAGED_WALLET_DENOM=usdc
# TODO: replace with a fresh master wallet before release
NEXT_PUBLIC_MASTER_WALLET_ADDRESS=akash1ss0d2yw38r6e7ew8ndye9h7kg62sem36zak4d5

NEXT_PUBLIC_STATS_APP_URL=https://stats.akash.network
NEXT_PUBLIC_PROVIDER_PROXY_URL=https://providerproxy.cloudmos.io
NEXT_PUBLIC_PROVIDER_PROXY_URL_WS=wss://providerproxy.cloudmos.io
NEXT_PUBLIC_NODE_ENV=$NODE_ENV
NEXT_PUBLIC_BASE_API_MAINNET_URL=https://api.cloudmos.io
NEXT_PUBLIC_BASE_API_SANDBOX_URL=https://api-sandbox.cloudmos.io
NEXT_PUBLIC_BASE_API_TESTNET_URL=https://api-testnet.cloudmos.io
NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL
22 changes: 22 additions & 0 deletions apps/deploy-web/env/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DEPLOYMENT_ENV

AUTH0_BASE_URL=
AUTH0_ISSUER_BASE_URL=
AUTH0_SECRET
AUTH0_CLIENT_ID
AUTH0_CLIENT_SECRET

NEXT_PUBLIC_BILLING_ENABLED=
NEXT_PUBLIC_MANAGED_WALLET_NETWORK_ID=
NEXT_PUBLIC_MANAGED_WALLET_DENOM=
NEXT_PUBLIC_MASTER_WALLET_ADDRESS=
NEXT_PUBLIC_STATS_APP_URL=
NEXT_PUBLIC_PROVIDER_PROXY_URL=
NEXT_PUBLIC_PROVIDER_PROXY_URL_WS=
NEXT_PUBLIC_NODE_ENV=
NEXT_PUBLIC_BASE_API_MAINNET_URL=
NEXT_PUBLIC_BASE_API_SANDBOX_URL=
NEXT_PUBLIC_BASE_API_TESTNET_URL=
NEXT_PUBLIC_API_BASE_URL=
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_SERVER_NAME
16 changes: 16 additions & 0 deletions apps/deploy-web/env/.env.staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
AUTH0_BASE_URL=https://console-beta.akash.network
AUTH0_ISSUER_BASE_URL=https://dev-5aprb0lr.us.auth0.com

NEXT_PUBLIC_BILLING_ENABLED=true
NEXT_PUBLIC_MANAGED_WALLET_NETWORK_ID=mainnet
NEXT_PUBLIC_MANAGED_WALLET_DENOM=usdc
NEXT_PUBLIC_MASTER_WALLET_ADDRESS=akash1ss0d2yw38r6e7ew8ndye9h7kg62sem36zak4d5

NEXT_PUBLIC_STATS_APP_URL=https://stats.akash.network
NEXT_PUBLIC_PROVIDER_PROXY_URL=https://providerproxy.cloudmos.io
NEXT_PUBLIC_PROVIDER_PROXY_URL_WS=wss://providerproxy.cloudmos.io
NEXT_PUBLIC_NODE_ENV=$NODE_ENV
NEXT_PUBLIC_BASE_API_MAINNET_URL=https://api-mainnet-staging.cloudmos.io
NEXT_PUBLIC_BASE_API_SANDBOX_URL=https://api-sandbox-staging.cloudmos.io
NEXT_PUBLIC_BASE_API_TESTNET_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL
NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL
19 changes: 14 additions & 5 deletions apps/deploy-web/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require("./env.config");
require("@akashnetwork/env-loader");
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true"
});
Expand All @@ -10,6 +10,16 @@ const withPWA = require("next-pwa")({
});
const { withSentryConfig } = require("@sentry/nextjs");

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}
*/
Expand All @@ -30,10 +40,9 @@ const moduleExports = {
ignoreDuringBuilds: true
},
transpilePackages: ["geist", "@akashnetwork/ui"],
// experimental: {
// // outputStandalone: true,
// externalDir: true // to make the import from shared parent folder work https://github.com/vercel/next.js/issues/9474#issuecomment-810212174
// },
experimental: {
instrumentationHook: true
},
publicRuntimeConfig: {
version
},
Expand Down
5 changes: 3 additions & 2 deletions apps/deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"license": "Apache-2.0",
"author": "Akash Network",
"scripts": {
"build": "next build",
"build-analyze": "set ANALYZE=true&& next build",
"build": "npm run build-env-schemas && next build",
"build-env-schemas": "tsc src/config/env-config.schema.ts --outDir . --skipLibCheck",
"dev": "next",
"format": "prettier --write ./*.{ts,js,json} **/*.{ts,tsx,js,json}",
"lint": "eslint .",
Expand All @@ -17,6 +17,7 @@
"dependencies": {
"@akashnetwork/akash-api": "^1.3.0",
"@akashnetwork/akashjs": "^0.10.0",
"@akashnetwork/env-loader": "*",
"@akashnetwork/http-sdk": "*",
"@akashnetwork/ui": "*",
"@auth0/nextjs-auth0": "^3.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { event } from "nextjs-google-analytics";
import { z } from "zod";

import { LinkTo } from "@src/components/shared/LinkTo";
import { UAKT_DENOM } from "@src/config/denom.config";
import { useWallet } from "@src/context/WalletProvider";
import { useDenomData } from "@src/hooks/useWalletBalance";
import { AllowanceType } from "@src/types/grant";
import { AnalyticsEvents } from "@src/utils/analytics";
import { uAktDenom } from "@src/utils/constants";
import { aktToUakt, coinToDenom } from "@src/utils/priceUtils";
import { TransactionMessageData } from "@src/utils/TransactionMessageData";

Expand Down Expand Up @@ -46,7 +46,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
});
const { handleSubmit, control, watch, clearErrors, setValue } = form;
const { amount, granteeAddress, expiration } = watch();
const denomData = useDenomData(uAktDenom);
const denomData = useDenomData(UAKT_DENOM);

const onDepositClick = event => {
event.preventDefault();
Expand All @@ -64,7 +64,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
if (editingAllowance) {
messages.push(TransactionMessageData.getRevokeAllowanceMsg(address, granteeAddress));
}
messages.push(TransactionMessageData.getGrantBasicAllowanceMsg(address, granteeAddress, spendLimit, uAktDenom, expirationDate));
messages.push(TransactionMessageData.getGrantBasicAllowanceMsg(address, granteeAddress, spendLimit, UAKT_DENOM, expirationDate));
const response = await signAndBroadcastTx(messages);

if (response) {
Expand Down Expand Up @@ -144,7 +144,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
min={0}
step={0.000001}
max={denomData?.inputMax}
startIcon={<span className="text-xs pl-2">{denomData?.label}</span>}
startIcon={<span className="pl-2 text-xs">{denomData?.label}</span>}
/>
);
}}
Expand Down
6 changes: 3 additions & 3 deletions apps/deploy-web/src/components/authorizations/GrantModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import { event } from "nextjs-google-analytics";
import { z } from "zod";

import { LinkTo } from "@src/components/shared/LinkTo";
import { UAKT_DENOM } from "@src/config/denom.config";
import { useWallet } from "@src/context/WalletProvider";
import { getUsdcDenom, useUsdcDenom } from "@src/hooks/useDenom";
import { useDenomData } from "@src/hooks/useWalletBalance";
import { GrantType } from "@src/types/grant";
import { AnalyticsEvents } from "@src/utils/analytics";
import { uAktDenom } from "@src/utils/constants";
import { denomToUdenom } from "@src/utils/mathHelpers";
import { aktToUakt, coinToDenom } from "@src/utils/priceUtils";
import { TransactionMessageData } from "@src/utils/TransactionMessageData";
Expand Down Expand Up @@ -69,7 +69,7 @@ export const GrantModal: React.FunctionComponent<Props> = ({ editingGrant, addre
const { handleSubmit, control, watch, clearErrors, setValue } = form;
const { amount, granteeAddress, expiration, token } = watch();
const selectedToken = supportedTokens.find(x => x.id === token);
const denom = token === "akt" ? uAktDenom : usdcDenom;
const denom = token === "akt" ? UAKT_DENOM : usdcDenom;
const denomData = useDenomData(denom);

const onDepositClick = event => {
Expand All @@ -82,7 +82,7 @@ export const GrantModal: React.FunctionComponent<Props> = ({ editingGrant, addre
clearErrors();
const spendLimit = token === "akt" ? aktToUakt(amount) : denomToUdenom(amount);
const usdcDenom = getUsdcDenom();
const denom = token === "akt" ? uAktDenom : usdcDenom;
const denom = token === "akt" ? UAKT_DENOM : usdcDenom;

const expirationDate = new Date(expiration);
const message = TransactionMessageData.getGrantMsg(address, granteeAddress, spendLimit, expirationDate, denom);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import { event } from "nextjs-google-analytics";
import { useSnackbar } from "notistack";
import { z } from "zod";

import { UAKT_DENOM } from "@src/config/denom.config";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useUsdcDenom } from "@src/hooks/useDenom";
import { useDenomData } from "@src/hooks/useWalletBalance";
import { useGranteeGrants } from "@src/queries/useGrantsQuery";
import { AnalyticsEvents } from "@src/utils/analytics";
import { uAktDenom } from "@src/utils/constants";
import { denomToUdenom, udenomToDenom } from "@src/utils/mathHelpers";
import { coinToUDenom, uaktToAKT } from "@src/utils/priceUtils";
import { LinkTo } from "../shared/LinkTo";
Expand Down Expand Up @@ -185,7 +185,7 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC
category: "deployments",
label: "Use depositor to deposit in deployment"
});
} else if (denom === uAktDenom && deposit > uaktBalance) {
} else if (denom === UAKT_DENOM && deposit > uaktBalance) {
setError(`You can't deposit more than you currently have in your balance. Current balance is: ${uaktToAKT(uaktBalance)} AKT.`);
return;
} else if (denom === usdcIbcDenom && deposit > usdcBalance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { useWallet } from "@src/context/WalletProvider";
import { useDeploymentDetail } from "@src/queries/useDeploymentQuery";
import { useDeploymentLeaseList } from "@src/queries/useLeaseQuery";
import { useProviderList } from "@src/queries/useProvidersQuery";
import { RouteStep } from "@src/types/route-steps.type";
import { AnalyticsEvents } from "@src/utils/analytics";
import { RouteStepKeys } from "@src/utils/constants";
import { getDeploymentLocalData } from "@src/utils/deploymentLocalDataUtils";
import { cn } from "@src/utils/styleUtils";
import { UrlService } from "@src/utils/urlUtils";
Expand Down Expand Up @@ -63,7 +63,7 @@ export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: strin
if (_leases) {
// Redirect to select bids if has no lease
if (deployment?.state === "active" && _leases.length === 0) {
router.replace(UrlService.newDeployment({ dseq, step: RouteStepKeys.createLeases }));
router.replace(UrlService.newDeployment({ dseq, step: RouteStep.createLeases }));
}

// Set the array of refs for lease rows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { OpenInWindow, OpenNewWindow } from "iconoir-react";
import Link from "next/link";

import ViewPanel from "@src/components/shared/ViewPanel";
import { browserEnvConfig } from "@src/config/browser-env.config";
import { useCertificate } from "@src/context/CertificateProvider";
import { useCustomWebSocket } from "@src/hooks/useCustomWebSocket";
import { XTerm } from "@src/lib/XTerm";
Expand All @@ -13,7 +14,6 @@ import { useLeaseStatus } from "@src/queries/useLeaseQuery";
import { useProviderList } from "@src/queries/useProvidersQuery";
import { LeaseDto } from "@src/types/deployment";
import { LeaseShellCode } from "@src/types/shell";
import { PROVIDER_PROXY_URL_WS } from "@src/utils/constants";
import { cn } from "@src/utils/styleUtils";
import { UrlService } from "@src/utils/urlUtils";
import { LeaseSelect } from "./LeaseSelect";
Expand Down Expand Up @@ -47,7 +47,7 @@ export const DeploymentLeaseShell: React.FunctionComponent<Props> = ({ leases })
});
const currentUrl = useRef<string | null>(null);
const terminalRef = useRef<XTermRefType>(null);
const { sendJsonMessage } = useCustomWebSocket(PROVIDER_PROXY_URL_WS, {
const { sendJsonMessage } = useCustomWebSocket(browserEnvConfig.NEXT_PUBLIC_PROVIDER_PROXY_URL_WS, {
onOpen: () => {
console.log("opened");
},
Expand Down
4 changes: 2 additions & 2 deletions apps/deploy-web/src/components/deployments/DeploymentLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import { LinearLoadingSkeleton } from "@src/components/shared/LinearLoadingSkele
import { MemoMonaco } from "@src/components/shared/MemoMonaco";
import { SelectCheckbox } from "@src/components/shared/SelectCheckbox";
import ViewPanel from "@src/components/shared/ViewPanel";
import { browserEnvConfig } from "@src/config/browser-env.config";
import { useBackgroundTask } from "@src/context/BackgroundTaskProvider";
import { useCertificate } from "@src/context/CertificateProvider";
import { useThrottledCallback } from "@src/hooks/useThrottle";
import { useLeaseStatus } from "@src/queries/useLeaseQuery";
import { useProviderList } from "@src/queries/useProvidersQuery";
import { LeaseDto } from "@src/types/deployment";
import { AnalyticsEvents } from "@src/utils/analytics";
import { PROVIDER_PROXY_URL_WS } from "@src/utils/constants";
import { cn } from "@src/utils/styleUtils";
import { LeaseSelect } from "./LeaseSelect";

Expand Down Expand Up @@ -57,7 +57,7 @@ export const DeploymentLogs: React.FunctionComponent<Props> = ({ leases, selecte
} = useLeaseStatus(providerInfo?.hostUri || "", selectedLease as LeaseDto, {
enabled: false
});
const { sendJsonMessage } = useWebSocket(PROVIDER_PROXY_URL_WS, {
const { sendJsonMessage } = useWebSocket(browserEnvConfig.NEXT_PUBLIC_PROVIDER_PROXY_URL_WS, {
onOpen: () => {},
onMessage: onLogReceived,
onError: error => console.error("error", error),
Expand Down
Loading

0 comments on commit 20ebd93

Please sign in to comment.