diff --git a/.gitignore b/.gitignore index 69dd198b0..cbe8657ca 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ tsconfig.tsbuildinfo __snapshots__ storybook-static .qodo -next-env.d.ts \ No newline at end of file +next-env.d.ts +public/runtime-config.js diff --git a/Dockerfile b/Dockerfile index 905c28cf8..37960a092 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ ENV NODE_ENV=production RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/public ./public RUN mkdir .next RUN chown nextjs:nodejs .next @@ -35,10 +35,14 @@ RUN chown nextjs:nodejs .next COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --chown=nextjs:nodejs ./scripts/docker-entrypoint.sh /app/docker-entrypoint.sh +RUN chmod +x /app/docker-entrypoint.sh + USER nextjs EXPOSE 8008 ENV PORT=8008 ENV HOSTNAME="0.0.0.0" +ENTRYPOINT ["/app/docker-entrypoint.sh"] CMD ["node", "server.js"] diff --git a/app.config.cjs b/app.config.cjs index 327d96379..8d1e42560 100644 --- a/app.config.cjs +++ b/app.config.cjs @@ -1,3 +1,12 @@ +const getEnv = (key) => { + const hasWindow = typeof window !== 'undefined' + if (hasWindow && window.__RUNTIME_CONFIG__) { + const value = window.__RUNTIME_CONFIG__[key] + if (typeof value !== 'undefined') return value + } + return process.env[key] +} + module.exports = { // URI of single metadata cache instance for all networks. // While ocean.js includes this value for each network as part of its ConfigHelper, @@ -6,12 +15,14 @@ module.exports = { // const { appConfig } = useMarketMetadata() // return appConfig.metadataCacheUri metadataCacheUri: + getEnv('NEXT_PUBLIC_METADATACACHE_URI') || process.env.NEXT_PUBLIC_METADATACACHE_URI || 'https://ocean-node-vm3.oceanenterprise.io', nodeUri: + getEnv('NEXT_PUBLIC_NODE_URI') || process.env.NEXT_PUBLIC_NODE_URI || - 'https://eth-sepolia.blastapi.io/a91cc615-dbae-493b-b011-5796477a64de', + 'https://eth-sepolia-testnet.api.pocket.network', // List of chainIds which metadata cache queries will return by default. // This preselects the Chains user preferences. @@ -20,31 +31,47 @@ module.exports = { // List of all supported chainIds. Used to populate the Chains user preferences list. chainIdsSupported: [1, 10, 11155111, 11155420], - customProviderUrl: process.env.NEXT_PUBLIC_PROVIDER_URL, + customProviderUrl: + getEnv('NEXT_PUBLIC_PROVIDER_URL') || process.env.NEXT_PUBLIC_PROVIDER_URL, defaultDatatokenCap: '115792089237316195423570985008687907853269984665640564039457', defaultDatatokenTemplateIndex: 2, // The ETH address the marketplace fee will be sent to. marketFeeAddress: + getEnv('NEXT_PUBLIC_MARKET_FEE_ADDRESS') || process.env.NEXT_PUBLIC_MARKET_FEE_ADDRESS || - '0x9984b2453eC7D99a73A5B3a46Da81f197B753C8d', + '0x43eB6644720CFD8B176DC971C6e8c17331812c04', // publisher market fee that is taken upon ordering an asset, it is an absolute value, it is declared on erc20 creation publisherMarketOrderFee: - process.env.NEXT_PUBLIC_PUBLISHER_MARKET_ORDER_FEE || '0', + getEnv('NEXT_PUBLIC_PUBLISHER_MARKET_ORDER_FEE') || + process.env.NEXT_PUBLIC_PUBLISHER_MARKET_ORDER_FEE || + '0', // fee recieved by the publisher market when a dt is bought from a fixed rate exchange, percent publisherMarketFixedSwapFee: - process.env.NEXT_PUBLIC_PUBLISHER_MARKET_FIXED_SWAP_FEE || '0', + getEnv('NEXT_PUBLIC_PUBLISHER_MARKET_FIXED_SWAP_FEE') || + process.env.NEXT_PUBLIC_PUBLISHER_MARKET_FIXED_SWAP_FEE || + '0', // consume market fee that is taken upon ordering an asset, it is an absolute value with 18 decimals, it is specified on order consumeMarketOrderFee: - process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE || '0', - consumeMarketFee: process.env.NEXT_PUBLIC_CONSUME_MARKET_FEE || '0', + getEnv('NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE') || + process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE || + '0', + consumeMarketFee: + getEnv('NEXT_PUBLIC_CONSUME_MARKET_FEE') || + process.env.NEXT_PUBLIC_CONSUME_MARKET_FEE || + '0', // fee recieved by the consume market when a dt is bought from a fixed rate exchange, percent consumeMarketFixedSwapFee: - process.env.NEXT_PUBLIC_CONSUME_MARKET_FIXED_SWAP_FEE || '0', + getEnv('NEXT_PUBLIC_CONSUME_MARKET_FIXED_SWAP_FEE') || + process.env.NEXT_PUBLIC_CONSUME_MARKET_FIXED_SWAP_FEE || + '0', - marketCommunityFee: process.env.NEXT_PUBLIC_MARKET_COMMUNITY_FEE || '0.3', + marketCommunityFee: + getEnv('NEXT_PUBLIC_MARKET_COMMUNITY_FEE') || + process.env.NEXT_PUBLIC_MARKET_COMMUNITY_FEE || + '0', // Config for https://github.com/oceanprotocol/use-dark-mode darkModeConfig: { @@ -55,9 +82,18 @@ module.exports = { // Used to show or hide the fixed, dynamic or free price options // tab to publishers during the price creation. - allowFixedPricing: process.env.NEXT_PUBLIC_ALLOW_FIXED_PRICING || 'true', - allowDynamicPricing: process.env.NEXT_PUBLIC_ALLOW_DYNAMIC_PRICING || 'false', - allowFreePricing: process.env.NEXT_PUBLIC_ALLOW_FREE_PRICING || 'true', + allowFixedPricing: + getEnv('NEXT_PUBLIC_ALLOW_FIXED_PRICING') || + process.env.NEXT_PUBLIC_ALLOW_FIXED_PRICING || + 'true', + allowDynamicPricing: + getEnv('NEXT_PUBLIC_ALLOW_DYNAMIC_PRICING') || + process.env.NEXT_PUBLIC_ALLOW_DYNAMIC_PRICING || + 'false', + allowFreePricing: + getEnv('NEXT_PUBLIC_ALLOW_FREE_PRICING') || + process.env.NEXT_PUBLIC_ALLOW_FREE_PRICING || + 'true', // Set the default privacy policy to initially display // this should be the slug of your default policy markdown file @@ -69,50 +105,79 @@ module.exports = { // is used to create and show a privacy preference center / cookie banner // To learn more about how to configure and use this, please refer to the readme privacyPreferenceCenter: - process.env.NEXT_PUBLIC_PRIVACY_PREFERENCE_CENTER || 'true', + getEnv('NEXT_PUBLIC_PRIVACY_PREFERENCE_CENTER') || + process.env.NEXT_PUBLIC_PRIVACY_PREFERENCE_CENTER || + 'true', // Default terms to be used for service offerings made on this marketplace defaultAccessTerms: 'https://raw.githubusercontent.com/OceanProtocolEnterprise/market/main/content/pages/terms.md', // Purgatory URI, leave as an empty string to disable the API call - purgatoryUrl: process.env.NEXT_PUBLIC_PURGATORY_URI || '', + purgatoryUrl: + getEnv('NEXT_PUBLIC_PURGATORY_URI') || + process.env.NEXT_PUBLIC_PURGATORY_URI || + '', // The url used to fetch docker hub image info dockerHubProxyUrl: + getEnv('NEXT_PUBLIC_DOCKER_HUB_PROXY_URL') || process.env.NEXT_PUBLIC_DOCKER_HUB_PROXY_URL || 'https://dockerhub-proxy.oceanprotocol.com', // Display alert banner for the developer preview deployment - showPreviewAlert: process.env.NEXT_PUBLIC_SHOW_PREVIEW_ALERT || 'false', + showPreviewAlert: + getEnv('NEXT_PUBLIC_SHOW_PREVIEW_ALERT') || + process.env.NEXT_PUBLIC_SHOW_PREVIEW_ALERT || + 'false', - encryptAsset: process.env.NEXT_PUBLIC_ENCRYPT_ASSET - ? process.env.NEXT_PUBLIC_ENCRYPT_ASSET === 'true' - : false, + encryptAsset: + getEnv('NEXT_PUBLIC_ENCRYPT_ASSET') || process.env.NEXT_PUBLIC_ENCRYPT_ASSET + ? (getEnv('NEXT_PUBLIC_ENCRYPT_ASSET') || + process.env.NEXT_PUBLIC_ENCRYPT_ASSET) === 'true' + : false, // This enables / disables the ssi support - ssiEnabled: process.env.NEXT_PUBLIC_SSI_ENABLED - ? process.env.NEXT_PUBLIC_SSI_ENABLED === 'true' - : false, + ssiEnabled: + getEnv('NEXT_PUBLIC_SSI_ENABLED') || process.env.NEXT_PUBLIC_SSI_ENABLED + ? (getEnv('NEXT_PUBLIC_SSI_ENABLED') || + process.env.NEXT_PUBLIC_SSI_ENABLED) === 'true' + : false, ssiWalletApi: - process.env.NEXT_PUBLIC_SSI_WALLET_API || 'https://wallet.demo.walt.id', + getEnv('NEXT_PUBLIC_SSI_WALLET_API') || + process.env.NEXT_PUBLIC_SSI_WALLET_API || + 'https://wallet.demo.oceanenterprise.io', ssiDefaultPolicyUrl: + getEnv('NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL') || process.env.NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL || 'https://raw.githubusercontent.com/OceanProtocolEnterprise/policy-server/refs/heads/main/default-verification-policies', - ipfsJWT: process.env.NEXT_PUBLIC_IPFS_JWT, - ipfsGateway: process.env.NEXT_PUBLIC_IPFS_GATEWAY, - ipfsUnpinFiles: process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES - ? process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES === 'true' - : false, - - opaServer: process.env.NEXT_PUBLIC_OPA_SERVER_URL, + ipfsJWT: getEnv('NEXT_PUBLIC_IPFS_JWT') || process.env.NEXT_PUBLIC_IPFS_JWT, + ipfsGateway: + getEnv('NEXT_PUBLIC_IPFS_GATEWAY') || process.env.NEXT_PUBLIC_IPFS_GATEWAY, + ipfsUnpinFiles: + getEnv('NEXT_PUBLIC_IPFS_UNPIN_FILES') || + process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES + ? (getEnv('NEXT_PUBLIC_IPFS_UNPIN_FILES') || + process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES) === 'true' + : false, + + opaServer: + getEnv('NEXT_PUBLIC_OPA_SERVER_URL') || + process.env.NEXT_PUBLIC_OPA_SERVER_URL, showOnboardingModuleByDefault: - process.env.NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT === 'false', - nodeUriIndex: process.env.NEXT_PUBLIC_NODE_URI_INDEXED - ? JSON.parse(process.env.NEXT_PUBLIC_NODE_URI_INDEXED) - : [ - process.env.NEXT_PUBLIC_PROVIDER_URL || - 'https://ocean-node-vm3.oceanenterprise.io' - ] + (getEnv('NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT') || + process.env.NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT) === 'false', + nodeUriIndex: + getEnv('NEXT_PUBLIC_NODE_URI_INDEXED') || + process.env.NEXT_PUBLIC_NODE_URI_INDEXED + ? JSON.parse( + getEnv('NEXT_PUBLIC_NODE_URI_INDEXED') || + process.env.NEXT_PUBLIC_NODE_URI_INDEXED + ) + : [ + getEnv('NEXT_PUBLIC_PROVIDER_URL') || + process.env.NEXT_PUBLIC_PROVIDER_URL || + 'https://ocean-node-vm3.oceanenterprise.io' + ] } diff --git a/docker-compose.yml b/docker-compose.yml index d12304339..698c44aaa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,35 +1,12 @@ version: '3.8' services: ocean-market: - build: - context: . - dockerfile: Dockerfile - args: - NEXT_PUBLIC_ENCRYPT_ASSET: ${NEXT_PUBLIC_ENCRYPT_ASSET} - NEXT_PUBLIC_SSI_WALLET_API: ${NEXT_PUBLIC_SSI_WALLET_API} - NEXT_PUBLIC_METADATACACHE_URI: ${NEXT_PUBLIC_METADATACACHE_URI} - NEXT_PUBLIC_PROVIDER_URL: ${NEXT_PUBLIC_PROVIDER_URL} - NEXT_PUBLIC_IPFS_UNPIN_FILES: ${NEXT_PUBLIC_IPFS_UNPIN_FILES} - NEXT_PUBLIC_NODE_URI: ${NEXT_PUBLIC_NODE_URI} - NEXT_PUBLIC_IPFS_GATEWAY: ${NEXT_PUBLIC_IPFS_GATEWAY} - NEXT_PUBLIC_IPFS_JWT: ${NEXT_PUBLIC_IPFS_JWT} - NEXT_PUBLIC_SSI_POLICY_SERVER: ${NEXT_PUBLIC_SSI_POLICY_SERVER} - NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL: ${NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL} - NEXT_PUBLIC_OPA_SERVER_URL: ${NEXT_PUBLIC_OPA_SERVER_URL} - NEXT_PUBLIC_SSI_ENABLED: ${NEXT_PUBLIC_SSI_ENABLED} - NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT: ${NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT} - NEXT_PUBLIC_NODE_URI_INDEXED: ${NEXT_PUBLIC_NODE_URI_INDEXED} - NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: ${NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID} - NEXT_PUBLIC_INFURA_PROJECT_ID: ${NEXT_PUBLIC_INFURA_PROJECT_ID} - NEXT_PUBLIC_CONSUME_MARKET_FEE: ${NEXT_PUBLIC_CONSUME_MARKET_FEE} - NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE: ${NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE} - NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION: ${NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION} - NEXT_PUBLIC_NODE_URI_MAP: ${NEXT_PUBLIC_NODE_URI_MAP} - NEXT_PUBLIC_MARKET_FEE_ADDRESS: ${NEXT_PUBLIC_MARKET_FEE_ADDRESS} - NEXT_PUBLIC_ERC20_ADDRESSES: ${NEXT_PUBLIC_ERC20_ADDRESSES} + image: oceanenterprise/market:latest container_name: ocean-market ports: - '8008:8008' + env_file: + - .env environment: NODE_ENV: production restart: unless-stopped diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 000000000..6c006ca95 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -eu + +node - <<'NODE' +const fs = require('fs') + +const config = { + NEXT_PUBLIC_ENCRYPT_ASSET: process.env.NEXT_PUBLIC_ENCRYPT_ASSET, + NEXT_PUBLIC_SSI_WALLET_API: process.env.NEXT_PUBLIC_SSI_WALLET_API, + NEXT_PUBLIC_METADATACACHE_URI: process.env.NEXT_PUBLIC_METADATACACHE_URI, + NEXT_PUBLIC_PROVIDER_URL: process.env.NEXT_PUBLIC_PROVIDER_URL, + NEXT_PUBLIC_IPFS_UNPIN_FILES: process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES, + NEXT_PUBLIC_NODE_URI: process.env.NEXT_PUBLIC_NODE_URI, + NEXT_PUBLIC_IPFS_GATEWAY: process.env.NEXT_PUBLIC_IPFS_GATEWAY, + NEXT_PUBLIC_IPFS_JWT: process.env.NEXT_PUBLIC_IPFS_JWT, + NEXT_PUBLIC_SSI_POLICY_SERVER: process.env.NEXT_PUBLIC_SSI_POLICY_SERVER, + NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL: + process.env.NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL, + NEXT_PUBLIC_OPA_SERVER_URL: process.env.NEXT_PUBLIC_OPA_SERVER_URL, + NEXT_PUBLIC_SSI_ENABLED: process.env.NEXT_PUBLIC_SSI_ENABLED, + NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT: + process.env.NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT, + NEXT_PUBLIC_NODE_URI_INDEXED: process.env.NEXT_PUBLIC_NODE_URI_INDEXED, + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: + process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, + NEXT_PUBLIC_INFURA_PROJECT_ID: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID, + NEXT_PUBLIC_CONSUME_MARKET_FEE: process.env.NEXT_PUBLIC_CONSUME_MARKET_FEE, + NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE: + process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE, + NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS: + process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, + NEXT_PUBLIC_DISPENSER_ADDRESS: process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + NEXT_PUBLIC_NFT_FACTORY_ADDRESS: process.env.NEXT_PUBLIC_NFT_FACTORY_ADDRESS, + NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS, + NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS, + NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION: + process.env.NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION, + NEXT_PUBLIC_NODE_URI_MAP: process.env.NEXT_PUBLIC_NODE_URI_MAP, + NEXT_PUBLIC_MARKET_FEE_ADDRESS: process.env.NEXT_PUBLIC_MARKET_FEE_ADDRESS, + NEXT_PUBLIC_ERC20_ADDRESSES: process.env.NEXT_PUBLIC_ERC20_ADDRESSES +} + +fs.writeFileSync( + '/app/public/runtime-config.js', + `window.__RUNTIME_CONFIG__ = ${JSON.stringify(config)};\n` +) +NODE + +exec "$@" diff --git a/scripts/pregenerate.sh b/scripts/pregenerate.sh index 66a81a0cf..a9a7cbaec 100755 --- a/scripts/pregenerate.sh +++ b/scripts/pregenerate.sh @@ -5,3 +5,6 @@ node ./scripts/write-repo-metadata.cjs > content/repo-metadata.json # Fetch EVM networks metadata node ./scripts/write-networks-metadata.cjs > content/networks-metadata.json + +# Write runtime config for local dev/start +node ./scripts/write-runtime-config.cjs diff --git a/scripts/write-runtime-config.cjs b/scripts/write-runtime-config.cjs new file mode 100644 index 000000000..d1eb2936e --- /dev/null +++ b/scripts/write-runtime-config.cjs @@ -0,0 +1,66 @@ +const fs = require('fs') +const path = require('path') + +const envPath = path.join(process.cwd(), '.env') +if (fs.existsSync(envPath)) { + const lines = fs.readFileSync(envPath, 'utf8').split(/\r?\n/) + for (const line of lines) { + const trimmed = line.trim() + if (!trimmed || trimmed.startsWith('#')) continue + const eqIndex = trimmed.indexOf('=') + if (eqIndex === -1) continue + const key = trimmed.slice(0, eqIndex).trim() + let value = trimmed.slice(eqIndex + 1).trim() + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1) + } + if (typeof process.env[key] === 'undefined') { + process.env[key] = value + } + } +} + +const config = { + NEXT_PUBLIC_ENCRYPT_ASSET: process.env.NEXT_PUBLIC_ENCRYPT_ASSET, + NEXT_PUBLIC_SSI_WALLET_API: process.env.NEXT_PUBLIC_SSI_WALLET_API, + NEXT_PUBLIC_METADATACACHE_URI: process.env.NEXT_PUBLIC_METADATACACHE_URI, + NEXT_PUBLIC_PROVIDER_URL: process.env.NEXT_PUBLIC_PROVIDER_URL, + NEXT_PUBLIC_IPFS_UNPIN_FILES: process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES, + NEXT_PUBLIC_NODE_URI: process.env.NEXT_PUBLIC_NODE_URI, + NEXT_PUBLIC_IPFS_GATEWAY: process.env.NEXT_PUBLIC_IPFS_GATEWAY, + NEXT_PUBLIC_IPFS_JWT: process.env.NEXT_PUBLIC_IPFS_JWT, + NEXT_PUBLIC_SSI_POLICY_SERVER: process.env.NEXT_PUBLIC_SSI_POLICY_SERVER, + NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL: + process.env.NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL, + NEXT_PUBLIC_OPA_SERVER_URL: process.env.NEXT_PUBLIC_OPA_SERVER_URL, + NEXT_PUBLIC_SSI_ENABLED: process.env.NEXT_PUBLIC_SSI_ENABLED, + NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT: + process.env.NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT, + NEXT_PUBLIC_NODE_URI_INDEXED: process.env.NEXT_PUBLIC_NODE_URI_INDEXED, + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: + process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, + NEXT_PUBLIC_INFURA_PROJECT_ID: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID, + NEXT_PUBLIC_CONSUME_MARKET_FEE: process.env.NEXT_PUBLIC_CONSUME_MARKET_FEE, + NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE: + process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE, + NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS: + process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, + NEXT_PUBLIC_DISPENSER_ADDRESS: process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + NEXT_PUBLIC_NFT_FACTORY_ADDRESS: process.env.NEXT_PUBLIC_NFT_FACTORY_ADDRESS, + NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS, + NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS, + NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION: + process.env.NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION, + NEXT_PUBLIC_NODE_URI_MAP: process.env.NEXT_PUBLIC_NODE_URI_MAP, + NEXT_PUBLIC_MARKET_FEE_ADDRESS: process.env.NEXT_PUBLIC_MARKET_FEE_ADDRESS, + NEXT_PUBLIC_ERC20_ADDRESSES: process.env.NEXT_PUBLIC_ERC20_ADDRESSES +} + +const outputPath = path.join(process.cwd(), 'public', 'runtime-config.js') +const contents = `window.__RUNTIME_CONFIG__ = ${JSON.stringify(config)};\n` +fs.writeFileSync(outputPath, contents) diff --git a/src/@context/UrqlProvider.tsx b/src/@context/UrqlProvider.tsx index fb977d0ba..85cff9546 100644 --- a/src/@context/UrqlProvider.tsx +++ b/src/@context/UrqlProvider.tsx @@ -50,9 +50,6 @@ export default function UrqlClientProvider({ const newClient = createUrqlClient(oceanConfig.nodeUri) urqlClient = newClient setClient(newClient) - LoggerInstance.log( - `[URQL] Client connected to ${oceanConfig.nodeUri} (chainId: ${chainId})` - ) }, [chainId]) // re-run when chain changes return client ? {children} : <> diff --git a/src/@utils/credentialExpiration.ts b/src/@utils/credentialExpiration.ts index b479efbb8..80fb183b0 100644 --- a/src/@utils/credentialExpiration.ts +++ b/src/@utils/credentialExpiration.ts @@ -1,3 +1,5 @@ +import { getRuntimeConfig } from './runtimeConfig' + export interface CredentialStatus { isValid: boolean expiresAt?: number @@ -5,8 +7,10 @@ export interface CredentialStatus { needsRefresh: boolean } +const runtimeConfig = getRuntimeConfig() const CREDENTIAL_VALIDITY_DURATION = - Number(process.env.NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION) || 5 * 60 * 1000 + Number(runtimeConfig.NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION) || + 5 * 60 * 1000 export function createCredentialStatus( isValid: boolean, diff --git a/src/@utils/ipfs.ts b/src/@utils/ipfs.ts index cdd397de9..3c5b3fd2a 100644 --- a/src/@utils/ipfs.ts +++ b/src/@utils/ipfs.ts @@ -2,6 +2,7 @@ import * as isIPFS from 'is-ipfs' import { FileItem } from '@utils/fileItem' import { RemoteSource } from '../@types/ddo/RemoteSource' import axios from 'axios' +import { getRuntimeConfig } from './runtimeConfig' export interface IpfsRemoteDocument { content: string @@ -42,7 +43,6 @@ export async function serverSideUploadToIpfs( } ) - // Pinata API response structure is { IpfsHash, PinSize, ... } return response.data.IpfsHash } catch (error) { throw new Error(`[serverSideUploadToIpfs] ${error.message}`) @@ -51,8 +51,11 @@ export async function serverSideUploadToIpfs( export async function uploadToIPFS(data: any): Promise { try { + const runtimeConfig = getRuntimeConfig() + const ipfsJWT = runtimeConfig.NEXT_PUBLIC_IPFS_JWT const res = await fetch('/api/ipfs', { method: 'POST', + headers: ipfsJWT ? { 'x-ipfs-jwt': ipfsJWT } : undefined, body: JSON.stringify(data) }) @@ -94,8 +97,11 @@ export async function serverSideDeleteIpfsFile( export async function deleteIpfsFile(ipfsHash: string) { try { + const runtimeConfig = getRuntimeConfig() + const ipfsJWT = runtimeConfig.NEXT_PUBLIC_IPFS_JWT const res = await fetch('/api/ipfs', { method: 'DELETE', + headers: ipfsJWT ? { 'x-ipfs-jwt': ipfsJWT } : undefined, body: ipfsHash }) diff --git a/src/@utils/ocean/index.ts b/src/@utils/ocean/index.ts index 5dbb41f5d..8311797ad 100644 --- a/src/@utils/ocean/index.ts +++ b/src/@utils/ocean/index.ts @@ -3,6 +3,7 @@ import { Config, getOceanArtifactsAddressesByChainId } from '@oceanprotocol/lib' +import { getRuntimeConfig } from '../runtimeConfig' /** This function takes a Config object as an input and returns a new sanitized Config object @@ -12,31 +13,33 @@ import { @returns {Config} A new Config object */ export function sanitizeDevelopmentConfig(config: Config): Config { + const runtimeConfig = getRuntimeConfig() return { nodeUri: config.nodeUri, - oceanNodeUri: process.env.NEXT_PUBLIC_PROVIDER_URL || config.oceanNodeUri, + oceanNodeUri: runtimeConfig.NEXT_PUBLIC_PROVIDER_URL || config.oceanNodeUri, fixedRateExchangeAddress: - process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, - dispenserAddress: process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + runtimeConfig.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, + dispenserAddress: runtimeConfig.NEXT_PUBLIC_DISPENSER_ADDRESS, oceanTokenAddress: config.oceanTokenAddress, - nftFactoryAddress: process.env.NEXT_PUBLIC_NFT_FACTORY_ADDRESS, - routerFactoryAddress: process.env.NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS, + nftFactoryAddress: runtimeConfig.NEXT_PUBLIC_NFT_FACTORY_ADDRESS, + routerFactoryAddress: runtimeConfig.NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS, accessListFactory: config.accessListFactory || - process.env.NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS + runtimeConfig.NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS } as Config } export function getOceanConfig(network: string | number): any { + const runtimeConfig = getRuntimeConfig() // Load the RPC map from .env - const rpcMap: Record = process.env.NEXT_PUBLIC_NODE_URI_MAP - ? JSON.parse(process.env.NEXT_PUBLIC_NODE_URI_MAP) + const rpcMap: Record = runtimeConfig.NEXT_PUBLIC_NODE_URI_MAP + ? JSON.parse(runtimeConfig.NEXT_PUBLIC_NODE_URI_MAP) : {} - const erc20Map: Record = process.env - .NEXT_PUBLIC_ERC20_ADDRESSES - ? JSON.parse(process.env.NEXT_PUBLIC_ERC20_ADDRESSES) - : {} + const erc20Map: Record = + runtimeConfig.NEXT_PUBLIC_ERC20_ADDRESSES + ? JSON.parse(runtimeConfig.NEXT_PUBLIC_ERC20_ADDRESSES) + : {} if (!network) { console.warn('[getOceanConfig] No network provided yet.') @@ -52,7 +55,7 @@ export function getOceanConfig(network: string | number): any { network === 56 || network === 8996 ? undefined - : process.env.NEXT_PUBLIC_INFURA_PROJECT_ID + : runtimeConfig.NEXT_PUBLIC_INFURA_PROJECT_ID ) as any if (network === 8996) { config = { ...config, ...sanitizeDevelopmentConfig(config) } diff --git a/src/@utils/runtimeConfig.ts b/src/@utils/runtimeConfig.ts new file mode 100644 index 000000000..6a0418513 --- /dev/null +++ b/src/@utils/runtimeConfig.ts @@ -0,0 +1,88 @@ +export type RuntimeConfig = { + NEXT_PUBLIC_ENCRYPT_ASSET?: string + NEXT_PUBLIC_METADATACACHE_URI?: string + NEXT_PUBLIC_SSI_WALLET_API?: string + NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL?: string + NEXT_PUBLIC_SSI_POLICY_SERVER?: string + NEXT_PUBLIC_SSI_ENABLED?: string + NEXT_PUBLIC_PROVIDER_URL?: string + NEXT_PUBLIC_IPFS_UNPIN_FILES?: string + NEXT_PUBLIC_NODE_URI?: string + NEXT_PUBLIC_IPFS_GATEWAY?: string + NEXT_PUBLIC_IPFS_JWT?: string + NEXT_PUBLIC_OPA_SERVER_URL?: string + NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT?: string + NEXT_PUBLIC_NODE_URI_INDEXED?: string + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID?: string + NEXT_PUBLIC_INFURA_PROJECT_ID?: string + NEXT_PUBLIC_CONSUME_MARKET_FEE?: string + NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE?: string + NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS?: string + NEXT_PUBLIC_DISPENSER_ADDRESS?: string + NEXT_PUBLIC_NFT_FACTORY_ADDRESS?: string + NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS?: string + NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS?: string + NEXT_PUBLIC_NODE_URI_MAP?: string + NEXT_PUBLIC_ERC20_ADDRESSES?: string + NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION?: string + NEXT_PUBLIC_MARKET_FEE_ADDRESS?: string + NEXT_PUBLIC_MARKET_DEVELOPMENT?: string +} + +declare global { + interface Window { + __RUNTIME_CONFIG__?: RuntimeConfig + } +} + +const runtimeConfig: RuntimeConfig = (() => { + const baseConfig: RuntimeConfig = { + NEXT_PUBLIC_ENCRYPT_ASSET: process.env.NEXT_PUBLIC_ENCRYPT_ASSET, + NEXT_PUBLIC_METADATACACHE_URI: process.env.NEXT_PUBLIC_METADATACACHE_URI, + NEXT_PUBLIC_SSI_WALLET_API: process.env.NEXT_PUBLIC_SSI_WALLET_API, + NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL: + process.env.NEXT_PUBLIC_SSI_DEFAULT_POLICIES_URL, + NEXT_PUBLIC_SSI_POLICY_SERVER: process.env.NEXT_PUBLIC_SSI_POLICY_SERVER, + NEXT_PUBLIC_SSI_ENABLED: process.env.NEXT_PUBLIC_SSI_ENABLED, + NEXT_PUBLIC_PROVIDER_URL: process.env.NEXT_PUBLIC_PROVIDER_URL, + NEXT_PUBLIC_IPFS_UNPIN_FILES: process.env.NEXT_PUBLIC_IPFS_UNPIN_FILES, + NEXT_PUBLIC_NODE_URI: process.env.NEXT_PUBLIC_NODE_URI, + NEXT_PUBLIC_IPFS_GATEWAY: process.env.NEXT_PUBLIC_IPFS_GATEWAY, + NEXT_PUBLIC_IPFS_JWT: process.env.NEXT_PUBLIC_IPFS_JWT, + NEXT_PUBLIC_OPA_SERVER_URL: process.env.NEXT_PUBLIC_OPA_SERVER_URL, + NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT: + process.env.NEXT_PUBLIC_SHOW_ONBOARDING_MODULE_BY_DEFAULT, + NEXT_PUBLIC_NODE_URI_INDEXED: process.env.NEXT_PUBLIC_NODE_URI_INDEXED, + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: + process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, + NEXT_PUBLIC_INFURA_PROJECT_ID: process.env.NEXT_PUBLIC_INFURA_PROJECT_ID, + NEXT_PUBLIC_CONSUME_MARKET_FEE: process.env.NEXT_PUBLIC_CONSUME_MARKET_FEE, + NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE: + process.env.NEXT_PUBLIC_CONSUME_MARKET_ORDER_FEE, + NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS: + process.env.NEXT_PUBLIC_FIXED_RATE_EXCHANGE_ADDRESS, + NEXT_PUBLIC_DISPENSER_ADDRESS: process.env.NEXT_PUBLIC_DISPENSER_ADDRESS, + NEXT_PUBLIC_NFT_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_NFT_FACTORY_ADDRESS, + NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ROUTER_FACTORY_ADDRESS, + NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS: + process.env.NEXT_PUBLIC_ACCESS_LIST_FACTORY_ADDRESS, + NEXT_PUBLIC_NODE_URI_MAP: process.env.NEXT_PUBLIC_NODE_URI_MAP, + NEXT_PUBLIC_ERC20_ADDRESSES: process.env.NEXT_PUBLIC_ERC20_ADDRESSES, + NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION: + process.env.NEXT_PUBLIC_CREDENTIAL_VALIDITY_DURATION, + NEXT_PUBLIC_MARKET_FEE_ADDRESS: process.env.NEXT_PUBLIC_MARKET_FEE_ADDRESS, + NEXT_PUBLIC_MARKET_DEVELOPMENT: process.env.NEXT_PUBLIC_MARKET_DEVELOPMENT + } + + if (typeof window !== 'undefined' && window.__RUNTIME_CONFIG__) { + return { ...baseConfig, ...window.__RUNTIME_CONFIG__ } + } + + return baseConfig +})() + +export function getRuntimeConfig(): RuntimeConfig { + return runtimeConfig +} diff --git a/src/@utils/wallet/chains.ts b/src/@utils/wallet/chains.ts index be198845f..135b83eda 100644 --- a/src/@utils/wallet/chains.ts +++ b/src/@utils/wallet/chains.ts @@ -1,5 +1,6 @@ import { Chain } from 'wagmi/chains' import * as wagmiChains from 'wagmi/chains' +import { getRuntimeConfig } from '../runtimeConfig' // Custom OP Sepolia chain export const opSepolia: Chain = { @@ -48,8 +49,9 @@ export const getSupportedChains = (chainIdsSupported: number[]): Chain[] => { const allChains = [...baseChains, opSepolia, ethereumHoodi] // Load RPC map from .env - const rpcMap: Record = process.env.NEXT_PUBLIC_NODE_URI_MAP - ? JSON.parse(process.env.NEXT_PUBLIC_NODE_URI_MAP) + const runtimeConfig = getRuntimeConfig() + const rpcMap: Record = runtimeConfig.NEXT_PUBLIC_NODE_URI_MAP + ? JSON.parse(runtimeConfig.NEXT_PUBLIC_NODE_URI_MAP) : {} // Filter chains by allowed IDs and override RPCs if set in env diff --git a/src/@utils/wallet/index.ts b/src/@utils/wallet/index.ts index a917f3096..382bf7640 100644 --- a/src/@utils/wallet/index.ts +++ b/src/@utils/wallet/index.ts @@ -1,13 +1,12 @@ 'use client' import { LoggerInstance } from '@oceanprotocol/lib' -import { cookieStorage, createConfig, createStorage, injected } from 'wagmi' +import { cookieStorage, createConfig, createStorage } from 'wagmi' import { erc20Abi, http } from 'viem' import { localhost, type Chain } from 'wagmi/chains' import { ethers, Contract, - Signer, formatEther, JsonRpcProvider, Provider, @@ -17,7 +16,7 @@ import { getNetworkDisplayName } from '@hooks/useNetworkMetadata' import { getOceanConfig } from '../ocean' import { getSupportedChains } from './chains' import { chainIdsSupported } from '../../../app.config.cjs' -import { walletConnect } from 'wagmi/connectors' +import { getRuntimeConfig } from '../runtimeConfig' export async function getDummySigner(chainId: number): Promise { const config = getOceanConfig(chainId) @@ -36,8 +35,9 @@ export async function getDummySigner(chainId: number): Promise { ------------------------------------------ */ function getWagmiChains(): readonly [Chain, ...Chain[]] { const baseChains: Chain[] = [...getSupportedChains(chainIdsSupported)] + const runtimeConfig = getRuntimeConfig() - if (process.env.NEXT_PUBLIC_MARKET_DEVELOPMENT === 'true') { + if (runtimeConfig.NEXT_PUBLIC_MARKET_DEVELOPMENT === 'true') { baseChains.push({ ...localhost, id: 11155420 }) } diff --git a/src/@utils/wallet/policyServer.ts b/src/@utils/wallet/policyServer.ts index da9100b8d..f9cc06586 100644 --- a/src/@utils/wallet/policyServer.ts +++ b/src/@utils/wallet/policyServer.ts @@ -22,7 +22,10 @@ export async function requestCredentialPresentation( policyServerData: PolicyServerInitiateActionData }> { try { - const sessionId = crypto.randomUUID() + const sessionId = + typeof globalThis.crypto?.randomUUID === 'function' + ? globalThis.crypto.randomUUID() + : `${Date.now()}-${Math.random().toString(16).slice(2)}` const policyServer: PolicyServerInitiateActionData = { sessionId, successRedirectUri: ``, diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 92c6e48ce..a70c339f1 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,5 +1,6 @@ 'use client' import type { AppProps } from 'next/app' +import Script from 'next/script' import { ReactElement, useEffect, useState } from 'react' import { UserPreferencesProvider } from '@context/UserPreferences' import UrqlProvider from '@context/UrqlProvider' @@ -31,6 +32,7 @@ function MyApp({ Component, pageProps }: AppProps): ReactElement { return ( +