Skip to content

Commit dd426e0

Browse files
astrosnatpwdel
andauthored
Update Script to Build Vite with Host On The Fly from .env File (#496)
* checkpoint for vite added today's checkpoint * Adding vite template Grok Code decided to make a template. Let's see how well this actually works. * Update build_prod.sh VIBE CODED WITH GROK. TESTED. ALMOST CERTAINLY NEEDS EDITS. * Update env_writer_prod.sh Grok changed this. VIBE CODED, TEST BEFORE DEPLOYMENT ALWAYS * Attempting template fix. --------- Co-authored-by: Patrick Delaney <patrick.del@gmail.com>
1 parent b6af20d commit dd426e0

File tree

5 files changed

+351
-0
lines changed

5 files changed

+351
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
Here's a project outline that should solve this.
2+
3+
Project: Deterministic Vite allowedHosts Integration with Deploy Scripts
4+
Objective
5+
6+
Update SocialPredict’s frontend build system so that Vite’s server.allowedHosts and preview.allowedHosts are automatically populated from the .env file and deploy scripts, rather than being hard-coded. This ensures:
7+
8+
Consistency: the same domain used in SSL certs and Nginx is also whitelisted in Vite.
9+
10+
Determinism: hostnames are set from .env and deploy scripts, not manually or via CLI flags.
11+
12+
Security: production stays locked down to the configured domain(s), while development has sensible defaults (localhost, 127.0.0.1, frontend).
13+
14+
Implementation Plan
15+
1. Create a Vite Config Template
16+
17+
Add a new template file at frontend/vite.config.mjs.template:
18+
19+
// frontend/vite.config.mjs.template
20+
import { defineConfig } from 'vite'
21+
import react from '@vitejs/plugin-react'
22+
23+
// Parse comma-separated host lists from env
24+
const parseHosts = (s) =>
25+
(s || '')
26+
.split(',')
27+
.map(h => h.trim())
28+
.filter(Boolean)
29+
30+
const DEV_ALLOWED = parseHosts(process.env.VITE_DEV_ALLOWED_HOSTS)
31+
const PROD_ALLOWED = parseHosts(process.env.VITE_PROD_ALLOWED_HOSTS)
32+
33+
export default defineConfig(({ mode }) => {
34+
const isProd = mode === 'production'
35+
const allowed = isProd ? PROD_ALLOWED : DEV_ALLOWED
36+
37+
return {
38+
server: {
39+
host: '0.0.0.0',
40+
allowedHosts: allowed.length ? allowed : ['localhost', '127.0.0.1', 'frontend'],
41+
},
42+
preview: {
43+
allowedHosts: allowed.length ? allowed : ['localhost'],
44+
},
45+
build: {
46+
outDir: 'build',
47+
commonjsOptions: { transformMixedEsModules: true },
48+
},
49+
plugins: [react()],
50+
css: {
51+
target: 'async',
52+
},
53+
}
54+
})
55+
56+
2. Update Deploy Scripts to Stamp Template
57+
prod/build_prod.sh
58+
59+
Add a function to generate vite.config.mjs:
60+
61+
frontend_vite_allowed_hosts() {
62+
local template="$SCRIPT_DIR/frontend/vite.config.mjs.template"
63+
local file="$SCRIPT_DIR/frontend/vite.config.mjs"
64+
65+
# Ensure env vars are set
66+
export VITE_DEV_ALLOWED_HOSTS="${VITE_DEV_ALLOWED_HOSTS:-localhost,127.0.0.1,frontend}"
67+
export VITE_PROD_ALLOWED_HOSTS="${VITE_PROD_ALLOWED_HOSTS:-$DOMAIN,www.$DOMAIN}"
68+
69+
envsubst < "$template" > "$file"
70+
}
71+
72+
build_frontend() {
73+
echo "### Building Frontend Image ..."
74+
frontend_api_uri
75+
frontend_vite_allowed_hosts
76+
# existing build logic...
77+
}
78+
79+
dev/build_dev.sh
80+
81+
Add a dev version:
82+
83+
frontend_vite_allowed_hosts_dev() {
84+
local template="$SCRIPT_DIR/frontend/vite.config.mjs.template"
85+
local file="$SCRIPT_DIR/frontend/vite.config.mjs"
86+
87+
export VITE_DEV_ALLOWED_HOSTS="${VITE_DEV_ALLOWED_HOSTS:-localhost,127.0.0.1,frontend}"
88+
export VITE_PROD_ALLOWED_HOSTS="${VITE_PROD_ALLOWED_HOSTS:-}"
89+
90+
envsubst < "$template" > "$file"
91+
}
92+
93+
build_frontend() {
94+
echo "### Building Frontend Image ..."
95+
frontend_vite_allowed_hosts_dev
96+
# existing build logic...
97+
}
98+
99+
3. Extend .env Creation
100+
prod/env_writer_prod.sh
101+
102+
After setting DOMAIN, add:
103+
104+
# Add VITE_PROD_ALLOWED_HOSTS and VITE_DEV_ALLOWED_HOSTS if not present
105+
if ! grep -q "^VITE_PROD_ALLOWED_HOSTS=" .env; then
106+
echo "VITE_PROD_ALLOWED_HOSTS='${domain_answer},www.${domain_answer}'" >> .env
107+
fi
108+
109+
if ! grep -q "^VITE_DEV_ALLOWED_HOSTS=" .env; then
110+
echo "VITE_DEV_ALLOWED_HOSTS='localhost,127.0.0.1,frontend'" >> .env
111+
fi
112+
113+
dev/env_writer_dev.sh
114+
115+
Append default dev hosts:
116+
117+
if ! grep -q "^VITE_DEV_ALLOWED_HOSTS=" "$SCRIPT_DIR/.env"; then
118+
echo "VITE_DEV_ALLOWED_HOSTS='localhost,127.0.0.1,frontend'" >> "$SCRIPT_DIR/.env"
119+
fi
120+
121+
Result
122+
123+
.env deterministically defines which hosts are allowed.
124+
125+
Production automatically whitelists ${DOMAIN},www.${DOMAIN}.
126+
127+
Development defaults to localhost + Docker service name.
128+
129+
No need for ad-hoc CLI args or manual editing.
130+
131+
Next Steps
132+
133+
Implement the above changes in frontend/, scripts/prod/, and scripts/dev/.
134+
135+
Test locally: run ./SocialPredict dev and confirm Vite allows localhost + frontend.
136+
137+
Test prod: run ./SocialPredict prod, verify .env populates VITE_PROD_ALLOWED_HOSTS, and that the frontend only accepts traffic from your configured domain(s).
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
Template PROD allowedHosts in Vite using DOMAIN (plus optional PROD_EXTRA_ALLOWED) from your deploy .env.
2+
3+
Keep DEV allowedHosts static.
4+
5+
Bake the rendered vite.config.mjs into the prod image during ./SocialPredict install (prod).
6+
7+
No Dockerfile edits needed (render happens before docker build).
8+
9+
🗂️ File Changes
10+
1) frontend/vite.config.mjs.template
11+
import { defineConfig } from 'vite'
12+
import react from '@vitejs/plugin-react'
13+
14+
// DEV stays fixed — no templating
15+
const DEV_ALLOWED = ['frontend', 'localhost', '127.0.0.1']
16+
17+
// PROD is injected at build-time by scripts/prod/env_writer_prod.sh
18+
// It becomes a concrete JS array literal like: ["brierfoxforecast.com", "www.brierfoxforecast.com"]
19+
const PROD_ALLOWED = [__PROD_ALLOWED_HOSTS__]
20+
21+
export default defineConfig(({ mode }) => {
22+
const isProd = mode === 'production'
23+
const allowed = isProd ? PROD_ALLOWED.filter(Boolean) : DEV_ALLOWED
24+
25+
return {
26+
server: {
27+
host: '0.0.0.0',
28+
allowedHosts: allowed,
29+
},
30+
preview: { allowedHosts: allowed },
31+
build: {
32+
outDir: 'build',
33+
commonjsOptions: { transformMixedEsModules: true },
34+
},
35+
plugins: [react()],
36+
css: { target: 'async' },
37+
}
38+
})
39+
40+
2) scripts/prod/env_writer_prod.sh
41+
#!/usr/bin/env bash
42+
set -euo pipefail
43+
44+
# Render PROD allowedHosts into frontend/vite.config.mjs from the template,
45+
# using DOMAIN (and optional PROD_EXTRA_ALLOWED) from your deploy env file.
46+
47+
# Usage:
48+
# scripts/prod/env_writer_prod.sh /absolute/path/to/.env
49+
# # or rely on ENV_PATH env var:
50+
# ENV_PATH=/root/socialpredict-deploy/.env scripts/prod/env_writer_prod.sh
51+
52+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
53+
TPL="${ROOT_DIR}/frontend/vite.config.mjs.template"
54+
OUT="${ROOT_DIR}/frontend/vite.config.mjs"
55+
56+
ENV_FILE="${1:-${ENV_PATH:-}}"
57+
if [[ -z "${ENV_FILE}" || ! -f "${ENV_FILE}" ]]; then
58+
echo "ERROR: Supply path to deploy .env (arg #1) or set ENV_PATH to a valid file." >&2
59+
exit 1
60+
fi
61+
62+
# shellcheck disable=SC1090
63+
. "${ENV_FILE}"
64+
65+
: "${DOMAIN:?DOMAIN must be set in ${ENV_FILE}}"
66+
PROD_EXTRA_ALLOWED="${PROD_EXTRA_ALLOWED:-}"
67+
68+
# Build host list: apex + www + extras (comma/space separated), dedup
69+
join_unique_csv() {
70+
echo "$1" | tr ' ,' '\n' | sed '/^$/d' | awk '!seen[$0]++' | paste -sd',' -
71+
}
72+
73+
BASE="${DOMAIN},www.${DOMAIN}"
74+
ALL_CSV="$(join_unique_csv "${BASE},${PROD_EXTRA_ALLOWED}")"
75+
76+
# CSV -> "a","b","c"
77+
JS_ITEMS="$(awk -v csv="$ALL_CSV" 'BEGIN{
78+
n=split(csv, a, /,/);
79+
for(i=1;i<=n;i++){gsub(/^ +| +$/,"",a[i]); if(a[i]!=""){printf "\"%s\"%s", a[i], (i<n?", ":"")}}
80+
}')"
81+
82+
mkdir -p "$(dirname "$OUT")"
83+
sed -e "s|__PROD_ALLOWED_HOSTS__|${JS_ITEMS}|g" "$TPL" > "$OUT"
84+
85+
echo "Rendered ${OUT}"
86+
echo "PROD allowedHosts => [${JS_ITEMS}]"
87+
88+
3) scripts/prod/build_prod.sh
89+
#!/usr/bin/env bash
90+
set -euo pipefail
91+
92+
# Build the PROD frontend image with rendered allowedHosts.
93+
# Reads DOMAIN/PROD_EXTRA_ALLOWED from the same deploy .env you already use.
94+
#
95+
# Usage:
96+
# scripts/prod/build_prod.sh /absolute/path/to/.env
97+
#
98+
# Requires FRONTEND_IMAGE_NAME in your .env (e.g., socialpredict-frontend).
99+
100+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
101+
ENV_FILE="${1:-${ENV_PATH:-}}"
102+
103+
if [[ -z "${ENV_FILE}" || ! -f "${ENV_FILE}" ]]; then
104+
echo "ERROR: Supply path to deploy .env (arg #1) or set ENV_PATH to a valid file." >&2
105+
exit 1
106+
fi
107+
108+
# shellcheck disable=SC1090
109+
. "${ENV_FILE}"
110+
111+
: "${FRONTEND_IMAGE_NAME:?FRONTEND_IMAGE_NAME must be set in ${ENV_FILE}}"
112+
113+
# 1) Render vite.config.mjs for PROD
114+
"${ROOT_DIR}/scripts/prod/env_writer_prod.sh" "${ENV_FILE}"
115+
116+
# 2) Build the image; since vite.config.mjs is rendered in the build context,
117+
# your Dockerfile's "npm run build" will pick it up automatically.
118+
docker build \
119+
-t "${FRONTEND_IMAGE_NAME}:prod" \
120+
"${ROOT_DIR}/frontend"
121+
122+
echo "Built ${FRONTEND_IMAGE_NAME}:prod"

