Add Docker support with runtime environment variable configuration
- Remove temporary arne.js file - Add runtime configuration via window.__RUNTIME_CONFIG__ - Create public/config.template.js for hostname injection - Add Dockerfile with multi-stage build (node + nginx) - Add docker-entrypoint.sh to generate config.js at startup - Add .dockerignore - Update all components to use runtime config instead of arne.js - Update index.html to load config.js before app This allows deploying the same Docker image to different environments by setting the API_HOSTNAME environment variable at runtime. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
dist
|
||||||
|
.DS_Store
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM nginx:alpine
|
||||||
|
RUN apk add --no-cache gettext
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
COPY public/config.template.js /usr/share/nginx/html/
|
||||||
|
COPY docker-entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Generate config.js from template at runtime
|
||||||
|
envsubst < /usr/share/nginx/html/config.template.js > /usr/share/nginx/html/config.js
|
||||||
|
|
||||||
|
# Start nginx
|
||||||
|
exec "$@"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
window.__RUNTIME_CONFIG__ = {
|
||||||
|
API_HOSTNAME: '${API_HOSTNAME}'
|
||||||
|
};
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<script src="<%= BASE_URL %>config.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export default {
|
|
||||||
hostname: "$HOSTNAME",
|
|
||||||
}
|
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import arne from '../../arne.js'
|
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -93,7 +92,7 @@ export default {
|
|||||||
reloadGames() {
|
reloadGames() {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/all`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/all`,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.allGamesList = response.data;
|
this.allGamesList = response.data;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import arne from '../../arne.js'
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -47,7 +46,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/reset`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/reset`,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -62,7 +61,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/sync`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/sync`,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve();
|
resolve();
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import arne from "../../arne.js";
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -168,7 +167,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/rand`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/rand`,
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
onDownloadProgress: (progressEvent) => {
|
onDownloadProgress: (progressEvent) => {
|
||||||
let percentCompleted = Math.round(
|
let percentCompleted = Math.round(
|
||||||
@@ -198,7 +197,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/rand/low`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/rand/low`,
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
onDownloadProgress: (progressEvent) => {
|
onDownloadProgress: (progressEvent) => {
|
||||||
let percentCompleted = Math.round(
|
let percentCompleted = Math.round(
|
||||||
@@ -228,7 +227,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/addPlayed`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/addPlayed`,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
@@ -243,7 +242,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/addQue`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/addQue`,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
@@ -258,7 +257,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/info`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/info`,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let gameInfoObject = {
|
let gameInfoObject = {
|
||||||
@@ -280,7 +279,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music/list`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music/list`,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let tracklistArray = response.data;
|
let tracklistArray = response.data;
|
||||||
@@ -297,7 +296,7 @@ export default {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.axios({
|
this.axios({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `${arne.hostname}/music?song=${trackNumber}`,
|
url: `${window.__RUNTIME_CONFIG__.API_HOSTNAME}/music?song=${trackNumber}`,
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user