diff --git a/Dockerfile b/Dockerfile index 44da2036aa..5b4116690f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -81,7 +81,7 @@ RUN yarn build # ***************************** # Production image, copy all the files and run next FROM node:18-alpine AS runner -RUN apk add --no-cache --upgrade bash +RUN apk add --no-cache --upgrade bash curl jq unzip ### APP WORKDIR /app @@ -98,9 +98,18 @@ COPY --from=builder /app/package.json ./package.json COPY --from=builder /envs-validator/index.js ./envs-validator.js COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-reporter.js -# Copy scripts and ENVs file +# Copy scripts +## Entripoint COPY --chmod=+x ./deploy/scripts/entrypoint.sh . +## ENV replacer COPY --chmod=+x ./deploy/scripts/replace_envs.sh . +## Favicon generator +COPY --chmod=+x ./deploy/scripts/favicon_generator.sh . +COPY ./deploy/tools/favicon-generator ./deploy/tools/favicon-generator +RUN ["chmod", "-R", "777", "./deploy/tools/favicon-generator"] +RUN ["chmod", "-R", "777", "./public"] + +# Copy ENVs files COPY --from=builder /app/.env.production . COPY --from=builder /app/.env . diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh index 7ba02e4f9a..078f7ba419 100755 --- a/deploy/scripts/entrypoint.sh +++ b/deploy/scripts/entrypoint.sh @@ -6,6 +6,14 @@ if [ $? != 0 ]; then echo 🛑 ENV integrity check failed. 1>&2 && exit 1 fi +# Generate favicons bundle +./favicon_generator.sh +if [ $? -ne 0 ]; then + echo "👎 Unable to generate favicons bundle." +else + echo "👍 Favicons bundle successfully generated." +fi + # Execute script for replace build-time ENVs placeholders with their values at runtime ./replace_envs.sh diff --git a/deploy/scripts/favicon_generator.sh b/deploy/scripts/favicon_generator.sh new file mode 100755 index 0000000000..884b515cd7 --- /dev/null +++ b/deploy/scripts/favicon_generator.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +master_url="${NEXT_PUBLIC_FAVICON_MASTER_URL:-$NEXT_PUBLIC_NETWORK_ICON}" +export MASTER_URL="$master_url" + +cd ./deploy/tools/favicon-generator +./script.sh +if [ $? -ne 0 ]; then + cd ../../../ + exit 1 +else + cd ../../../ + favicon_folder="./public/favicon/" + + echo "⏳ Replacing default favicons with freshly generated pack..." + if [ -d "$favicon_folder" ]; then + rm -r "$favicon_folder" + fi + mkdir -p "$favicon_folder" + cp -r ./deploy/tools/favicon-generator/output/* "$favicon_folder" +fi \ No newline at end of file diff --git a/deploy/tools/favicon-generator/.gitignore b/deploy/tools/favicon-generator/.gitignore new file mode 100755 index 0000000000..09af2dfe51 --- /dev/null +++ b/deploy/tools/favicon-generator/.gitignore @@ -0,0 +1,4 @@ +/output +config.json +response.json +favicon_package** \ No newline at end of file diff --git a/deploy/tools/favicon-generator/config.template.json b/deploy/tools/favicon-generator/config.template.json new file mode 100755 index 0000000000..4d032e8bb9 --- /dev/null +++ b/deploy/tools/favicon-generator/config.template.json @@ -0,0 +1,41 @@ +{ + "favicon_generation": { + "api_key": "", + "master_picture": { + "type": "url", + "url": "" + }, + "files_location": { + "type": "path", + "path": "/favicons" + }, + "favicon_design": { + "desktop_browser": {}, + "ios": { + "picture_aspect": "no_change", + "assets": { + "ios6_and_prior_icons": false, + "ios7_and_later_icons": true, + "precomposed_icons": false, + "declare_only_default_icon": true + } + }, + "safari_pinned_tab": { + "picture_aspect": "black_and_white", + "threshold": 60 + } + }, + "settings": { + "compression": "3", + "scaling_algorithm": "Mitchell", + "error_on_image_too_small": true, + "readme_file": false, + "html_code_file": false, + "use_path_as_is": false + }, + "versioning": { + "param_name": "ver", + "param_value": "15Zd8" + } + } +} \ No newline at end of file diff --git a/deploy/tools/favicon-generator/script.sh b/deploy/tools/favicon-generator/script.sh new file mode 100755 index 0000000000..098e336b8f --- /dev/null +++ b/deploy/tools/favicon-generator/script.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +echo "🌀 Generating favicons bundle..." + +# Check if MASTER_URL is provided +if [ -z "$MASTER_URL" ]; then + echo "🛑 Error: MASTER_URL variable is not provided." + exit 1 +fi + +# Check if NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY is provided +if [ -z "$NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY" ]; then + echo "🛑 Error: NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY variable is not provided." + exit 1 +fi + +# Mask the NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY to display only the first 8 characters +API_KEY_MASKED="${NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY:0:8}***" +echo "🆗 The following variables are provided:" +echo " MASTER_URL: $MASTER_URL" +echo " NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: $API_KEY_MASKED" +echo + +# RealFaviconGenerator API endpoint URL +API_URL="https://realfavicongenerator.net/api/favicon" + +# Target folder for the downloaded assets +TARGET_FOLDER="./output" + +# Path to the config JSON template file +CONFIG_TEMPLATE_FILE="config.template.json" + +# Path to the generated config JSON file +CONFIG_FILE="config.json" + +# Replace and placeholders in the JSON template file +API_KEY_VALUE="$NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY" +sed -e "s||$API_KEY_VALUE|" -e "s||$MASTER_URL|" "$CONFIG_TEMPLATE_FILE" > "$CONFIG_FILE" + +# Make the API POST request with JSON data from the config file +echo "⏳ Making request to API..." +API_RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" -d @"$CONFIG_FILE" "$API_URL") + +# Create the response.json file with the API response +echo "$API_RESPONSE" > response.json + +# Check if the API response is valid JSON and contains success status +if ! jq -e '.favicon_generation_result.result.status == "success"' <<< "$API_RESPONSE" >/dev/null; then + echo "🛑 Error: API response does not contain the expected structure or has an error status." + ERROR_MESSAGE=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.result.error_message' | tr -d '\\') + if [ -n "$ERROR_MESSAGE" ]; then + echo "🛑 $ERROR_MESSAGE" + fi + exit 1 +fi +echo "🆗 API responded with success status." + +# Parse the JSON response to extract the file URL and remove backslashes +FILE_URL=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.favicon.package_url' | tr -d '\\') +PREVIEW_URL=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.preview_picture_url' | tr -d '\\') + +# Check if FILE_URL is empty +if [ -z "$FILE_URL" ]; then + echo "🛑 File URL not found in JSON response." + exit 1 +fi + +echo "🆗 Found following file URL in the response: $FILE_URL" +echo "🆗 Favicon preview URL: $PREVIEW_URL" +echo + +# Generate a filename based on the URL +FILE_NAME=$(basename "$FILE_URL") + +# Check if the target folder exists and clear its contents if it does +if [ -d "$TARGET_FOLDER" ]; then + rm -r "$TARGET_FOLDER" +fi +mkdir -p "$TARGET_FOLDER" + +# Download the file +echo "⏳ Trying to download the file..." +curl -s -L "$FILE_URL" -o "$FILE_NAME" + +# Check if the download was successful +if [ $? -eq 0 ]; then + echo "🆗 File downloaded successfully." + echo +else + echo "🛑 Error: Failed to download the file." + exit 1 +fi + +# Unzip the downloaded file to the target folder +echo "⏳ Unzipping the file..." +unzip -q "$FILE_NAME" -d "$TARGET_FOLDER" + +# Check if the unzip operation was successful +if [ $? -eq 0 ]; then + echo "🆗 File unzipped successfully." + echo +else + echo "🛑 Failed to unzip the file." + exit 1 +fi + +# Clean up - remove the JSON response file and temporary JSON config file +rm response.json "$CONFIG_FILE" + +echo "✅ Done." \ No newline at end of file diff --git a/deploy/values/l2-optimism-goerli/values.yaml b/deploy/values/l2-optimism-goerli/values.yaml index 57417aa370..350de67450 100644 --- a/deploy/values/l2-optimism-goerli/values.yaml +++ b/deploy/values/l2-optimism-goerli/values.yaml @@ -200,3 +200,4 @@ frontend: envFromSecret: NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/l2-optimism-goerli?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/l2-optimism-goerli?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY diff --git a/deploy/values/main/values.yaml b/deploy/values/main/values.yaml index a1c91da22c..d7e01f4d26 100644 --- a/deploy/values/main/values.yaml +++ b/deploy/values/main/values.yaml @@ -166,3 +166,4 @@ frontend: NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY diff --git a/deploy/values/review-l2/values.yaml.gotmpl b/deploy/values/review-l2/values.yaml.gotmpl index 4033801926..d35fe7150f 100644 --- a/deploy/values/review-l2/values.yaml.gotmpl +++ b/deploy/values/review-l2/values.yaml.gotmpl @@ -24,6 +24,7 @@ frontend: - "/account" - "/apps" - "/static" + - "/favicon" - "/auth/profile" - "/auth/unverified-email" - "/txs" @@ -143,3 +144,5 @@ frontend: _default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: _default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: + _default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index 99f59705d2..0fd3ee5697 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -23,6 +23,7 @@ frontend: - "/account" - "/apps" - "/static" + - "/favicon" - "/auth/profile" - "/auth/unverified-email" - "/txs" @@ -77,6 +78,10 @@ frontend: _default: 'true' NEXT_PUBLIC_FEATURED_NETWORKS: _default: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json + NEXT_PUBLIC_NETWORK_LOGO: + _default: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/goerli.svg + NEXT_PUBLIC_NETWORK_ICON: + _default: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/goerli.svg NEXT_PUBLIC_API_HOST: _default: blockscout-main.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST: @@ -123,5 +128,7 @@ frontend: _default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: _default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: + _default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY NEXT_PUBLIC_WEB3_WALLETS: _default: "['token_pocket','coinbase','metamask']" diff --git a/docs/ENVS.md b/docs/ENVS.md index ff8e2d7ba6..1413c79ba0 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -12,6 +12,7 @@ The app instance could be customized by passing following variables to NodeJS en - [Homepage](ENVS.md#homepage) - [Sidebar](ENVS.md#sidebar) - [Footer](ENVS.md#footer) + - [Favicon](ENVS.md#favicon) - [Views](ENVS.md#views) - [Block](ENVS.md#block-views) - [Misc](ENVS.md#misc) @@ -132,6 +133,17 @@ The app version shown in the footer is derived from build-time ENV variables `NE   +### Favicon + +By default, the app has generic favicon. You can override this behavior by providing the following variables. Hence, the favicon assets bundle will be generated at the container start time and will be used instead of default one. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY | `string` | RealFaviconGenerator [API key](https://realfavicongenerator.net/api/) | Required | - | `` | +| NEXT_PUBLIC_FAVICON_MASTER_URL | `string` | - | - | `NEXT_PUBLIC_NETWORK_ICON` | `https://placekitten.com/180/180` | + +  + ### Views #### Block views diff --git a/package.json b/package.json index a62c6366e3..4fea2bf917 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.35.1-focal ./playwright/run-tests.sh", "test:pw:ci": "yarn test:pw --project=$PW_PROJECT", "test:jest": "jest", - "test:jest:watch": "jest --watch" + "test:jest:watch": "jest --watch", + "favicon:generate:dev": "./tools/scripts/favicon-generator.dev.sh" }, "dependencies": { "@chakra-ui/react": "2.7.1", diff --git a/pages/_document.tsx b/pages/_document.tsx index 9f594285c9..e6c0e263ae 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -30,6 +30,7 @@ class MyDocument extends Document { return ( + { /* FONTS */ } - - - - + + { /* FAVICON */ } + + + + + + + { /* OG TAGS */ } + + diff --git a/public/static/apple-touch-icon.png b/public/static/apple-touch-icon.png deleted file mode 100644 index e4c1f5def3..0000000000 Binary files a/public/static/apple-touch-icon.png and /dev/null differ diff --git a/public/static/favicon-16x16.png b/public/static/favicon-16x16.png deleted file mode 100644 index 7e14a5926f..0000000000 Binary files a/public/static/favicon-16x16.png and /dev/null differ diff --git a/public/static/favicon-32x32.png b/public/static/favicon-32x32.png deleted file mode 100644 index 0e6e9345ff..0000000000 Binary files a/public/static/favicon-32x32.png and /dev/null differ diff --git a/public/static/safari-pinned-tab.svg b/public/static/safari-pinned-tab.svg deleted file mode 100644 index 3a0295671c..0000000000 --- a/public/static/safari-pinned-tab.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tools/scripts/favicon-generator.dev.sh b/tools/scripts/favicon-generator.dev.sh new file mode 100755 index 0000000000..9d69088a8f --- /dev/null +++ b/tools/scripts/favicon-generator.dev.sh @@ -0,0 +1,19 @@ +secrets_file="./configs/envs/.env.secrets" +favicon_folder="./public/favicon/" +master_url="https://raw.githubusercontent.com/blockscout/frontend/main/tools/scripts/favicon.svg" + +if [ ! -f "$secrets_file" ]; then + echo "Error: File '$secrets_file' not found." + exit 1 +fi + +dotenv \ + -v MASTER_URL=$master_url \ + -e $secrets_file \ + -- bash -c 'cd ./deploy/tools/favicon-generator && ./script.sh' + +if [ -d "$favicon_folder" ]; then + rm -r "$favicon_folder" +fi +mkdir -p "$favicon_folder" +cp -r ./deploy/tools/favicon-generator/output/* "$favicon_folder" \ No newline at end of file diff --git a/tools/scripts/favicon.svg b/tools/scripts/favicon.svg new file mode 100644 index 0000000000..b69b047ffe --- /dev/null +++ b/tools/scripts/favicon.svg @@ -0,0 +1,3 @@ + + +