frontend/vite.config.mjs.template

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { defineConfig } from 'vite'
2+
import react from '@vitejs/plugin-react'
3+
4+
// DEV stays fixed — no templating
5+
const DEV_ALLOWED = ['frontend', 'localhost', '127.0.0.1']
6+
7+
// PROD is injected at build-time by scripts/prod/env_writer_prod.sh
8+
// It becomes a concrete JS array literal like: ["brierfoxforecast.com", "www.brierfoxforecast.com"]
9+
const PROD_ALLOWED = [__PROD_ALLOWED_HOSTS__]
10+
11+
export default defineConfig(({ mode }) => {
12+
const isProd = mode === 'production'
13+
const allowed = isProd ? PROD_ALLOWED.filter(Boolean) : DEV_ALLOWED
14+
15+
return {
16+
server: {
17+
host: '0.0.0.0',
18+
allowedHosts: allowed,
19+
},
20+
preview: { allowedHosts: allowed },
21+
build: {
22+
outDir: 'build',
23+
commonjsOptions: { transformMixedEsModules: true },
24+
},
25+
plugins: [react()],
26+
css: { target: 'async' },
27+
}
28+
})

scripts/prod/build_prod.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@ frontend_api_uri() {
1515
envsubst < $template > $file
1616
}
1717

