Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 36 additions & 14 deletions template/.github/workflows/deploy-traefik.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
name: CD-traefik
name: CD-Traefik-Deploy

on:
push:
branches:
- staging
push:
branches:
- staging

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.SSH_USER }}
host: ${{ secrets.SSH_HOST }}
password: ${{ secrets.SSH_PASSWORD }}
script: cd && cd traefik/deploy && bash ./js-project.sh ${{ github.event.repository.owner.name }} ${{ github.event.repository.name }} ${{ vars.PROJECT_NAME }} '${{ toJSON(vars) }}'
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: 📦 Checkout du code
uses: actions/checkout@v3

- name: 🔐 Connexion à Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: 🛠️ Build et push de l'image Docker
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/project-${{ vars.PROJECT_NAME }}:latest

- name: 🚀 Déploiement via SSH (script VPS)
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.SSH_PASSWORD }}
script: |
bash ~/traefik/deploy/js-project-docker.sh \
"${{ github.repository_owner }}" \
"${{ github.event.repository.name }}" \
"${{ vars.PROJECT_NAME }}" \
"${{ secrets.DOCKER_USERNAME }}"
74 changes: 39 additions & 35 deletions template/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
# Use the latest Dockerfile syntax version, which allows using BuildKit's enhanced features.
# This line is primarily for internal syntax definition and doesn't need modification.
#syntax=docker/dockerfile:1.4
# Étape 1 : Build client + server
FROM node:20-alpine AS build

# Start with the official Node.js 20 image based on Alpine Linux for a smaller base image.
# Using Alpine keeps the image lightweight, though compatibility issues may arise with certain libraries.
WORKDIR /app

# Installation des dépendances
COPY client/package*.json ./client/
COPY server/package*.json ./server/

RUN cd client && npm install
RUN cd server && npm install

# Copie les sources des deux projets
COPY client ./client
COPY server ./server

# Build du client
RUN cd client && npm run build

# Build du serveur
RUN cd server && npm run build

# Étape 2 : Image finale
FROM node:20-alpine

# Install necessary packages.
# libc6-compat is often needed for compatibility with some npm packages.
# To improve this layer, limit the packages installed to the bare minimum to keep the image small.
# Additionally, consider whether libc6-compat is absolutely necessary here.
# `no-cache` is used to avoid persisting the package index, saving space.
# hadolint ignore=DL3018
RUN apk add --no-cache libc6-compat

# Set the working directory for the application. This is where commands will be run by default.
# Creating the directory first (RUN mkdir -p /usr/src/app) is generally redundant, as WORKDIR will create it.
WORKDIR /usr/src/app

# Copy all files from the build context to the working directory in the container.
# To improve: Use a .dockerignore file to exclude unnecessary files from the copy (e.g., tests, config files, logs),
# which will result in a smaller image and faster build times.
COPY . .

# Install npm packages.
# To improve: Separate dependencies and application code for better use of Docker cache.
# 1. First, copy just the package.json and package-lock.json.
# 2. Run `npm install` only on those files to cache dependencies, avoiding reinstallation on every code change.
# Example improvement:
# COPY package*.json ./
# RUN npm install
# Then copy the rest of the application code.
RUN npm install

# Set the default command to run the build and start the application.
# To improve: Separate build and runtime steps. Consider running the build in a multi-stage build
# to create a smaller final image without unnecessary build tools or intermediate files.
WORKDIR /app

# Copie le dossier server
COPY --from=build /app/server ./server

# Copie le dossier client
COPY --from=build /app/client ./client

# Installation des dépendances du serveur
WORKDIR /app/server

# Installation des dépendances de production
RUN npm install --omit=dev

EXPOSE 3310

CMD ["npm", "run", "start"]
45 changes: 18 additions & 27 deletions template/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
version: "3.4"
version: "3.8"

services:
web:
build: .
container_name: ${PROJECT_NAME:-project}-web
env_file:
- ../envs/.env-${GITHUB_REPOSITORY_NAME}
command: sh -c "npm run build && npm run start"
environment:
APP_PORT: 3310
APP_SECRET: ${APP_SECRET:-123456789}
DB_HOST: ${DATABASE_SUBDOMAIN_NAME}-db
DB_PORT: 3306
DB_USER: ${USER_NAME}
DB_PASSWORD: ${USER_PASSWORD}
DB_NAME: ${DB_NAME}
VITE_API_URL: ""
PROJECT_NAME_SPECIFIC_SAMPLE: ${PROJECT_NAME_SPECIFIC_SAMPLE}
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.${PROJECT_NAME:-project}-secure.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME:-project}-secure.rule=Host(`${PROJECT_NAME:-project}.${HOST:-localhost}`)"
- "traefik.http.services.${PROJECT_NAME:-project}.loadbalancer.server.port=3310"
web:
image: ${DOCKER_IMAGE}
container_name: ${PROJECT_NAME}-web
restart: always
env_file:
- .env
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.${PROJECT_NAME}-secure.entrypoints=websecure"
- "traefik.http.routers.${PROJECT_NAME}-secure.rule=Host(`${PROJECT_NAME}.${HOST}`)"
- "traefik.http.services.${PROJECT_NAME}.loadbalancer.server.port=3310"
- "traefik.http.routers.${PROJECT_NAME}-secure.tls.certresolver=letsencrypt"

