make deployment easier (#225)
This commit is contained in:
74
Dockerfile
Normal file
74
Dockerfile
Normal file
@@ -0,0 +1,74 @@
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install root dependencies
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Install frontend dependencies
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Install backend dependencies
|
||||
WORKDIR /app/backend
|
||||
COPY backend/package.json backend/package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Build frontend
|
||||
FROM base AS frontend-builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json ./
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY --from=deps /app/frontend/node_modules ./frontend/node_modules
|
||||
COPY frontend ./frontend
|
||||
|
||||
WORKDIR /app/frontend
|
||||
RUN npm run build
|
||||
|
||||
# Final production image
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=8000
|
||||
ENV FRONTEND_ORIGIN=http://localhost:8000
|
||||
|
||||
# Create a non-root user
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copy backend files
|
||||
COPY backend ./backend
|
||||
|
||||
# Copy frontend build
|
||||
COPY --from=frontend-builder /app/frontend/.next/standalone ./
|
||||
COPY --from=frontend-builder /app/frontend/.next/static ./.next/static
|
||||
COPY --from=frontend-builder /app/frontend/public ./public
|
||||
|
||||
# Copy Next.js module for the server
|
||||
COPY --from=deps /app/frontend/node_modules/next ./node_modules/next
|
||||
|
||||
# Install backend dependencies directly in the container to ensure native modules are built correctly
|
||||
WORKDIR /app/backend
|
||||
COPY backend/package.json backend/package-lock.json* ./
|
||||
RUN npm ci
|
||||
WORKDIR /app
|
||||
|
||||
# Copy custom combined_server.js that combines frontend and backend
|
||||
COPY combined_server.js ./
|
||||
|
||||
# Switch to non-root user
|
||||
USER nextjs
|
||||
|
||||
# Expose the port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run database migrations and start the combined server
|
||||
CMD ["node", "combined_server.js"]
|
||||
@@ -1,11 +0,0 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
|
||||
EXPOSE $PORT
|
||||
RUN npx sequelize db:migrate
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
@@ -9,6 +9,6 @@
|
||||
},
|
||||
"production": {
|
||||
"dialect": "sqlite",
|
||||
"storage": "database/database.sqlite"
|
||||
"storage": "../database/database.sqlite"
|
||||
}
|
||||
}
|
||||
|
||||
152
backend/index.js
152
backend/index.js
@@ -1,153 +1,7 @@
|
||||
const express = require('express');
|
||||
const RateLimit = require('express-rate-limit');
|
||||
const path = require('path');
|
||||
const { Sequelize } = require('sequelize');
|
||||
const app = express();
|
||||
|
||||
// enable frontend access
|
||||
const cors = require('cors');
|
||||
const frontendOrigin = process.env.FRONTEND_ORIGIN || 'http://localhost:8000';
|
||||
const corsOptions = {
|
||||
origin: frontendOrigin,
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
};
|
||||
app.use(cors(corsOptions));
|
||||
|
||||
// enable json middleware
|
||||
app.use(express.json());
|
||||
|
||||
// enable rate limiter
|
||||
const limiter = RateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1h
|
||||
max: 1000, // 1000 requests per hour
|
||||
message: 'Too many requests from this IP, please try again after an hour',
|
||||
});
|
||||
app.use(limiter);
|
||||
|
||||
// Specify the directory to serve static files
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// init sequalize
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: 'database/database.sqlite',
|
||||
logging: false,
|
||||
});
|
||||
|
||||
// "/"
|
||||
const indexRoute = require('./routes/index');
|
||||
app.use('/', indexRoute);
|
||||
|
||||
// "users"
|
||||
const usersIndexRoute = require('./routes/users/index')(sequelize);
|
||||
const usersFindRoute = require('./routes/users/find')(sequelize);
|
||||
const usersSearchRoute = require('./routes/users/search')(sequelize);
|
||||
const signUpRoute = require('./routes/users/signup')(sequelize);
|
||||
const signInRoute = require('./routes/users/signin')(sequelize);
|
||||
app.use('/users', usersIndexRoute);
|
||||
app.use('/users', usersFindRoute);
|
||||
app.use('/users', usersSearchRoute);
|
||||
app.use('/users', signUpRoute);
|
||||
app.use('/users', signInRoute);
|
||||
// ESM import
|
||||
(async () => {
|
||||
const updateRoute = await import('./routes/users/update.mjs');
|
||||
app.use('/users', updateRoute.default(sequelize));
|
||||
})();
|
||||
|
||||
// "/projects"
|
||||
const projectsIndexRoute = require('./routes/projects/index')(sequelize);
|
||||
const projectsShowRoute = require('./routes/projects/show')(sequelize);
|
||||
const projectsNewRoute = require('./routes/projects/new')(sequelize);
|
||||
const projectsEditRoute = require('./routes/projects/edit')(sequelize);
|
||||
const projectsDeleteRoute = require('./routes/projects/delete')(sequelize);
|
||||
app.use('/projects', projectsIndexRoute);
|
||||
app.use('/projects', projectsShowRoute);
|
||||
app.use('/projects', projectsNewRoute);
|
||||
app.use('/projects', projectsEditRoute);
|
||||
app.use('/projects', projectsDeleteRoute);
|
||||
|
||||
// "/folders"
|
||||
const foldersIndexRoute = require('./routes/folders/index')(sequelize);
|
||||
const foldersNewRoute = require('./routes/folders/new')(sequelize);
|
||||
const foldersEditRoute = require('./routes/folders/edit')(sequelize);
|
||||
const foldersDeleteRoute = require('./routes/folders/delete')(sequelize);
|
||||
app.use('/folders', foldersIndexRoute);
|
||||
app.use('/folders', foldersNewRoute);
|
||||
app.use('/folders', foldersEditRoute);
|
||||
app.use('/folders', foldersDeleteRoute);
|
||||
|
||||
// "/cases"
|
||||
const casesDownloadRoute = require('./routes/cases/download')(sequelize);
|
||||
const casesIndexRoute = require('./routes/cases/index')(sequelize);
|
||||
const casesIndexByProjectIdRoute = require('./routes/cases/indexByProjectId')(sequelize);
|
||||
const casesShowRoute = require('./routes/cases/show')(sequelize);
|
||||
const casesNewRoute = require('./routes/cases/new')(sequelize);
|
||||
const casesEditRoute = require('./routes/cases/edit')(sequelize);
|
||||
const casesDeleteRoute = require('./routes/cases/delete')(sequelize);
|
||||
app.use('/cases', casesDownloadRoute);
|
||||
app.use('/cases', casesIndexRoute);
|
||||
app.use('/cases', casesIndexByProjectIdRoute);
|
||||
app.use('/cases', casesShowRoute);
|
||||
app.use('/cases', casesNewRoute);
|
||||
app.use('/cases', casesEditRoute);
|
||||
app.use('/cases', casesDeleteRoute);
|
||||
|
||||
// "/steps"
|
||||
const stepsEditRoute = require('./routes/steps/edit')(sequelize);
|
||||
app.use('/steps', stepsEditRoute);
|
||||
|
||||
// "/attachments"
|
||||
const attachmentsNewRoute = require('./routes/attachments/new')(sequelize);
|
||||
const attachmentsDeleteRoute = require('./routes/attachments/delete')(sequelize);
|
||||
const attachmentsDownloadRoute = require('./routes/attachments/download')(sequelize);
|
||||
app.use('/attachments', attachmentsNewRoute);
|
||||
app.use('/attachments', attachmentsDeleteRoute);
|
||||
app.use('/attachments', attachmentsDownloadRoute);
|
||||
|
||||
// "/runs"
|
||||
const runsDownloadRoute = require('./routes/runs/download')(sequelize);
|
||||
const runsIndexRoute = require('./routes/runs/index')(sequelize);
|
||||
const runsShowRoute = require('./routes/runs/show')(sequelize);
|
||||
const runsNewRoute = require('./routes/runs/new')(sequelize);
|
||||
const runsEditRoute = require('./routes/runs/edit')(sequelize);
|
||||
const runDeleteRoute = require('./routes/runs/delete')(sequelize);
|
||||
app.use('/runs', runsDownloadRoute);
|
||||
app.use('/runs', runsIndexRoute);
|
||||
app.use('/runs', runsShowRoute);
|
||||
app.use('/runs', runsNewRoute);
|
||||
app.use('/runs', runsEditRoute);
|
||||
app.use('/runs', runDeleteRoute);
|
||||
|
||||
// "/runcases"
|
||||
const runCaseIndexRoute = require('./routes/runcases/index')(sequelize);
|
||||
const runCaseEditRoute = require('./routes/runcases/edit')(sequelize);
|
||||
app.use('/runcases', runCaseIndexRoute);
|
||||
app.use('/runcases', runCaseEditRoute);
|
||||
|
||||
// "/members"
|
||||
const membersIndexRoute = require('./routes/members/index')(sequelize);
|
||||
const membersNewRoute = require('./routes/members/new')(sequelize);
|
||||
const membersEditRoute = require('./routes/members/edit')(sequelize);
|
||||
const membersDeleteRoute = require('./routes/members/delete')(sequelize);
|
||||
const membersCheckRoute = require('./routes/members/check')(sequelize);
|
||||
app.use('/members', membersIndexRoute);
|
||||
app.use('/members', membersNewRoute);
|
||||
app.use('/members', membersEditRoute);
|
||||
app.use('/members', membersDeleteRoute);
|
||||
app.use('/members', membersCheckRoute);
|
||||
|
||||
// "/home"
|
||||
const homeIndexRoute = require('./routes/home/index')(sequelize);
|
||||
app.use('/home', homeIndexRoute);
|
||||
const server = require('./server');
|
||||
|
||||
const PORT = process.env.PORT || 8001;
|
||||
app.listen(PORT, () => {
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Backend server is running on port ${PORT}`);
|
||||
console.log(`Access from the frontend origin: ${frontendOrigin} is valid.`);
|
||||
if (!process.env.SECRET_KEY) {
|
||||
console.log(
|
||||
"[Warning]: Default key is used for token generation. Please set the environment variable 'SECRET_KEY'`."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
"scripts": {
|
||||
"start": "node --env-file=.env index",
|
||||
"dev": "node index",
|
||||
"migrate": "bash scripts/migrate.sh",
|
||||
"seed": "bash scripts/seed.sh",
|
||||
"drop": "bash scripts/drop.sh"
|
||||
"migrate": "sequelize db:migrate",
|
||||
"seed": "sequelize db:seed:all",
|
||||
"drop": "sequelize db:migrate:undo:all"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
npx sequelize db:migrate:undo:all
|
||||
echo "Dropped all tables"
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
npx sequelize db:migrate
|
||||
echo "Migration complete."
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
npx sequelize db:seed:all
|
||||
143
backend/server.js
Normal file
143
backend/server.js
Normal file
@@ -0,0 +1,143 @@
|
||||
const express = require('express');
|
||||
const RateLimit = require('express-rate-limit');
|
||||
const path = require('path');
|
||||
const { Sequelize } = require('sequelize');
|
||||
const app = express();
|
||||
|
||||
// enable json middleware
|
||||
app.use(express.json());
|
||||
|
||||
// enable rate limiter
|
||||
const limiter = RateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1h
|
||||
max: 1000, // 1000 requests per hour
|
||||
message: 'Too many requests from this IP, please try again after an hour',
|
||||
});
|
||||
app.use(limiter);
|
||||
|
||||
// Specify the directory to serve static files
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// init sequalize
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: 'database/database.sqlite',
|
||||
logging: false,
|
||||
});
|
||||
|
||||
// "/"
|
||||
const indexRoute = require('./routes/index');
|
||||
app.use('/', indexRoute);
|
||||
|
||||
// "users"
|
||||
const usersIndexRoute = require('./routes/users/index')(sequelize);
|
||||
const usersFindRoute = require('./routes/users/find')(sequelize);
|
||||
const usersSearchRoute = require('./routes/users/search')(sequelize);
|
||||
const signUpRoute = require('./routes/users/signup')(sequelize);
|
||||
const signInRoute = require('./routes/users/signin')(sequelize);
|
||||
app.use('/users', usersIndexRoute);
|
||||
app.use('/users', usersFindRoute);
|
||||
app.use('/users', usersSearchRoute);
|
||||
app.use('/users', signUpRoute);
|
||||
app.use('/users', signInRoute);
|
||||
// ESM import
|
||||
(async () => {
|
||||
const updateRoute = await import('./routes/users/update.mjs');
|
||||
app.use('/users', updateRoute.default(sequelize));
|
||||
})();
|
||||
|
||||
// "/projects"
|
||||
const projectsIndexRoute = require('./routes/projects/index')(sequelize);
|
||||
const projectsShowRoute = require('./routes/projects/show')(sequelize);
|
||||
const projectsNewRoute = require('./routes/projects/new')(sequelize);
|
||||
const projectsEditRoute = require('./routes/projects/edit')(sequelize);
|
||||
const projectsDeleteRoute = require('./routes/projects/delete')(sequelize);
|
||||
app.use('/projects', projectsIndexRoute);
|
||||
app.use('/projects', projectsShowRoute);
|
||||
app.use('/projects', projectsNewRoute);
|
||||
app.use('/projects', projectsEditRoute);
|
||||
app.use('/projects', projectsDeleteRoute);
|
||||
|
||||
// "/folders"
|
||||
const foldersIndexRoute = require('./routes/folders/index')(sequelize);
|
||||
const foldersNewRoute = require('./routes/folders/new')(sequelize);
|
||||
const foldersEditRoute = require('./routes/folders/edit')(sequelize);
|
||||
const foldersDeleteRoute = require('./routes/folders/delete')(sequelize);
|
||||
app.use('/folders', foldersIndexRoute);
|
||||
app.use('/folders', foldersNewRoute);
|
||||
app.use('/folders', foldersEditRoute);
|
||||
app.use('/folders', foldersDeleteRoute);
|
||||
|
||||
// "/cases"
|
||||
// "/cases"
|
||||
const casesDownloadRoute = require('./routes/cases/download')(sequelize);
|
||||
const casesIndexRoute = require('./routes/cases/index')(sequelize);
|
||||
const casesIndexByProjectIdRoute = require('./routes/cases/indexByProjectId')(sequelize);
|
||||
const casesShowRoute = require('./routes/cases/show')(sequelize);
|
||||
const casesNewRoute = require('./routes/cases/new')(sequelize);
|
||||
const casesEditRoute = require('./routes/cases/edit')(sequelize);
|
||||
const casesDeleteRoute = require('./routes/cases/delete')(sequelize);
|
||||
app.use('/cases', casesDownloadRoute);
|
||||
app.use('/cases', casesIndexRoute);
|
||||
app.use('/cases', casesIndexByProjectIdRoute);
|
||||
app.use('/cases', casesShowRoute);
|
||||
app.use('/cases', casesNewRoute);
|
||||
app.use('/cases', casesEditRoute);
|
||||
app.use('/cases', casesDeleteRoute);
|
||||
|
||||
// "/steps"
|
||||
const stepsEditRoute = require('./routes/steps/edit')(sequelize);
|
||||
app.use('/steps', stepsEditRoute);
|
||||
|
||||
// "/attachments"
|
||||
const attachmentsNewRoute = require('./routes/attachments/new')(sequelize);
|
||||
const attachmentsDeleteRoute = require('./routes/attachments/delete')(sequelize);
|
||||
const attachmentsDownloadRoute = require('./routes/attachments/download')(sequelize);
|
||||
app.use('/attachments', attachmentsNewRoute);
|
||||
app.use('/attachments', attachmentsDeleteRoute);
|
||||
app.use('/attachments', attachmentsDownloadRoute);
|
||||
|
||||
// "/runs"
|
||||
const runsDownloadRoute = require('./routes/runs/download')(sequelize);
|
||||
const runsIndexRoute = require('./routes/runs/index')(sequelize);
|
||||
const runsShowRoute = require('./routes/runs/show')(sequelize);
|
||||
const runsNewRoute = require('./routes/runs/new')(sequelize);
|
||||
const runsEditRoute = require('./routes/runs/edit')(sequelize);
|
||||
const runDeleteRoute = require('./routes/runs/delete')(sequelize);
|
||||
app.use('/runs', runsDownloadRoute);
|
||||
app.use('/runs', runsIndexRoute);
|
||||
app.use('/runs', runsShowRoute);
|
||||
app.use('/runs', runsNewRoute);
|
||||
app.use('/runs', runsEditRoute);
|
||||
app.use('/runs', runDeleteRoute);
|
||||
|
||||
// "/runcases"
|
||||
const runCaseIndexRoute = require('./routes/runcases/index')(sequelize);
|
||||
const runCaseEditRoute = require('./routes/runcases/edit')(sequelize);
|
||||
app.use('/runcases', runCaseIndexRoute);
|
||||
app.use('/runcases', runCaseEditRoute);
|
||||
|
||||
// "/members"
|
||||
const membersIndexRoute = require('./routes/members/index')(sequelize);
|
||||
const membersNewRoute = require('./routes/members/new')(sequelize);
|
||||
const membersEditRoute = require('./routes/members/edit')(sequelize);
|
||||
const membersDeleteRoute = require('./routes/members/delete')(sequelize);
|
||||
const membersCheckRoute = require('./routes/members/check')(sequelize);
|
||||
app.use('/members', membersIndexRoute);
|
||||
app.use('/members', membersNewRoute);
|
||||
app.use('/members', membersEditRoute);
|
||||
app.use('/members', membersDeleteRoute);
|
||||
app.use('/members', membersCheckRoute);
|
||||
|
||||
// "/home"
|
||||
const homeIndexRoute = require('./routes/home/index')(sequelize);
|
||||
app.use('/home', homeIndexRoute);
|
||||
|
||||
if (!process.env.SECRET_KEY) {
|
||||
console.log(
|
||||
"[Warning]: Default key is used for token generation. Please set the environment variable 'SECRET_KEY'`."
|
||||
);
|
||||
}
|
||||
|
||||
// Export the app instead of starting the server
|
||||
module.exports = app;
|
||||
87
combined_server.js
Normal file
87
combined_server.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// Use express from backend node_modules
|
||||
const express = require('./backend/node_modules/express');
|
||||
const { createServer: createHttpServer } = require('http');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
async function runMigrations() {
|
||||
try {
|
||||
console.log('Running database migrations...');
|
||||
// Use execSync with cwd option to run in the backend directory
|
||||
execSync('npx sequelize-cli db:migrate', {
|
||||
cwd: path.join(__dirname, 'backend'),
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
console.log('Database migrations completed successfully.');
|
||||
|
||||
if (process.env.IS_DEMO === 'true' || process.env.IS_DEMO === '1') {
|
||||
console.log('Demo mode detected. Seeding the database...');
|
||||
execSync('npx sequelize-cli db:seed:all', {
|
||||
cwd: path.join(__dirname, 'backend'),
|
||||
stdio: 'inherit',
|
||||
});
|
||||
console.log('Database seeding completed successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error running database migrations or seeding:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function startServer() {
|
||||
try {
|
||||
const server = express();
|
||||
const httpServer = createHttpServer(server);
|
||||
|
||||
// Import the backend app
|
||||
const backendApp = require('./backend/server');
|
||||
|
||||
// Use the backend app for /api routes
|
||||
server.use('/api', backendApp);
|
||||
|
||||
// For Next.js standalone build
|
||||
// Check if we have the Next.js server file
|
||||
const nextServerPath = './node_modules/next/dist/server/next.js';
|
||||
if (fs.existsSync(nextServerPath)) {
|
||||
// Import Next.js
|
||||
const next = require(nextServerPath);
|
||||
|
||||
// Initialize Next.js app
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const nextApp = next({ dev, dir: path.join(__dirname, '.') });
|
||||
const handle = nextApp.getRequestHandler();
|
||||
|
||||
// Prepare Next.js
|
||||
nextApp.prepare().then(() => {
|
||||
console.log('nextjs prepared');
|
||||
// Use Next.js to handle all other routes
|
||||
server.all('*', (req, res) => {
|
||||
return handle(req, res);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('Next.js module not found at:', nextServerPath);
|
||||
server.all('*', (req, res) => {
|
||||
res.status(500).send('Frontend server not available');
|
||||
});
|
||||
}
|
||||
|
||||
const PORT = process.env.PORT || 8000;
|
||||
httpServer.listen(PORT, (err) => {
|
||||
if (err) throw err;
|
||||
console.log(`> Ready on http://localhost:${PORT}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error starting server:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runMigrations().then(() => {
|
||||
startServer();
|
||||
}).catch(error => {
|
||||
console.error('Failed to start application:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,31 +1,16 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: unittcms-backend:latest
|
||||
build:
|
||||
context: ./backend
|
||||
ports:
|
||||
- '8001:8001'
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- FRONTEND_ORIGIN=http://localhost:8000
|
||||
- PORT=8001
|
||||
volumes:
|
||||
- db-data:/app/database
|
||||
|
||||
frontend:
|
||||
image: unittcms-frontend:latest
|
||||
build:
|
||||
context: ./frontend
|
||||
unittcms:
|
||||
image: unittcms:latest
|
||||
ports:
|
||||
- '8000:8000'
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- NEXT_PUBLIC_BACKEND_ORIGIN=http://localhost:8001
|
||||
- PORT=8000
|
||||
depends_on:
|
||||
- backend
|
||||
- SECRET_KEY=your_secret_key_here
|
||||
- IS_DEMO=false # set to true to seed the database
|
||||
volumes:
|
||||
- db-data:/app/database
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage.
|
||||
# Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn run build; \
|
||||
elif [ -f package-lock.json ]; then npm run build; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# server.js is created by next build from the standalone output
|
||||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||
CMD HOSTNAME="0.0.0.0" node server.js
|
||||
@@ -1,5 +1,5 @@
|
||||
const Config = {
|
||||
apiServer: process.env.NEXT_PUBLIC_BACKEND_ORIGIN || 'http://localhost:8001',
|
||||
apiServer: process.env.NEXT_PUBLIC_BACKEND_ORIGIN || '/api',
|
||||
|
||||
// set 'NEXT_PUBLIC_IS_DEMO=true' in frontend/.env
|
||||
isDemoSite: process.env.NEXT_PUBLIC_IS_DEMO === 'true' || false,
|
||||
|
||||
Reference in New Issue
Block a user