Skip to content

Commit 08dce5a

Browse files
committed
Support sub-path deployment for Docker and Vite
1 parent 87f7504 commit 08dce5a

File tree

6 files changed

+121
-9
lines changed

6 files changed

+121
-9
lines changed

.env.sample

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
VITE_BACKEND_URL=http://backend.com
1+
VITE_BACKEND_URL=http://backend.com
2+
# Uncomment to build for a sub-path (defaults to /)
3+
# VITE_BASE_PATH=/drawdb

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ COPY package*.json ./
55
RUN npm ci
66
COPY . .
77
ENV NODE_OPTIONS="--max-old-space-size=4096"
8+
ENV VITE_BASE_PATH=__BASE_PATH__
89
RUN npm run build
910

1011
# Stage 2: Setup the Nginx Server to serve the app
1112
FROM docker.io/library/nginx:stable-alpine3.17 AS production
1213
COPY --from=build /app/dist /usr/share/nginx/html
13-
RUN echo 'server { listen 80; server_name _; root /usr/share/nginx/html; location / { try_files $uri /index.html; } }' > /etc/nginx/conf.d/default.conf
14+
COPY docker-entrypoint.sh /docker-entrypoint.sh
15+
RUN chmod +x /docker-entrypoint.sh && rm /etc/nginx/conf.d/default.conf
1416
EXPOSE 80
17+
ENTRYPOINT ["/docker-entrypoint.sh"]
1518
CMD ["nginx", "-g", "daemon off;"]

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,15 @@ docker build -t drawdb .
6464
docker run -p 3000:80 drawdb
6565
```
6666

67+
To serve the app from a sub-path, set the deployment context at runtime. Replace `/drawdb` with your desired base path.
68+
69+
```bash
70+
docker run -p 3000:80 -e DRAWDB_BASE_PATH=/drawdb drawdb
71+
```
72+
73+
### Deployment Context
74+
75+
- `VITE_BASE_PATH` (build-time) controls the base URL when building outside of Docker. Set it before running `npm run build`, for example `VITE_BASE_PATH=/drawdb npm run build`. The default `/` serves the app at the domain root. The Docker image sets a placeholder during build so you can usually leave this unset.
76+
- `DRAWDB_BASE_PATH` (runtime, Docker only) updates the pre-built assets and Nginx routing when the container starts. It must match the path used at build time (default `/`). Provide a relative path such as `/drawdb`.
77+
6778
If you want to enable sharing, set up the [server](https://github.com/drawdb-io/drawdb-server) and environment variables according to `.env.sample`. This is optional unless you need to share files..

docker-entrypoint.sh

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
CONTENT_DIR="/usr/share/nginx/html"
5+
# Allow overriding the served base path at runtime (default /)
6+
BASE_PATH="${DRAWDB_BASE_PATH:-/}"
7+
8+
if [ -z "$BASE_PATH" ]; then
9+
BASE_PATH="/"
10+
fi
11+
12+
# Reject absolute URLs; only relative paths are supported
13+
case "$BASE_PATH" in
14+
http://*|https://*)
15+
echo "DRAWDB_BASE_PATH must be a relative path (e.g. /drawdb)" >&2
16+
exit 1
17+
;;
18+
esac
19+
20+
# Ensure the path has a single leading slash and no trailing slash
21+
if [ "$BASE_PATH" != "/" ]; then
22+
BASE_PATH="/${BASE_PATH#/}"
23+
BASE_PATH="${BASE_PATH%/}"
24+
fi
25+
26+
# Derive helper variants used by the templated replacements below
27+
if [ "$BASE_PATH" = "/" ]; then
28+
BASE_PATH_WITH_SLASH="/"
29+
BASE_PATH_NO_LEAD=""
30+
BASE_PATH_NO_LEAD_WITH_SLASH=""
31+
else
32+
BASE_PATH_WITH_SLASH="$BASE_PATH/"
33+
BASE_PATH_NO_LEAD="${BASE_PATH#/}"
34+
BASE_PATH_NO_LEAD_WITH_SLASH="${BASE_PATH_NO_LEAD}/"
35+
fi
36+
37+
# Replace the placeholder base path in built assets, if present
38+
if grep -R "__BASE_PATH__" "$CONTENT_DIR" >/dev/null 2>&1; then
39+
find "$CONTENT_DIR" -type f \( -name '*.js' -o -name '*.css' -o -name '*.html' -o -name '*.json' -o -name '*.txt' \) -print0 |
40+
while IFS= read -r -d '' file; do
41+
sed -i \
42+
-e "s|/__BASE_PATH__/|${BASE_PATH_WITH_SLASH}|g" \
43+
-e "s|/__BASE_PATH__|${BASE_PATH}|g" \
44+
-e "s|__BASE_PATH__/|${BASE_PATH_NO_LEAD_WITH_SLASH}|g" \
45+
-e "s|__BASE_PATH__|${BASE_PATH_NO_LEAD}|g" \
46+
"$file"
47+
done
48+
fi
49+
50+
# Generate Nginx configuration matching the selected base path
51+
if [ "$BASE_PATH" = "/" ]; then
52+
cat >/etc/nginx/conf.d/default.conf <<'EOF'
53+
server {
54+
listen 80;
55+
server_name _;
56+
root /usr/share/nginx/html;
57+
58+
location / {
59+
try_files $uri $uri/ /index.html;
60+
}
61+
}
62+
EOF
63+
else
64+
cat >/etc/nginx/conf.d/default.conf <<EOF
65+
server {
66+
listen 80;
67+
server_name _;
68+
root /usr/share/nginx/html;
69+
70+
location = $BASE_PATH {
71+
return 301 $BASE_PATH/;
72+
}
73+
74+
location $BASE_PATH/ {
75+
rewrite ^$BASE_PATH/(.*)$ /\$1 break;
76+
try_files \$uri \$uri/ /index.html;
77+
}
78+
}
79+
EOF
80+
fi
81+
82+
exec "$@"

src/App.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import SettingsContextProvider from "./context/SettingsContext";
88
import NotFound from "./pages/NotFound";
99

1010
export default function App() {
11+
const basename = import.meta.env.BASE_URL.replace(/\/$/, "");
12+
1113
return (
1214
<SettingsContextProvider>
13-
<BrowserRouter>
15+
<BrowserRouter basename={basename || undefined}>
1416
<RestoreScroll />
1517
<Routes>
1618
<Route path="/" element={<LandingPage />} />

vite.config.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
import { defineConfig } from 'vite'
2-
import react from '@vitejs/plugin-react'
1+
/* eslint-env node */
2+
import { defineConfig, loadEnv } from "vite";
3+
import react from "@vitejs/plugin-react";
34

4-
// https://vitejs.dev/config/
5-
export default defineConfig({
6-
plugins: [react()],
7-
})
5+
export default defineConfig(({ mode }) => {
6+
const env = loadEnv(mode, process.cwd(), "");
7+
let base = env.VITE_BASE_PATH || "/";
8+
if (!base.startsWith("/") && !/^https?:\/\//.test(base)) {
9+
base = `/${base}`;
10+
}
11+
if (!base.endsWith("/")) {
12+
base += "/";
13+
}
14+
15+
return {
16+
base,
17+
plugins: [react()],
18+
};
19+
});

0 commit comments

Comments
 (0)