diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml
index 8d9c9e2..0d8b22a 100644
--- a/.gitea/workflows/release.yaml
+++ b/.gitea/workflows/release.yaml
@@ -22,6 +22,7 @@ jobs:
run: |
cd client
docker build \
+ --target production-stage \
--build-arg VITE_API_URL=${{ vars.VITE_API_URL }} \
-t ${{ vars.REGISTRY_URL }}/zen-kanji-client:latest .
docker push ${{ vars.REGISTRY_URL }}/zen-kanji-client:latest
diff --git a/client/Dockerfile b/client/Dockerfile
index 3e7e8cc..4d81fa8 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -1,15 +1,31 @@
-FROM node:24-alpine AS dev-stage
+# Stage 1: Build the Application
+FROM node:20-alpine AS build-stage
WORKDIR /app
+ARG VITE_API_URL
+ENV VITE_API_URL=$VITE_API_URL
+
COPY package*.json ./
-
RUN npm ci
+COPY . .
+RUN npm run build
+
+FROM node:20-alpine AS dev-stage
+WORKDIR /app
+COPY package*.json ./
+RUN npm ci
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host"]
-FROM dev-stage AS build-stage
-ARG VITE_API_URL
-ENV VITE_API_URL=$VITE_API_URL
-RUN npm run build
+FROM nginx:alpine AS production-stage
+RUN mkdir -p /run/nginx
+
+COPY --from=build-stage /app/dist /usr/share/nginx/html
+
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+EXPOSE 80
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/client/index.html b/client/index.html
index 2d09bbf..515ee18 100644
--- a/client/index.html
+++ b/client/index.html
@@ -5,7 +5,7 @@
Zen Kanji
-
+
diff --git a/client/nginx.conf b/client/nginx.conf
new file mode 100644
index 0000000..90b2cc3
--- /dev/null
+++ b/client/nginx.conf
@@ -0,0 +1,16 @@
+server {
+ listen 80;
+ server_name localhost;
+
+ root /usr/share/nginx/html;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
+ expires 1y;
+ add_header Cache-Control "public, no-transform";
+ }
+}
diff --git a/client/vite.config.js b/client/vite.config.js
index 10685cd..9759d43 100644
--- a/client/vite.config.js
+++ b/client/vite.config.js
@@ -18,15 +18,15 @@ export default defineConfig(({ mode }) => {
},
server: {
allowedHosts: [
- env.VITE_HOST,
+ 'localhost',
],
host: true,
port: 5173,
strictPort: true,
hmr: {
- host: env.VITE_HOST,
- protocol: 'wss',
- clientPort: 443,
+ host: 'localhost',
+ protocol: 'ws',
+ clientPort: 5173,
},
},
};
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
new file mode 100644
index 0000000..fb948c9
--- /dev/null
+++ b/docker-compose.dev.yml
@@ -0,0 +1,33 @@
+services:
+ mongo:
+ image: mongo:6
+ container_name: zen_mongo
+ restart: always
+ ports:
+ - "27017:27017"
+ volumes:
+ - mongo-data:/data/db
+ networks:
+ - zen-network
+
+ server:
+ depends_on:
+ - mongo
+ environment:
+ - MONGO_URI=mongodb://mongo:27017/zenkanji
+ volumes:
+ - ./server:/app
+ - /app/node_modules
+ ports:
+ - "3000:3000"
+ command: npm run dev
+
+ client:
+ build:
+ target: dev-stage
+ ports:
+ - "5173:5173"
+ volumes:
+ - ./client:/app
+ - /app/node_modules
+ command: npm run dev -- --host
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..e69de29
diff --git a/docker-compose.yml b/docker-compose.yml
index fa20eb0..1f888eb 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,47 +1,23 @@
services:
- mongo:
- image: mongo:6
- container_name: zen_mongo
- restart: always
- ports:
- - "27017:27017"
- volumes:
- - mongo-data:/data/db
- networks:
- - zen-network
-
server:
- build: ./server
+ build:
+ context: ./server
container_name: zen_server
- restart: always
- ports:
- - "3000:3000"
env_file:
- - .env
- depends_on:
- - mongo
+ - ./server/.env
networks:
- zen-network
- volumes:
- - ./server:/app
- - /app/node_modules
client:
build:
context: ./client
- target: dev-stage
container_name: zen_client
- ports:
- - "5173:5173"
env_file:
- - .env
+ - ./client/.env
depends_on:
- server
networks:
- zen-network
- volumes:
- - ./client:/app
- - /app/node_modules
volumes:
mongo-data:
diff --git a/server/server.js b/server/server.js
index d19c803..e352bca 100644
--- a/server/server.js
+++ b/server/server.js
@@ -17,10 +17,20 @@ const allowedOrigins = [
'capacitor://localhost',
'https://10.0.2.2:5173',
'http://localhost:5173'
-];
+].filter(Boolean).map(uri => uri.replace(/\/$/, ''));
await fastify.register(cors, {
- origin: allowedOrigins,
+ origin: (origin, cb) => {
+ if (!origin) return cb(null, true);
+
+ if (allowedOrigins.includes(origin)) {
+ return cb(null, true);
+ }
+
+ console.log(`CORS BLOCKED: Browser sent "${origin}". Allowed list:`, allowedOrigins);
+
+ cb(new Error("Not allowed by CORS"));
+ },
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true
});