Merge branch 'develop' into dashboard
Test / Type Check (all packages) (pull_request) Failing after 10s
Test / API Unit Tests (pull_request) Has been skipped
Test / Homepage Unit Tests (pull_request) Has been skipped
Test / Carplace Unit Tests (pull_request) Has been skipped
Test / Admin Unit Tests (pull_request) Has been skipped
Test / Dashboard Unit Tests (pull_request) Has been skipped
Test / API Integration Tests (pull_request) Has been skipped

This commit is contained in:
2026-07-04 05:06:04 +00:00
5 changed files with 326 additions and 75 deletions
+101 -36
View File
@@ -27,23 +27,56 @@ jobs:
image_repository: ${{ steps.image-meta.outputs.repository }}
docker_image: ${{ steps.image-meta.outputs.full }}
steps:
- uses: actions/checkout@v4
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" \
fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
git rev-parse --short HEAD
- name: Ensure Docker CLI is available
run: |
if ! command -v docker >/dev/null 2>&1; then
apt-get update -qq
apt-get install -y -qq docker.io
echo "::error::Docker CLI is not available in this Gitea runner image."
echo "::error::Configure the runner label used by this job to an image that already includes Docker CLI, for example ubuntu-latest:docker://catthehacker/ubuntu:act-latest."
echo "::error::The current runner maps ubuntu-latest to node:20-bookworm, and this job cannot install docker.io because the job container has no external DNS."
exit 1
fi
docker --version
docker info >/dev/null
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-config-inline: |
[registry."192.168.3.80"]
insecure = true
shell: bash
run: |
set -euo pipefail
docker buildx version
printf '[registry."%s"]\n insecure = true\n' "$REGISTRY_HOST" > /tmp/buildkitd.toml
if ! docker buildx inspect rentaldrivego-builder >/dev/null 2>&1; then
docker buildx create \
--name rentaldrivego-builder \
--driver docker-container \
--config /tmp/buildkitd.toml \
--use
else
docker buildx use rentaldrivego-builder
fi
docker buildx inspect --bootstrap
- name: Docker image metadata
id: image-meta
@@ -69,7 +102,7 @@ jobs:
env:
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_HOMEPAGE_URL: ${{ secrets.NEXT_PUBLIC_HOMEPAGE_URL }}
NEXT_PUBLIC_STOREFRONT_URL: ${{ secrets.NEXT_PUBLIC_STOREFRONT_URL }}
NEXT_PUBLIC_CARPLACE_URL: ${{ secrets.NEXT_PUBLIC_CARPLACE_URL }}
NEXT_PUBLIC_DASHBOARD_URL: ${{ secrets.NEXT_PUBLIC_DASHBOARD_URL }}
NEXT_PUBLIC_ADMIN_URL: ${{ secrets.NEXT_PUBLIC_ADMIN_URL }}
SITE_ORIGIN: ${{ secrets.SITE_ORIGIN }}
@@ -77,7 +110,7 @@ jobs:
for variable in \
NEXT_PUBLIC_API_URL \
NEXT_PUBLIC_HOMEPAGE_URL \
NEXT_PUBLIC_STOREFRONT_URL \
NEXT_PUBLIC_CARPLACE_URL \
NEXT_PUBLIC_DASHBOARD_URL \
NEXT_PUBLIC_ADMIN_URL \
SITE_ORIGIN
@@ -98,34 +131,41 @@ jobs:
- name: Log in to Gitea Container Registry
if: steps.registry-check.outputs.available == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_HOST }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
shell: bash
run: |
printf '%s' "${{ secrets.REGISTRY_PASSWORD }}" | \
docker login "$REGISTRY_HOST" \
--username "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
- name: Build and push
uses: docker/build-push-action@v6
shell: bash
env:
BUILDKIT_NO_CLIENT_TOKEN: "true"
with:
context: .
file: ${{ env.DOCKERFILE_PATH }}
platforms: ${{ env.DOCKER_PLATFORM }}
push: ${{ steps.registry-check.outputs.available == 'true' }}
tags: |
${{ steps.image-meta.outputs.full }}
${{ steps.image-meta.outputs.latest }}
build-args: |
API_INTERNAL_URL=http://api:4000/api/v1
DASHBOARD_INTERNAL_URL=http://dashboard:3001
ADMIN_INTERNAL_URL=http://admin:3002
NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_HOMEPAGE_URL=${{ secrets.NEXT_PUBLIC_HOMEPAGE_URL }}
NEXT_PUBLIC_STOREFRONT_URL=${{ secrets.NEXT_PUBLIC_STOREFRONT_URL }}
NEXT_PUBLIC_DASHBOARD_URL=${{ secrets.NEXT_PUBLIC_DASHBOARD_URL }}
NEXT_PUBLIC_ADMIN_URL=${{ secrets.NEXT_PUBLIC_ADMIN_URL }}
SITE_ORIGIN=${{ secrets.SITE_ORIGIN }}
run: |
set -euo pipefail
OUTPUT_ARG="--load"
if [ "${{ steps.registry-check.outputs.available }}" = "true" ]; then
OUTPUT_ARG="--push"
fi
docker buildx build \
--file "$DOCKERFILE_PATH" \
--platform "$DOCKER_PLATFORM" \
--tag "${{ steps.image-meta.outputs.full }}" \
--tag "${{ steps.image-meta.outputs.latest }}" \
--build-arg API_INTERNAL_URL=http://api:4000/api/v1 \
--build-arg DASHBOARD_INTERNAL_URL=http://dashboard:3001 \
--build-arg ADMIN_INTERNAL_URL=http://admin:3002 \
--build-arg NEXT_PUBLIC_API_URL="${{ secrets.NEXT_PUBLIC_API_URL }}" \
--build-arg NEXT_PUBLIC_HOMEPAGE_URL="${{ secrets.NEXT_PUBLIC_HOMEPAGE_URL }}" \
--build-arg NEXT_PUBLIC_CARPLACE_URL="${{ secrets.NEXT_PUBLIC_CARPLACE_URL }}" \
--build-arg NEXT_PUBLIC_DASHBOARD_URL="${{ secrets.NEXT_PUBLIC_DASHBOARD_URL }}" \
--build-arg NEXT_PUBLIC_ADMIN_URL="${{ secrets.NEXT_PUBLIC_ADMIN_URL }}" \
--build-arg SITE_ORIGIN="${{ secrets.SITE_ORIGIN }}" \
"$OUTPUT_ARG" \
.
deploy:
name: Deploy to VPS
@@ -135,7 +175,27 @@ jobs:
DOCKER_IMAGE: ${{ needs.build-image.outputs.docker_image }}
IMAGE_REPOSITORY: ${{ needs.build-image.outputs.image_repository }}
steps:
- uses: actions/checkout@v4
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" \
fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
git rev-parse --short HEAD
- name: Check deploy credentials
id: check-creds
@@ -152,7 +212,12 @@ jobs:
- name: Install SSH client
if: steps.check-creds.outputs.skip == 'false'
run: |
apt-get update -qq && apt-get install -y -qq openssh-client
if ! command -v ssh >/dev/null 2>&1 || ! command -v scp >/dev/null 2>&1; then
echo "::error::OpenSSH client tools are not available in this Gitea runner image."
echo "::error::Use a runner image that already includes ssh and scp; installing packages from apt is not reliable because the job container has no external DNS."
exit 1
fi
ssh -V
- name: Set up SSH key
if: steps.check-creds.outputs.skip == 'false'
+147 -28
View File
@@ -24,10 +24,27 @@ jobs:
name: Type Check (all packages)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -62,10 +79,27 @@ jobs:
runs-on: ubuntu-latest
needs: type-check
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -100,10 +134,27 @@ jobs:
runs-on: ubuntu-latest
needs: type-check
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -138,10 +189,27 @@ jobs:
runs-on: ubuntu-latest
needs: type-check
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -176,10 +244,27 @@ jobs:
runs-on: ubuntu-latest
needs: type-check
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -213,10 +298,27 @@ jobs:
runs-on: ubuntu-latest
needs: type-check
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
@@ -277,10 +379,27 @@ jobs:
JWT_EXPIRY: 8h
FILE_STORAGE_ROOT: /tmp/rentaldrivego-test-storage
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Check out repository
shell: bash
run: |
set -euo pipefail
if [ -z "$(find . -mindepth 1 -maxdepth 1 -print -quit)" ]; then
REPOSITORY_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
git init .
git remote add origin "$REPOSITORY_URL"
if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
AUTH_HEADER="$(printf '%s:%s' "${{ gitea.actor }}" "${{ secrets.GITHUB_TOKEN }}" | base64 | tr -d '\n')"
git -c http.extraheader="Authorization: Basic ${AUTH_HEADER}" fetch --depth 1 origin "${{ gitea.ref }}"
else
git fetch --depth 1 origin "${{ gitea.ref }}"
fi
git checkout --detach "${{ gitea.sha }}" || git checkout --detach FETCH_HEAD
fi
- name: Check Node runtime
run: |
node --version
npm --version
- run: corepack enable && corepack prepare npm@10.5.0 --activate
- name: Install dependencies
run: |
+12 -10
View File
@@ -4,18 +4,20 @@ import TopBar from '@/components/layout/TopBar'
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="fleet-dashboard-shell flex min-h-screen transition-colors print:block print:min-h-0 print:bg-white">
<div className="print:hidden">
<Sidebar />
</div>
<div className="ms-0 flex min-h-screen min-w-0 flex-1 flex-col lg:ms-64 print:ms-0 print:min-h-0 print:block">
<DashboardAccessGuard>
<div className="fleet-dashboard-shell flex min-h-screen transition-colors print:block print:min-h-0 print:bg-white">
<div className="print:hidden">
<TopBar />
<Sidebar />
</div>
<div className="ms-0 flex min-h-screen min-w-0 flex-1 flex-col lg:ms-64 print:ms-0 print:min-h-0 print:block">
<div className="print:hidden">
<TopBar />
</div>
<main className="flex-1 overflow-y-auto p-6 print:p-0 print:overflow-visible">
{children}
</main>
</div>
<main className="flex-1 overflow-y-auto p-6 print:p-0 print:overflow-visible">
<DashboardAccessGuard>{children}</DashboardAccessGuard>
</main>
</div>
</div>
</DashboardAccessGuard>
)
}
+18
View File
@@ -0,0 +1,18 @@
http:
routers:
mail-certificate:
rule: "Host(`mail.rentaldrivego.ma`)"
entryPoints:
- websecure
service: mail-certificate-placeholder
tls:
certResolver: letsencrypt
domains:
- main: "mail.rentaldrivego.ma"
services:
mail-certificate-placeholder:
loadBalancer:
servers:
# This router exists to request and renew the certificate.
# Mail protocols are not routed through this HTTP service.
- url: "http://127.0.0.1:65535"
+48 -1
View File
@@ -1,6 +1,8 @@
services:
traefik:
image: traefik:latest
# Set TRAEFIK_VERSION in .env to the exact version you are running.
# Do not use "latest" in production.
image: traefik:${TRAEFIK_VERSION}
command:
- --api.dashboard=false
- --api.insecure=false
@@ -29,6 +31,51 @@ services:
- ./dynamic:/etc/traefik/dynamic:ro
networks:
- traefik-proxy
traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.11.4
depends_on:
- traefik
restart: unless-stopped
# Wait for Traefik's ACME storage, then export certificates whenever
# acme.json changes.
entrypoint:
- /bin/sh
- -ec
command:
- |
until [ -s /data/acme.json ]; do
sleep 2
done
exec traefik-certs-dumper file \
--version v3 \
--watch \
--source /data/acme.json \
--dest /output \
--domain-subdir \
--crt-name fullchain \
--key-name privkey \
--crt-ext .pem \
--key-ext .pem
volumes:
# Existing Traefik ACME volume, mounted read-only.
- traefik-letsencrypt:/data:ro
# Normal certificate files will appear on the VPS here.
- ./exported-certs:/output
# The dumper only reads a local volume and writes exported files.
# It needs no network access.
network_mode: none
labels:
- "traefik.enable=false"
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=16m,mode=1777
networks:
traefik-proxy:
external: true