diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..df3623f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 03626a7..2253eab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,4 +12,9 @@ COPY . . RUN npm install -g pnpm && export NEXT_PUBLIC_IS_BASE=${NEXT_PUBLIC_IS_BASE} && pnpm build FROM nginx -COPY --from=builder /app/out /usr/share/nginx/html \ No newline at end of file +COPY --from=builder /app/out /usr/share/nginx/html +RUN apt-get update && apt-get install -y jq && apt-get clean +COPY ./nginx.conf.example /etc/nginx/nginx.conf +COPY ./start.sh /start.sh +RUN chmod +x /start.sh +CMD ["/bin/bash", "/start.sh"] \ No newline at end of file diff --git a/nginx.conf.example b/nginx.conf.example index b475b6a..54ab135 100644 --- a/nginx.conf.example +++ b/nginx.conf.example @@ -22,36 +22,17 @@ http { } location ~ /index/ { - proxy_pass http://Sepolia/; + proxy_pass http://meiliSearch:7700/; proxy_set_header Host $host; proxy_set_header X-Forward-For $remote_addr; proxy_set_header X-Forward-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; - proxy_set_header Authorization "Bearer $TOKEN"; - } - location ~ /indexMain/ { - proxy_pass http://main/; - proxy_set_header Host $host; - proxy_set_header X-Forward-For $remote_addr; - proxy_set_header X-Forward-Proto $scheme; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Authorization "Bearer $TOKEN"; + proxy_set_header Authorization "Bearer TOKEN"; } location ~ /api/ { - proxy_pass http://Sepolia/; - proxy_set_header Host $host; - proxy_set_header X-Forward-For $remote_addr; - proxy_set_header X-Forward-Proto $scheme; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - location ~ /apiMain/ { - proxy_pass http://main/; + proxy_pass http://node1:8080/; proxy_set_header Host $host; proxy_set_header X-Forward-For $remote_addr; proxy_set_header X-Forward-Proto $scheme; diff --git a/src/components/Cards/SurfaceCards/ClaimCard.tsx b/src/components/Cards/SurfaceCards/ClaimCard.tsx index 79ce675..d83423d 100644 --- a/src/components/Cards/SurfaceCards/ClaimCard.tsx +++ b/src/components/Cards/SurfaceCards/ClaimCard.tsx @@ -1,20 +1,22 @@ import { ClaimData } from "@/types"; -import React, { FC } from "react"; +import React, { FC, useContext } from "react"; import { SurfaceCardBase } from "./SurfaceCardBase"; import { Link } from "@/components/Link"; import { Skeleton } from "@/components/Skeleton"; import { CardField } from "../Card"; import { shortenAddress } from "@/utils"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; type ClaimCardProps = { claimData: ClaimData; }; -const ClaimCard: FC> = ({ claimData = {} }) => { +const ClaimCard: FC> = ({ + claimData = {} as ClaimData, +}) => { const { claim, claimant, output_block, event_id } = claimData; const { explorer_l1: EXPLORER_L1, explorer_l2: EXPLORER_L2 } = - useNetworkConfig(); + useContext(NetworkConfigContext); return (
diff --git a/src/components/Cards/SurfaceCards/CreditCard.tsx b/src/components/Cards/SurfaceCards/CreditCard.tsx index a42b2c3..c2223df 100644 --- a/src/components/Cards/SurfaceCards/CreditCard.tsx +++ b/src/components/Cards/SurfaceCards/CreditCard.tsx @@ -1,4 +1,4 @@ -import type { FC } from "react"; +import { useContext, type FC } from "react"; import { Skeleton } from "@/components/Skeleton"; import { Link } from "../../Link"; @@ -7,7 +7,7 @@ import { SurfaceCardBase } from "./SurfaceCardBase"; import { Credit } from "@/types"; import { shortenAddress } from "@/utils"; import { EtherUnitDisplay } from "@/components/Displays/EtherUnitDisplay"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; type CreditCardProps = { credit: Credit; @@ -18,7 +18,7 @@ const CreditCard: FC> = function ({ credit: { address, amount } = {}, index, }) { - const { explorer_l1: EXPLORER_L1 } = useNetworkConfig(); + const { explorer_l1: EXPLORER_L1 } = useContext(NetworkConfigContext); return (
diff --git a/src/components/Cards/SurfaceCards/EventCard.tsx b/src/components/Cards/SurfaceCards/EventCard.tsx index ddf738b..8940cdf 100644 --- a/src/components/Cards/SurfaceCards/EventCard.tsx +++ b/src/components/Cards/SurfaceCards/EventCard.tsx @@ -1,4 +1,4 @@ -import type { FC } from "react"; +import { useContext, type FC } from "react"; import dayjs from "@/utils/dayjs"; import { Skeleton } from "@/components/Skeleton"; @@ -6,7 +6,7 @@ import { Link } from "../../Link"; import { SurfaceCardBase } from "./SurfaceCardBase"; import { LatestEvents } from "@/types"; import { shortenAddress } from "@/utils"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; type EventCardProps = { events: LatestEvents; @@ -22,7 +22,7 @@ const EventCard: FC> = function ({ tx_hash, } = {}, }) { - const { explorer_l1: EXPLORER_L1 } = useNetworkConfig(); + const { explorer_l1: EXPLORER_L1 } = useContext(NetworkConfigContext); return ( diff --git a/src/components/Cards/SurfaceCards/GameCard.tsx b/src/components/Cards/SurfaceCards/GameCard.tsx index 3fbee1a..eed8f10 100644 --- a/src/components/Cards/SurfaceCards/GameCard.tsx +++ b/src/components/Cards/SurfaceCards/GameCard.tsx @@ -1,4 +1,4 @@ -import type { FC } from "react"; +import { useContext, type FC } from "react"; import dayjs from "@/utils/dayjs"; import { Skeleton } from "@/components/Skeleton"; @@ -10,7 +10,7 @@ import { shortenAddress } from "@/utils"; import shield from "@/icons/shield.png"; import sword from "@/icons/sword.png"; import Image from "next/image"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; type GameCardProps = { game: Game; @@ -26,7 +26,7 @@ const GameCard: FC> = function ({ } = {}, }) { const { explorer_l1: EXPLORER_L1, explorer_l2: EXPLORER_L2 } = - useNetworkConfig(); + useContext(NetworkConfigContext); return (
diff --git a/src/components/DisputeGameLogo.tsx b/src/components/DisputeGameLogo.tsx index 9ad48be..7d9232d 100644 --- a/src/components/DisputeGameLogo.tsx +++ b/src/components/DisputeGameLogo.tsx @@ -1,12 +1,18 @@ -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; import Link from "next/link"; +import { useContext } from "react"; - -const DisputeGameLogo: React.FC<{ className?: string }> = ({ }) => { - const { network } = useNetworkConfig() - return - logo - +const DisputeGameLogo: React.FC<{ className?: string }> = ({}) => { + const { network } = useContext(NetworkConfigContext); + return ( + + logo + + ); }; export default DisputeGameLogo; diff --git a/src/components/ExplorerDetails.tsx b/src/components/ExplorerDetails.tsx index b665f52..4780849 100644 --- a/src/components/ExplorerDetails.tsx +++ b/src/components/ExplorerDetails.tsx @@ -1,10 +1,10 @@ -import React, { useMemo } from "react"; +import React, { useContext, useMemo } from "react"; import "react-loading-skeleton/dist/skeleton.css"; import Skeleton from "react-loading-skeleton"; import { capitalize } from "@/utils"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; type ExplorerDetailsItemProps = { name: string; @@ -31,7 +31,7 @@ function ExplorerDetailsItem({ } export function ExplorerDetails() { - const { network } = useNetworkConfig(); + const { network } = useContext(NetworkConfigContext); const explorerDetailsItems: ExplorerDetailsItemProps[] = useMemo(() => { return [{ name: "Network", value: capitalize(network!) }]; }, [network]); diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx index 6ab6759..1a065a0 100644 --- a/src/components/Logo.tsx +++ b/src/components/Logo.tsx @@ -1,12 +1,16 @@ -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; -import { useTheme } from "next-themes"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; import Link from "next/link"; +import { useContext } from "react"; export const Logo: React.FC<{ className?: string }> = ({ className = "" }) => { - const { network } = useNetworkConfig() + const { network } = useContext(NetworkConfigContext); return ( - logo + logo ); -} +}; diff --git a/src/components/NetworkConfigContext.tsx b/src/components/NetworkConfigContext.tsx new file mode 100644 index 0000000..90daef8 --- /dev/null +++ b/src/components/NetworkConfigContext.tsx @@ -0,0 +1,42 @@ +import { NetworkConfig, networkConfigs, Network } from "@/utils/env"; +import { createContext, ReactNode, useEffect, useState } from "react"; + +const defaultNetwork = networkConfigs["sepolia"]; +export const NetworkConfigContext = + createContext(defaultNetwork); + +export default function NetworkConfigProvider({ + children, +}: { + children: ReactNode; +}) { + const [config, setConfig] = useState(defaultNetwork); + useEffect(() => { + const getChain = async () => { + try { + const res = await fetch("/api/disputegames/chainname"); + const data = await res.json(); + let network = data?.blockchain as string; + if (network) { + if (network.startsWith("eth-")) { + network = network.substring(4); + } + const fetchedConfig = networkConfigs[network as Network]; + if (fetchedConfig) { + setConfig(fetchedConfig); // 更新上下文状态 + } + } + } catch (error) { + console.error("Failed to fetch network:", error); + } + }; + + getChain(); + }, []); + + return ( + + {children} + + ); +} diff --git a/src/hooks/useAutoSwitchNetwork.ts b/src/hooks/useAutoSwitchNetwork.ts index 75c7edd..97f8989 100644 --- a/src/hooks/useAutoSwitchNetwork.ts +++ b/src/hooks/useAutoSwitchNetwork.ts @@ -1,20 +1,19 @@ -import { useAccount, useSwitchChain } from "wagmi" -import { useNetworkConfig } from "./useNetworkConfig" -import { useEffect } from "react" -import { mainnet, sepolia } from "viem/chains" - +import { useAccount, useSwitchChain } from "wagmi"; +import { useContext, useEffect } from "react"; +import { mainnet, sepolia } from "viem/chains"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; const useAutoSwitchNetwork = () => { - const { network } = useNetworkConfig() - const { switchChain } = useSwitchChain() - const { isConnected, chain } = useAccount() + const { network } = useContext(NetworkConfigContext); + const { switchChain } = useSwitchChain(); + const { isConnected, chain } = useAccount(); useEffect(() => { if (!isConnected || !chain) return; - const targetChainId = network === 'mainnet' ? mainnet.id : sepolia.id; + const targetChainId = network === "mainnet" ? mainnet.id : sepolia.id; if (chain.id !== targetChainId) { - switchChain({ chainId: targetChainId }) + switchChain({ chainId: targetChainId }); } - }, [isConnected, network, chain]) -} + }, [isConnected, network, chain]); +}; -export default useAutoSwitchNetwork \ No newline at end of file +export default useAutoSwitchNetwork; diff --git a/src/hooks/useEthersProvider.ts b/src/hooks/useEthersProvider.ts index 2819c49..029a9de 100644 --- a/src/hooks/useEthersProvider.ts +++ b/src/hooks/useEthersProvider.ts @@ -1,37 +1,37 @@ -import { type Config, getClient } from '@wagmi/core' -import { FallbackProvider, JsonRpcProvider } from 'ethers' -import type { Client, Chain, Transport } from 'viem' -import { wagmiConfig } from './useWagmiConfig' -import { useMemo } from 'react' -import { useNetworkConfig } from './useNetworkConfig' -import { mainnet, sepolia } from 'viem/chains' +import { type Config, getClient } from "@wagmi/core"; +import { FallbackProvider, JsonRpcProvider } from "ethers"; +import type { Client, Chain, Transport } from "viem"; +import { wagmiConfig } from "./useWagmiConfig"; +import { useContext, useMemo } from "react"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; +import { mainnet, sepolia } from "viem/chains"; export function clientToProvider(client: Client) { - const { chain, transport } = client + const { chain, transport } = client; const network = { chainId: chain.id, name: chain.name, ensAddress: chain.contracts?.ensRegistry?.address, - } - if (transport.type === 'fallback') { + }; + if (transport.type === "fallback") { const providers = (transport.transports as ReturnType[]).map( - ({ value }) => new JsonRpcProvider(value?.url, network), - ) - if (providers.length === 1) return providers[0] - return new FallbackProvider(providers) + ({ value }) => new JsonRpcProvider(value?.url, network) + ); + if (providers.length === 1) return providers[0]; + return new FallbackProvider(providers); } - return new JsonRpcProvider(transport.url, network) + return new JsonRpcProvider(transport.url, network); } /** Action to convert a viem Client to an ethers.js Provider. */ export function useEthersProvider() { - const { network } = useNetworkConfig() + const { network } = useContext(NetworkConfigContext); const chainId = useMemo(() => { - return network === 'mainnet' ? mainnet.id : sepolia.id - }, [network]) - const client = useMemo(() => getClient(wagmiConfig, { chainId }), [chainId]) + return network === "mainnet" ? mainnet.id : sepolia.id; + }, [network]); + const client = useMemo(() => getClient(wagmiConfig, { chainId }), [chainId]); return useMemo(() => { - if (!client) return - return clientToProvider(client) - }, [client]) -} \ No newline at end of file + if (!client) return; + return clientToProvider(client); + }, [client]); +} diff --git a/src/hooks/useEthersSigner.tsx b/src/hooks/useEthersSigner.tsx index 4789f94..289df91 100644 --- a/src/hooks/useEthersSigner.tsx +++ b/src/hooks/useEthersSigner.tsx @@ -1,48 +1,48 @@ -import { Config, getConnectorClient } from '@wagmi/core' -import { BrowserProvider, JsonRpcSigner } from 'ethers' -import { useEffect, useMemo, useState } from 'react' -import type { Account, Chain, Client, Transport } from 'viem' -import { Connector, useAccount } from 'wagmi' -import { wagmiConfig } from './useWagmiConfig' -import { useNetworkConfig } from './useNetworkConfig' -import { mainnet, sepolia } from 'viem/chains' +import { Config, getConnectorClient } from "@wagmi/core"; +import { BrowserProvider, JsonRpcSigner } from "ethers"; +import { useContext, useEffect, useMemo, useState } from "react"; +import type { Account, Chain, Client, Transport } from "viem"; +import { Connector, useAccount } from "wagmi"; +import { wagmiConfig } from "./useWagmiConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; +import { mainnet, sepolia } from "viem/chains"; export function clientToSigner(client: Client) { - const { account, chain, transport } = client + const { account, chain, transport } = client; const network = { chainId: chain.id, name: chain.name, ensAddress: chain.contracts?.ensRegistry?.address, - } - const provider = new BrowserProvider(transport, network) - const signer = new JsonRpcSigner(provider, account.address) - return signer + }; + const provider = new BrowserProvider(transport, network); + const signer = new JsonRpcSigner(provider, account.address); + return signer; } /** Action to convert a viem Wallet Client to an ethers.js Signer. */ export async function getEthersSigner( config: Config, - { chainId, connector }: { chainId?: number, connector?: Connector } = {}, + { chainId, connector }: { chainId?: number; connector?: Connector } = {} ) { - const client = await getConnectorClient(config, { chainId, connector }) - return clientToSigner(client) + const client = await getConnectorClient(config, { chainId, connector }); + return clientToSigner(client); } export const useEthersSigner = () => { - const [signer, setSigner] = useState() - const { network } = useNetworkConfig() + const [signer, setSigner] = useState(); + const { network } = useContext(NetworkConfigContext); const chainId = useMemo(() => { - return network === 'mainnet' ? mainnet.id : sepolia.id - }, [network]) - const { isConnected, connector } = useAccount() + return network === "mainnet" ? mainnet.id : sepolia.id; + }, [network]); + const { isConnected, connector } = useAccount(); useEffect(() => { const getSigner = async () => { - const p = await getEthersSigner(wagmiConfig, { chainId, connector }) - setSigner(p) - } + const p = await getEthersSigner(wagmiConfig, { chainId, connector }); + setSigner(p); + }; if (isConnected && connector) { - getSigner() + getSigner(); } - }, [isConnected]) - return signer -} \ No newline at end of file + }, [isConnected]); + return signer; +}; diff --git a/src/hooks/useNetworkConfig.ts b/src/hooks/useNetworkConfig.ts deleted file mode 100644 index 0fef846..0000000 --- a/src/hooks/useNetworkConfig.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NetworkConfig, networkConfigs, Network } from "@/utils/env"; -import { useMemo } from "react"; - -export const useNetworkConfig = (): NetworkConfig => { - const host = window?.location?.host; - const defaultNetwork = networkConfigs["mainnet"]; - defaultNetwork.network = "mainnet"; - const config = useMemo(() => { - for (const key of Object.keys(networkConfigs)) { - const val = networkConfigs[key as Network]; - if (val && val.origin === host) { - val.network = key as Network; - return val; - } - } - }, [origin]); - return config || defaultNetwork; -}; diff --git a/src/hooks/useWagmiConfig.ts b/src/hooks/useWagmiConfig.ts index 1dd7ff8..38a3c7d 100644 --- a/src/hooks/useWagmiConfig.ts +++ b/src/hooks/useWagmiConfig.ts @@ -1,22 +1,18 @@ -import { useMemo } from "react"; -import { useNetworkConfig } from "./useNetworkConfig"; import { getDefaultConfig } from "@rainbow-me/rainbowkit"; -import { mainnet, sepolia } from '@wagmi/core/chains' -import { http, createConfig } from '@wagmi/core' - +import { mainnet, sepolia } from "@wagmi/core/chains"; +import { http, createConfig } from "@wagmi/core"; export const RainbowConfig = getDefaultConfig({ - appName: 'SuperProof explorer', - projectId: '876a28d2d23153fe7f76c24bacbabb72', + appName: "SuperProof explorer", + projectId: "876a28d2d23153fe7f76c24bacbabb72", chains: [mainnet, sepolia], // ssr: true -}) - +}); export const wagmiConfig = createConfig({ chains: [sepolia, mainnet], transports: { [sepolia.id]: http(), - [mainnet.id]: http() + [mainnet.id]: http(), }, -}) +}); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5af76bc..0852878 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,7 +1,7 @@ import "../styles/global.css"; import "@upstash/feedback/index.css"; -import { ToastContainer } from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; +import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; import type { AppProps as NextAppProps } from "next/app"; import { ThemeProvider, useTheme } from "next-themes"; import Head from "next/head"; @@ -10,6 +10,7 @@ import AppLayout from "@/components/AppLayout/AppLayout"; import { useIsMounted } from "@/hooks/useIsMounted"; import Script from "next/script"; import WalletProvider from "@/components/Wallet/Provider"; +import NetworkConfigProvider from "@/components/NetworkConfigContext"; const App = ({ Component, pageProps }: NextAppProps) => { const { resolvedTheme } = useTheme(); @@ -44,7 +45,9 @@ const App = ({ Component, pageProps }: NextAppProps) => { - + + + {/* diff --git a/src/pages/game/[game].tsx b/src/pages/game/[game].tsx index 6880a39..22c6341 100644 --- a/src/pages/game/[game].tsx +++ b/src/pages/game/[game].tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import ClaimChart from "@/components/Charts/ClaimChart"; import { useClaimData } from "@/hooks/useClaimData"; import { ChartSkeleton } from "@/components/ChartSkeleton"; @@ -14,7 +14,7 @@ import { ClockIcon, FlagIcon } from "@heroicons/react/24/outline"; import { Card } from "@/components/Cards/Card"; import ClaimCard from "@/components/Cards/SurfaceCards/ClaimCard"; import { SlidableList } from "@/components/SlidableList"; -import { useNetworkConfig } from "@/hooks/useNetworkConfig"; +import { NetworkConfigContext } from "@/components/NetworkConfigContext"; import { useEthersProvider } from "@/hooks/useEthersProvider"; import { getChallengeContract } from "@/service/contract"; @@ -27,21 +27,21 @@ const GameDetail = () => { q: address, }); const [resolved, setResolved] = useState(false); - const provider = useEthersProvider() + const provider = useEthersProvider(); const { explorer_l1: EXPLORER_L1, explorer_l2: EXPLORER_L2 } = - useNetworkConfig(); + useContext(NetworkConfigContext); useEffect(() => { const getResolveTime = async () => { if (provider && address) { - const contract = getChallengeContract(address, provider) - const res = await contract.resolvedAt() - console.log({ res }) - setResolved(res > 0) + const contract = getChallengeContract(address, provider); + const res = await contract.resolvedAt(); + console.log({ res }); + setResolved(res > 0); } - } - getResolveTime() - }, [provider, address]) + }; + getResolveTime(); + }, [provider, address]); return (
@@ -106,7 +106,11 @@ const GameDetail = () => {
) : ( - + )} = { @@ -16,15 +16,18 @@ export const networkConfigs: Record = { origin: process.env.NEXT_PUBLIC_OP_MAINNET_URL || "", explorer_l1: "https://etherscan.io", explorer_l2: "https://optimistic.etherscan.io/", + network: "mainnet", }, sepolia: { origin: process.env.NEXT_PUBLIC_OP_SEPOLIA_URL || "", explorer_l1: "https://sepolia.etherscan.io", explorer_l2: "https://sepolia-optimism.etherscan.io", + network: "sepolia", }, "base-sepolia": { origin: process.env.NEXT_PUBLIC_BASE_SEPOLIA_URL || "", explorer_l1: "https://sepolia.etherscan.io", explorer_l2: "https://sepolia.basescan.org", + network: "base-sepolia", }, }; diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..bd1207a --- /dev/null +++ b/start.sh @@ -0,0 +1,10 @@ +#!/bin/bash + + +curl_response=$(curl -s -H "Authorization: Bearer $ADMIN_KEY" http://meiliSearch:7700/keys) +API_KEY=$(echo "$curl_response" | jq -r '.results[] | select(.actions[] | contains("*")) | .key') +NGINX_CONF="/etc/nginx/nginx.conf" + +sed -i "s/KEY/$API_KEY/g" $NGINX_CONF + +nginx \ No newline at end of file