Skip to content

Commit d3b9617

Browse files
committed
feat: backend proxy feature
1 parent 5719db7 commit d3b9617

File tree

5 files changed

+79
-49
lines changed

5 files changed

+79
-49
lines changed

Dockerfile

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
# Build stage
2-
FROM --platform=linux/amd64 node:21-alpine AS build
2+
FROM node:21-alpine AS build
33
WORKDIR /app
44
COPY package.json package-lock.json ./
55
RUN npm ci --ignore-scripts --include=dev
66
COPY . .
7+
ENV NODE_ENV=production
78
RUN npm run build:prod
8-
RUN ls -l /app/dist && cat /app/dist/index.html # Debug build output
9+
RUN ls -l /app/dist && cat /app/dist/index.html
910

1011
# Production stage
11-
FROM --platform=linux/amd64 node:21-alpine AS production
12+
FROM nginx:alpine AS production
1213
WORKDIR /app
13-
ENV NODE_ENV=production
14-
RUN npm install -g --only=production http-server
15-
COPY --from=build /app/dist ./dist
16-
# Inject script tag
17-
RUN sed -i '/<head>/ s|<head>|<head>\n <script src="/config.js"></script>|' /app/dist/index.html && \
18-
cat /app/dist/index.html # Debug sed output
19-
# Create entrypoint script to inject config.js at runtime
20-
RUN echo '#!/bin/sh' > /app/entrypoint.sh && \
21-
echo 'if [ -z "$VITE_HOST_URL" ]; then' >> /app/entrypoint.sh && \
22-
echo ' echo "window.appConfig = { apiBaseUrl: \"\" };" > /app/dist/config.js' >> /app/entrypoint.sh && \
23-
echo 'else' >> /app/entrypoint.sh && \
24-
echo ' echo "window.appConfig = { apiBaseUrl: \"$VITE_HOST_URL\" };" > /app/dist/config.js' >> /app/entrypoint.sh && \
25-
echo 'fi' >> /app/entrypoint.sh && \
26-
echo 'cat /app/dist/config.js' >> /app/entrypoint.sh && \
27-
echo 'exec http-server ./dist -p 80 --spa' >> /app/entrypoint.sh && \
28-
chmod +x /app/entrypoint.sh
14+
COPY --from=build /app/dist /usr/share/nginx/html
15+
16+
# Copy Nginx template (not the final config)
17+
COPY nginx.conf.template /app/nginx.conf.template
18+
19+
# Copy entrypoint script
20+
COPY entrypoint.sh /app/entrypoint.sh
21+
22+
# Inject script tag for config.js
23+
RUN sed -i '/<head>/ s|<head>|<head>\n <script src="/config.js"></script>|' /usr/share/nginx/html/index.html && \
24+
cat /usr/share/nginx/html/index.html # Debug sed output
25+
26+
# Make entrypoint executable
27+
RUN chmod +x /app/entrypoint.sh
28+
2929
EXPOSE 80
30+
3031
ENTRYPOINT ["/app/entrypoint.sh"]

entrypoint.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
3+
if [ -z "$VITE_HOST_URL" ]; then
4+
echo "window.appConfig = { apiBaseUrl: \"\", proxy: false };" > /usr/share/nginx/html/config.js
5+
else
6+
echo "window.appConfig = { apiBaseUrl: \"$VITE_HOST_URL\", proxy: \"$PROXY\" };" > /usr/share/nginx/html/config.js
7+
fi
8+
9+
cat /usr/share/nginx/html/config.js
10+
11+
# Substitute environment variables into nginx.conf.template and output to default.conf
12+
envsubst '${VITE_HOST_URL}' < /app/nginx.conf.template > /etc/nginx/conf.d/default.conf
13+
14+
# Start Nginx
15+
nginx -g "daemon off;"

nginx.conf.template

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
server {
2+
listen 80;
3+
4+
root /usr/share/nginx/html;
5+
index index.html;
6+
7+
access_log /var/log/nginx/access.log;
8+
error_log /var/log/nginx/error.log;
9+
10+
location / {
11+
try_files $uri $uri/ /index.html;
12+
}
13+
14+
location /api/ {
15+
proxy_pass ${VITE_HOST_URL}/;
16+
proxy_set_header Host $host;
17+
proxy_set_header X-Real-IP $remote_addr;
18+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
19+
}
20+
}