networks:
proxy:
external: true
proxy:
external: true
68 changes: 34 additions & 34 deletions template/server/bin/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import fs from "node:fs";
import path from "node:path";

// Build the path to the schema SQL file
const schema = path.join(__dirname, "../../server/database/schema.sql");
const schema = path.resolve(process.cwd(), "database/schema.sql");

// Get database connection details from .env file
const { DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME } = process.env;
Expand All @@ -14,39 +14,39 @@ const { DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME } = process.env;
import mysql from "mysql2/promise";

const migrate = async () => {
try {
// Read the SQL statements from the schema file
const sql = fs.readFileSync(schema, "utf8");

// Create a specific connection to the database
const database = await mysql.createConnection({
host: DB_HOST,
port: DB_PORT as number | undefined,
user: DB_USER,
password: DB_PASSWORD,
multipleStatements: true, // Allow multiple SQL statements
});

// Drop the existing database if it exists
await database.query(`drop database if exists ${DB_NAME}`);

// Create a new database with the specified name
await database.query(`create database ${DB_NAME}`);

// Switch to the newly created database
await database.query(`use ${DB_NAME}`);

// Execute the SQL statements to update the database schema
await database.query(sql);

// Close the database connection
database.end();

console.info(`${DB_NAME} updated from '${path.normalize(schema)}' 🆙`);
} catch (err) {
const { message, stack } = err as Error;
console.error("Error updating the database:", message, stack);
}
try {
// Read the SQL statements from the schema file
const sql = fs.readFileSync(schema, "utf8");

// Create a specific connection to the database
const database = await mysql.createConnection({
host: DB_HOST,
port: DB_PORT as number | undefined,
user: DB_USER,
password: DB_PASSWORD,
multipleStatements: true, // Allow multiple SQL statements
});

// Drop the existing database if it exists
await database.query(`drop database if exists ${DB_NAME}`);

// Create a new database with the specified name
await database.query(`create database ${DB_NAME}`);

// Switch to the newly created database
await database.query(`use ${DB_NAME}`);

// Execute the SQL statements to update the database schema
await database.query(sql);

// Close the database connection
database.end();

console.info(`${DB_NAME} updated from '${path.normalize(schema)}' 🆙`);
} catch (err) {
const { message, stack } = err as Error;
console.error("Error updating the database:", message, stack);
}
};

// Run the migration function
Expand Down
56 changes: 28 additions & 28 deletions template/server/package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
{
"name": "@js-monorepo/server",
"scripts": {
"check-types": "tsc --noEmit",
"dev": "tsx watch --clear-screen=false ./src/main",
"db:migrate": "tsx ./bin/migrate",
"db:seed": "tsx ./bin/seed",
"build": "tsx ./bin/migrate",
"start": "tsx ./src/main",
"test": "jest --verbose"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"mysql2": "^3.14.0"
},
"devDependencies": {
"@faker-js/faker": "^9.6.0",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.1",
"@types/jest": "^29.5.14",
"@types/supertest": "^6.0.3",
"jest": "^29.7.0",
"supertest": "^7.1.0",
"ts-jest": "^29.3.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
}
"name": "@js-monorepo/server",
"scripts": {
"check-types": "tsc --noEmit",
"dev": "tsx watch --clear-screen=false ./src/main",
"db:migrate": "tsx ./bin/migrate",
"db:seed": "tsx ./bin/seed",
"build": "tsc",
"start": "tsx ./src/main",
"test": "jest --verbose"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"tsx": "^4.19.3",
"mysql2": "^3.14.0"
},
"devDependencies": {
"@faker-js/faker": "^9.6.0",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.1",
"@types/jest": "^29.5.14",
"@types/supertest": "^6.0.3",
"jest": "^29.7.0",
"supertest": "^7.1.0",
"ts-jest": "^29.3.0",
"typescript": "^5.8.2"
}
}