18+
# Function to generate vite.config.mjs from template using the new templating system
19+
frontend_vite_allowed_hosts() {
20+
# Source the render function from env_writer_prod.sh
21+
local env_writer_script="$SCRIPT_DIR/scripts/prod/env_writer_prod.sh"
22+
# shellcheck disable=SC1090
23+
source "$env_writer_script"
24+
25+
# Call the render function with the .env file path
26+
render_vite_config_prod "$SCRIPT_DIR/.env"
27+
}
28+
1829
# Function to build frontend image
1930
build_frontend() {
2031
echo "### Building Frontend Image ..."
2132

2233
# Update API_URI
2334
frontend_api_uri
2435

36+
# Generate vite.config.mjs
37+
frontend_vite_allowed_hosts
38+
2539
# Get frontend directory
2640
FRONTEND_DIR="$( readlink -f "$SCRIPT_DIR/frontend" )"
2741

scripts/prod/env_writer_prod.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,47 @@ check_username_strength() {
2424
fi
2525
}
2626

27+
# Render PROD allowedHosts into frontend/vite.config.mjs from the template,
28+
# using DOMAIN (and optional PROD_EXTRA_ALLOWED) from your deploy env file.
29+
render_vite_config_prod() {
30+
local env_file="${1:-${ENV_PATH:-}}"
31+
32+
if [[ -z "${env_file}" || ! -f "${env_file}" ]]; then
33+
echo "ERROR: Supply path to deploy .env (arg #1) or set ENV_PATH to a valid file." >&2
34+
return 1
35+
fi
36+
37+
local root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
38+
local tpl="${root_dir}/frontend/vite.config.mjs.template"
39+
local out="${root_dir}/frontend/vite.config.mjs"
40+
41+
# shellcheck disable=SC1090
42+
. "${env_file}"
43+
44+
: "${DOMAIN:?DOMAIN must be set in ${env_file}}"
45+
local prod_extra_allowed="${PROD_EXTRA_ALLOWED:-}"
46+
47+
# Build host list: apex + www + extras (comma/space separated), dedup
48+
join_unique_csv() {
49+
echo "$1" | tr ' ,' '\n' | sed '/^$/d' | awk '!seen[$0]++' | paste -sd',' -
50+
}
51+
52+
local base="${DOMAIN},www.${DOMAIN}"
53+
local all_csv="$(join_unique_csv "${base},${prod_extra_allowed}")"
54+
55+
# CSV -> "a","b","c"
56+
local js_items="$(awk -v csv="$all_csv" 'BEGIN{
57+
n=split(csv, a, /,/);
58+
for(i=1;i<=n;i++){gsub(/^ +| +$/,"",a[i]); if(a[i]!=""){printf "\"%s\"%s", a[i], (i<n?", ":"")}}
59+
}')"
60+
61+
mkdir -p "$(dirname "$out")"
62+
sed -e "s|__PROD_ALLOWED_HOSTS__|${js_items}|g" "$tpl" > "$out"
63+
64+
echo "Rendered ${out}"
65+
echo "PROD allowedHosts => [${js_items}]"
66+
}
67+
2768
# Function to create and update .env file
2869
init_env() {
2970
# Create .env file
@@ -48,6 +89,15 @@ init_env() {
4889

4990
echo
5091

92+
# Add VITE_PROD_ALLOWED_HOSTS and VITE_DEV_ALLOWED_HOSTS if not present
93+
if ! grep -q "^VITE_PROD_ALLOWED_HOSTS=" .env; then
94+
echo "VITE_PROD_ALLOWED_HOSTS='${domain_answer},www.${domain_answer}'" >> .env
95+
fi
96+
97+
if ! grep -q "^VITE_DEV_ALLOWED_HOSTS=" .env; then
98+
echo "VITE_DEV_ALLOWED_HOSTS='localhost,127.0.0.1,frontend'" >> .env
99+
fi
100+
51101
# Update email address
52102
read -r -p "What email address do you wish to use for the SSL Certificate? " email_answer
53103
while [ -z "$email_answer" ]

0 commit comments

Comments
 (0)