src/utilities/getApiBaseUrl.utils.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
import { config } from "@src/config";
2-
31
export const getApiBaseUrl = (): string => {
4-
const buildUrl = (url: string): string => {
2+
const buildUrl = (url: string, useProxy: boolean): string => {
53
if (url.match(/^(http|https):\/\//)) {
6-
return url;
4+
return useProxy ? "/api" : url; // If proxying, return "/api"; otherwise, use the full URL
75
} else {
86
const path = url.startsWith("/") ? url.substring(1) : url;
97
return `${window.location.origin}/${path}`;
108
}
119
};
1210

13-
if (config?.apiBaseUrl) {
14-
return buildUrl(config.apiBaseUrl);
11+
// Check runtime config first (injected via config.js)
12+
if (window.appConfig?.apiBaseUrl) {
13+
const useProxy = window.appConfig.proxy === "true" || window.appConfig.proxy === true;
14+
return buildUrl(window.appConfig.apiBaseUrl, useProxy);
1515
}
1616

17+
// Fallback to Vite env var (for dev or if config.js isn’t set)
1718
const hostUrl = import.meta.env.VITE_HOST_URL;
1819
if (hostUrl) {
19-
return buildUrl(hostUrl);
20+
return buildUrl(hostUrl, false); // Default to no proxy for Vite env
2021
}
21-
const { hostname } = window.location;
2222

23+
// Fallback logic based on hostname
24+
const { hostname } = window.location;
2325
if (hostname === "app.autokitteh.cloud") {
2426
return "https://api.autokitteh.cloud";
2527
}

vite.config.ts

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default defineConfig({
4242
"import.meta.env.VITE_NODE_ENV": JSON.stringify(process.env.VITE_NODE_ENV),
4343
"import.meta.env.VITE_DESCOPE_PROJECT_ID": JSON.stringify(process.env.VITE_DESCOPE_PROJECT_ID),
4444
"import.meta.env.GOOGLE_ANALYTICS_ID": JSON.stringify(process.env.GOOGLE_ANALYTICS_ID),
45-
"import.meta.env.VITE_HOST_URL": JSON.stringify(process.env.VITE_HOST_URL),
45+
// Remove VITE_HOST_URL if runtime injection is the only source
4646
"import.meta.env.DISPLAY_DISCORD_INTEGRATION": process.env.DISPLAY_DISCORD_INTEGRATION,
4747
"import.meta.env.DISPLAY_SLACK_SOCKET_INTEGRATION": process.env.DISPLAY_SLACK_SOCKET_INTEGRATION,
4848
"import.meta.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN),
@@ -85,13 +85,8 @@ export default defineConfig({
8585
cleanupIDs: false,
8686
removeUselessStrokeAndFill: false,
8787
removeUnknownsAndDefaults: false,
88-
convertPathData: {
89-
floatPrecision: 2,
90-
transformPrecision: 4,
91-
},
92-
cleanupNumericValues: {
93-
floatPrecision: 2,
94-
},
88+
convertPathData: { floatPrecision: 2, transformPrecision: 4 },
89+
cleanupNumericValues: { floatPrecision: 2 },
9590
collapseGroups: true,
9691
mergePaths: true,
9792
convertTransform: true,
@@ -109,18 +104,9 @@ export default defineConfig({
109104
}),
110105
viteStaticCopy({
111106
targets: [
112-
{
113-
src: "src/assets/templates/**/*",
114-
dest: "assets/templates",
115-
},
116-
{
117-
src: "src/assets/new_project_program/**/*",
118-
dest: "assets/new_project_program",
119-
},
120-
{
121-
src: "src/assets/image/pages/**/*",
122-
dest: "assets/image/pages",
123-
},
107+
{ src: "src/assets/templates/**/*", dest: "assets/templates" },
108+
{ src: "src/assets/new_project_program/**/*", dest: "assets/new_project_program" },
109+
{ src: "src/assets/image/pages/**/*", dest: "assets/image/pages" },
124110
],
125111
}),
126112
reactVirtualized(),
@@ -151,9 +137,15 @@ export default defineConfig({
151137
"tailwind-config": path.resolve(__dirname, "./tailwind.config.cjs"),
152138
},
153139
},
154-
155140
server: {
156141
port: 8000,
157-
allowedHosts: true,
142+
host: true,
143+
proxy: {
144+
"/api": {
145+
target: process.env.VITE_HOST_URL || "http://localhost:9980",
146+
changeOrigin: true,
147+
rewrite: (path) => path.replace(/^\/api/, ""),
148+
},
149+
},
158150
},
159151
});

0 commit comments

Comments
 (0)