implement docker
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
backend/node_modules
|
||||||
|
|
||||||
|
# Environment secrets - JANGAN masuk ke image
|
||||||
|
backend/.env
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
backend/*.log
|
||||||
|
|
||||||
|
# Git files
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
+10
@@ -1,3 +1,13 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
backend/node_modules/
|
||||||
backend/.env
|
backend/.env
|
||||||
*.log
|
*.log
|
||||||
|
backend/*.log
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# ─── Stage 1: Install dependencies ──────────────────────────────────────────
|
||||||
|
FROM node:22-alpine AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY backend/package*.json ./
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
|
# ─── Stage 2: Runtime ────────────────────────────────────────────────────────
|
||||||
|
FROM node:22-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dumb-init untuk proper signal handling
|
||||||
|
RUN apk add --no-cache dumb-init
|
||||||
|
|
||||||
|
# Copy dependencies dari stage deps
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
|
||||||
|
# Copy backend source
|
||||||
|
COPY backend/ ./backend/
|
||||||
|
|
||||||
|
# Copy frontend static files
|
||||||
|
COPY frontend/ ./frontend/
|
||||||
|
|
||||||
|
# Set working directory ke backend (karena server.js ada di sini)
|
||||||
|
WORKDIR /app/backend
|
||||||
|
|
||||||
|
# Non-root user untuk keamanan
|
||||||
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=3000
|
||||||
|
|
||||||
|
# Health check (dipakai Coolify untuk monitoring)
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:3000/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["dumb-init", "--"]
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# ─── Konfigurasi Database MySQL ─────────────────────────────────────────────
|
||||||
|
# Di Coolify: gunakan nama service MySQL yang dibuat, contoh: "mysql" atau "db"
|
||||||
|
DB_HOST=db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER=webgis_user
|
||||||
|
DB_PASSWORD=ganti_dengan_password_kuat
|
||||||
|
DB_NAME=spbu_pontianak
|
||||||
|
|
||||||
|
# ─── Server ──────────────────────────────────────────────────────────────────
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=production
|
||||||
@@ -10,9 +10,21 @@ const app = express();
|
|||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
// ─── Middleware ───────────────────────────────────────────────────────────────
|
// ─── Middleware ───────────────────────────────────────────────────────────────
|
||||||
|
app.set("trust proxy", 1); // Untuk Coolify reverse proxy (Traefik/Nginx)
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use("/api/poverty", povertyRoutes);
|
app.use("/api/poverty", povertyRoutes);
|
||||||
|
|
||||||
|
// ─── Health Check (dibutuhkan Coolify untuk monitoring container) ─────────────────
|
||||||
|
app.get("/health", async (req, res) => {
|
||||||
|
try {
|
||||||
|
await db.execute("SELECT 1");
|
||||||
|
res.json({ status: "ok", db: "connected", timestamp: new Date().toISOString() });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(503).json({ status: "error", db: "disconnected", message: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ─── Root: landing page (harus sebelum express.static) ───────────────────────
|
// ─── Root: landing page (harus sebelum express.static) ───────────────────────
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname, "../frontend/landing.html"));
|
res.sendFile(path.join(__dirname, "../frontend/landing.html"));
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ─── Database MySQL ───────────────────────────────────────────────────────
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: webgis_db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword}
|
||||||
|
MYSQL_DATABASE: ${DB_NAME:-spbu_pontianak}
|
||||||
|
MYSQL_USER: ${DB_USER:-webgis_user}
|
||||||
|
MYSQL_PASSWORD: ${DB_PASSWORD:-webgispassword}
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
- ./schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-rootpassword}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 30s
|
||||||
|
networks:
|
||||||
|
- webgis_net
|
||||||
|
|
||||||
|
# ─── Aplikasi Node.js ─────────────────────────────────────────────────────
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: webgis_app
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-3000}:3000"
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: 3000
|
||||||
|
DB_HOST: db
|
||||||
|
DB_PORT: 3306
|
||||||
|
DB_USER: ${DB_USER:-webgis_user}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-webgispassword}
|
||||||
|
DB_NAME: ${DB_NAME:-spbu_pontianak}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
networks:
|
||||||
|
- webgis_net
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
webgis_net:
|
||||||
|
driver: bridge
|
||||||
Reference in New Issue
Block a user