diff --git a/.github/workflows/build-docker-image.yaml b/.github/workflows/build-docker-image.yaml index 1fc1cac49..98b0080b1 100644 --- a/.github/workflows/build-docker-image.yaml +++ b/.github/workflows/build-docker-image.yaml @@ -30,7 +30,7 @@ jobs: id: meta uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: - images: multiversx/mx-explorer-dapp + images: gioviboy/mx-explorer-dapp - name: Build and push Docker image id: push diff --git a/entrypoint.sh b/entrypoint.sh index f21c00dda..4a1df7626 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -36,10 +36,36 @@ if [ -n "$START_API_ADDRESS_STOP" ]; then fi if [ -n "$START_IS_SOVEREIGN_STOP" ]; then + # normalizează la true/false + case "$(printf '%s' "$START_IS_SOVEREIGN_STOP" | tr '[:upper:]' '[:lower:]')" in + 1|true|yes|y|on) START_IS_SOVEREIGN_STOP=true ;; + 0|false|no|n|off|"") START_IS_SOVEREIGN_STOP=false ;; + *) START_IS_SOVEREIGN_STOP=true ;; + esac + echo "IS Sovereign defined: ${START_IS_SOVEREIGN_STOP}, replacing in config" - find /usr/share/nginx/html/ -type f -exec sed -i 's|START_IS_SOVEREIGN_STOP|'${START_IS_SOVEREIGN_STOP}'|g' {} + + find /usr/share/nginx/html/ -type f -exec sed -i \ + -e "s|'START_IS_SOVEREIGN_STOP'|${START_IS_SOVEREIGN_STOP}|g" \ + -e "s|\"START_IS_SOVEREIGN_STOP\"|${START_IS_SOVEREIGN_STOP}|g" \ + -e "s|START_IS_SOVEREIGN_STOP|${START_IS_SOVEREIGN_STOP}|g" {} + fi +if [ -n "$START_IS_ACCESSTOKEN_STOP" ]; then + # normalizează la true/false + case "$(printf '%s' "$START_IS_ACCESSTOKEN_STOP" | tr '[:upper:]' '[:lower:]')" in + 1|true|yes|y|on) START_IS_ACCESSTOKEN_STOP=true ;; + 0|false|no|n|off|"") START_IS_ACCESSTOKEN_STOP=false ;; + *) START_IS_ACCESSTOKEN_STOP=true ;; + esac + + echo "IS Sovereign defined: ${START_IS_ACCESSTOKEN_STOP}, replacing in config" + find /usr/share/nginx/html/ -type f -exec sed -i \ + -e "s|'START_IS_ACCESSTOKEN_STOP'|${START_IS_ACCESSTOKEN_STOP}|g" \ + -e "s|\"START_IS_ACCESSTOKEN_STOP\"|${START_IS_ACCESSTOKEN_STOP}|g" \ + -e "s|START_IS_ACCESSTOKEN_STOP|${START_IS_ACCESSTOKEN_STOP}|g" {} + +fi + + # FIX NGINX sed -i '/location \/ {/,/}/d' /etc/nginx/conf.d/default.conf sed -i '/#access_log/a\ diff --git a/package.json b/package.json index c00593932..24f9aab21 100644 --- a/package.json +++ b/package.json @@ -55,17 +55,29 @@ "yup": "0.32.11" }, "scripts": { - "start": "cross-env VITE_APP_CACHE_BUST=$(git rev-parse --short HEAD || echo 'local') vite", - "start-devnet": "yarn run copy-devnet-config & cross-env VITE_APP_SHARE_PREFIX=devnet- yarn run start", - "start-mainnet": "yarn run copy-mainnet-config & yarn run start", - "start-testnet": "yarn run copy-testnet-config & cross-env VITE_APP_SHARE_PREFIX=testnet- yarn run start", - "build-devnet": "yarn run copy-devnet-config & cross-env VITE_APP_SHARE_PREFIX=devnet- VITE_APP_GA_ID=G-QE38EW59B8 yarn run build", - "build-mainnet": "yarn run copy-mainnet-config & cross-env VITE_APP_SHARE_PREFIX= VITE_APP_GA_ID=G-EFN7QYSMQS yarn run build", - "build-testnet": "yarn run copy-testnet-config & cross-env VITE_APP_SHARE_PREFIX=testnet- VITE_APP_GA_ID=G-EFN7QYSMQS yarn run build", - "build-staging": "yarn run copy-mainnet-config & cross-env VITE_APP_SHARE_PREFIX=staging- VITE_APP_GA_ID=G-EFN7QYSMQS VITE_APP_IS_STAGING=true yarn run build", - "build-internal": "yarn run copy-multiple-config & cross-env VITE_APP_SHARE_PREFIX=internal- VITE_APP_GA_ID=G-EFN7QYSMQS yarn run build", - "build-next": "yarn run copy-multiple-config & cross-env VITE_APP_SHARE_PREFIX=next- VITE_APP_GA_ID=G-EFN7QYSMQS yarn run build", - "build": "cross-env VITE_APP_CACHE_BUST=$(git rev-parse --short HEAD || echo 'local') vite build && yarn version:make", + "start-local": "yarn run config:from-env && node scripts/start.js", + "build-local": "yarn run config:from-env && node scripts/build.js", + "config:from-env": "node scripts/config-from-env.js", + "start": "node scripts/start.js", + "prestart-mainnet": "yarn run copy-mainnet-config", + "start-mainnet": "node scripts/start.js --share-prefix=", + "prestart-devnet": "yarn run copy-devnet-config", + "start-devnet": "node scripts/start.js --share-prefix=devnet-", + "prestart-testnet": "yarn run copy-testnet-config", + "start-testnet": "node scripts/start.js --share-prefix=testnet-", + "build": "node scripts/build.js", + "prebuild-mainnet": "yarn run copy-mainnet-config", + "build-mainnet": "node scripts/build.js --share-prefix= --ga=G-EFN7QYSMQS", + "prebuild-devnet": "yarn run copy-devnet-config", + "build-devnet": "node scripts/build.js --share-prefix=devnet- --ga=G-QE38EW59B8", + "prebuild-testnet": "yarn run copy-testnet-config", + "build-testnet": "node scripts/build.js --share-prefix=testnet- --ga=G-EFN7QYSMQS", + "prebuild-staging": "yarn run copy-mainnet-config", + "build-staging": "node scripts/build.js --share-prefix=staging- --ga=G-EFN7QYSMQS --staging=true", + "prebuild-internal": "yarn run copy-multiple-config", + "build-internal": "node scripts/build.js --share-prefix=internal- --ga=G-EFN7QYSMQS", + "prebuild-next": "yarn run copy-multiple-config", + "build-next": "node scripts/build.js --share-prefix=next- --ga=G-EFN7QYSMQS", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "copy-placeholder-config": "run-script-os", @@ -86,7 +98,7 @@ "copy-testnet-config:windows": "copy .\\src\\config\\config.testnet.ts .\\src\\config\\index.ts", "copy-multiple-config:windows": "copy .\\src\\config\\config.multiple.ts .\\src\\config\\index.ts", "copy-base-interceptor:windows": "copy .\\src\\interceptors\\Interceptor.tsx .\\src\\interceptors\\index.tsx", - "version:make": "git rev-parse --short HEAD | sed 's/^/\"/;s/$/\"/' > build/version.json", + "version:make": "node scripts/version-make.js", "prepare": "yarn run prepare-free-icons && yarn run copy-base-interceptor", "prepare-free-icons": "run-script-os", "prepare-free-icons:nix": "cp ./src/icons/duotone/fontawesomeFree.ts ./src/icons/duotone/index.ts && cp ./src/icons/light/fontawesomeFree.ts ./src/icons/light/index.ts && cp ./src/icons/regular/fontawesomeFree.ts ./src/icons/regular/index.ts && cp ./src/icons/solid/fontawesomeFree.ts ./src/icons/solid/index.ts", @@ -113,6 +125,7 @@ "cross-env": "7.0.3", "cypress": "13.6.3", "cypress-mochawesome-reporter": "3.7.0", + "dotenv": "^17.2.1", "eslint": "8.57.0", "eslint-config-prettier": "8.8.0", "eslint-plugin-import": "2.28.1", diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 000000000..c529928b6 --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,54 @@ +// scripts/build.js +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// 1) SHA pentru cache-bust +let sha = 'local'; +try { + sha = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) + .toString() + .trim(); +} catch { /* fără git -> 'local' */ } + +// 2) Parsează argumente CLI ca să nu mai depindem de cross-env +// ex: node scripts/build.js --share-prefix=devnet- --ga=G-XXXX --staging=true +const argv = process.argv.slice(2); +for (const a of argv) { + if (a.startsWith('--share-prefix=')) { + process.env.VITE_APP_SHARE_PREFIX = a.split('=')[1]; + } else if (a.startsWith('--ga=')) { + process.env.VITE_APP_GA_ID = a.split('=')[1]; + } else if (a.startsWith('--staging=')) { + process.env.VITE_APP_IS_STAGING = a.split('=')[1]; + } +} + +// 3) Setează cache-bust +process.env.VITE_APP_CACHE_BUST = sha; + +// 4) Rulează Vite cu API-ul oficial (nu prin npx) +(async () => { + try { + const { build } = require('vite'); + await build(); // Vite va afișa logurile: "vite vX building for production..." + } catch (err) { + // fallback (dacă nu există vite în deps) + try { + const { spawnSync } = require('child_process'); + const nodeCmd = process.platform === 'win32' ? 'node.exe' : 'node'; + const res = spawnSync(nodeCmd, [path.join('node_modules', 'vite', 'bin', 'vite.js'), 'build'], { + stdio: 'inherit', + env: process.env, + }); + if (res.status !== 0) process.exit(res.status ?? 1); + } catch (e2) { + console.error('Build failed:', err); + process.exit(1); + } + } + + // 5) Scrie build/version.json cu SHA + fs.mkdirSync(path.join('build'), { recursive: true }); + fs.writeFileSync(path.join('build', 'version.json'), JSON.stringify(sha)); +})(); diff --git a/scripts/config-from-env.js b/scripts/config-from-env.js new file mode 100644 index 000000000..7892a62cb --- /dev/null +++ b/scripts/config-from-env.js @@ -0,0 +1,67 @@ +// scripts/config-from-env.js +const fs = require('fs'); +const path = require('path'); +const dotenv = require('dotenv'); + +dotenv.config({ path: path.resolve(process.cwd(), '.env.local') }); + +const placeholderPath = path.resolve('src', 'config', 'config.placeholder.ts'); +const targetPath = path.resolve('src', 'config', 'index.ts'); + +if (!fs.existsSync(placeholderPath)) { + console.error(`Nu găsesc ${placeholderPath}. Verifică calea.`); + process.exit(1); +} + +let content = fs.readFileSync(placeholderPath, 'utf8'); + +// Token-urile suportate (completează dacă apar altele) +const ALL_KEYS = [ + 'START_NAME_STOP', + 'START_CHAIN_ID_STOP', + 'START_EGLD_LABEL_STOP', + 'START_WALLET_ADDRESS_STOP', + 'START_EXPLORER_ADDRESS_STOP', + 'START_NFT_EXPLORER_ADDRESS_STOP', + 'START_API_ADDRESS_STOP', + 'START_IS_SOVEREIGN_STOP' +]; + +// Chei booleene care trebuie injectate ca true/false fără ghilimele +const BOOL_KEYS = [ + 'START_IS_SOVEREIGN_STOP' +]; + +const STR_KEYS = ALL_KEYS.filter(k => !BOOL_KEYS.includes(k)); + +function parseBool(v) { + if (v == null) return false; + const s = String(v).trim().toLowerCase(); + if (['1', 'true', 'yes', 'y', 'on'].includes(s)) return true; + if (['0', 'false', 'no', 'n', 'off'].includes(s)) return false; + // fallback: orice altceva non-empty => true + return Boolean(s); +} + +// 1) Booleene: înlocuiește 'TOKEN' / "TOKEN" cu true/false (fără ghilimele) +// și acoperă și cazul când TOKEN apare ne-ghilimat. +for (const k of BOOL_KEYS) { + if (!(k in process.env)) continue; + const b = parseBool(process.env[k]); + // quote-agnostic: 'TOKEN' sau "TOKEN" + const rxQuoted = new RegExp(`(['"])${k}\\1`, 'g'); + content = content.replace(rxQuoted, String(b)); + // fallback: TOKEN fără ghilimele + content = content.split(k).join(String(b)); +} + +// 2) Stringuri: înlocuire textuală; dacă placeholderul are ghilimele, +// valoarea rămâne string valid în TS. +for (const k of STR_KEYS) { + if (!(k in process.env)) continue; + const v = process.env[k] ?? ''; + content = content.split(k).join(v); +} + +fs.writeFileSync(targetPath, content); +console.log(`Config generat în ${path.relative(process.cwd(), targetPath)} din .env.local`); diff --git a/scripts/start.js b/scripts/start.js new file mode 100644 index 000000000..43d5bd0f8 --- /dev/null +++ b/scripts/start.js @@ -0,0 +1,82 @@ +// scripts/start.js +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +require('dotenv').config({ path: path.resolve(process.cwd(), '.env.local') }); + +// 1) Cache-bust cu SHA +let sha = 'local'; +try { + sha = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) + .toString() + .trim(); +} catch {} +process.env.VITE_APP_CACHE_BUST = process.env.VITE_APP_CACHE_BUST || sha; +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +// 2) Permite override prin CLI: --share-prefix= --ga= --staging=true +for (const a of process.argv.slice(2)) { + if (a.startsWith('--share-prefix=')) process.env.VITE_APP_SHARE_PREFIX = a.split('=')[1]; + else if (a.startsWith('--ga=')) process.env.VITE_APP_GA_ID = a.split('=')[1]; + else if (a.startsWith('--staging=')) process.env.VITE_APP_IS_STAGING = a.split('=')[1]; +} + +// 3) Construiește map-ul pentru token-urile START_* (din .env.local) +const startTokens = Object.fromEntries( + Object.entries(process.env) + .filter(([k]) => k.startsWith('START_')) + .map(([k, v]) => [k, v ?? '']) +); + +// Plugin simplu care înlocuiește token-urile în sursele TS/JS/TSX/JSX +function tokenReplacePlugin() { + const exts = /\.(ts|tsx|js|jsx)$/i; + const keys = Object.keys(startTokens); + if (!keys.length) return { name: 'token-replace', transform: code => ({ code, map: null }) }; + + return { + name: 'token-replace', + enforce: 'pre', + transform(code, id) { + if (!exts.test(id)) return null; + let out = code; + for (const k of keys) { + // Înlocuire textuală; dacă în cod e "START_XXX", îl schimbăm în stringul real + const val = JSON.stringify(startTokens[k]); + out = out.split(k).join(val); + } + return { code: out, map: null }; + }, + // Înlocuire și în index.html + transformIndexHtml(html) { + let out = html; + for (const k of keys) { + out = out.split(k).join(startTokens[k] ?? ''); + } + return out; + } + }; +} + +(async () => { + try { + const { createServer } = require('vite'); + + const server = await createServer({ + plugins: [tokenReplacePlugin()], + }); + + await server.listen(); + server.printUrls(); + + const shutdown = async () => { + try { await server.close(); } catch {} + process.exit(0); + }; + process.on('SIGINT', shutdown); + process.on('SIGTERM', shutdown); + } catch (err) { + console.error('Failed to start Vite dev server:', err); + process.exit(1); + } +})(); diff --git a/scripts/version-make.js b/scripts/version-make.js new file mode 100644 index 000000000..ff92bb2e9 --- /dev/null +++ b/scripts/version-make.js @@ -0,0 +1,11 @@ +// scripts/version-make.js +const { execSync } = require('child_process'); +const fs = require('fs'); +let sha = 'local'; +try { + sha = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) + .toString() + .trim(); +} catch {} +fs.mkdirSync('build', { recursive: true }); +fs.writeFileSync('build/version.json', JSON.stringify(sha)); diff --git a/src/config/config.placeholder.ts b/src/config/config.placeholder.ts index 0e5dac280..40a14c458 100644 --- a/src/config/config.placeholder.ts +++ b/src/config/config.placeholder.ts @@ -19,6 +19,7 @@ export const networks: NetworkType[] = [ explorerAddress: 'START_EXPLORER_ADDRESS_STOP', nftExplorerAddress: 'START_NFT_EXPLORER_ADDRESS_STOP', isSovereign: 'START_IS_SOVEREIGN_STOP', + accessToken: 'START_IS_ACCESSTOKEN_STOP', apiAddress: 'START_API_ADDRESS_STOP' }, diff --git a/yarn.lock b/yarn.lock index fa733affe..af1b4405a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3703,6 +3703,11 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dotenv@^17.2.1: + version "17.2.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" + integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== + draco3d@^1.4.1: version "1.5.7" resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.7.tgz#94f9bce293eb8920c159dc91a4ce9124a9e899e0"