Skip to content
Merged
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
9 changes: 8 additions & 1 deletion .github/config/build-matrices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,11 @@ projects:
- name: specs-back
context: ./tools/specs/server
tag: hub-specs-back
artifact-name: trivy-reports-specs-back
artifact-name: trivy-reports-specs-back
specs-front:
description: "Specs client"
include:
- name: specs-client
context: ./tools/specs/client
tag: hub-specs-client
artifact-name: trivy-reports-specs-client
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ client/bin/
certs/

# Environment variables
.envrc
.envrc

dist/
3 changes: 3 additions & 0 deletions tools/specs/client/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
.git
1 change: 1 addition & 0 deletions tools/specs/client/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_SPECS_API_DOMAIN=https://integration.hub.esante.gouv.fr/specs/api
55 changes: 55 additions & 0 deletions tools/specs/client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 1. Build
FROM dhi.io/node:24.19-alpine3.24-dev AS builder

WORKDIR /app

# Enable pnpm via corepack (bundled with Node)
RUN corepack enable

# Copy the pnpm project files needed for install to the container
COPY package.json pnpm-lock.yaml ./

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy the project files to the container
COPY . .

# Build the project
RUN pnpm build

# 2. Serve
FROM dhi.io/nginx:1.31.3-alpine3.24

# The hardened nginx image runs as a non-root user by default; switch to root
# for the setup steps below, then drop back to non-root before running.
USER 0

# Copy the static build output to the nginx document root
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy the nginx configuration files
COPY nginx.conf /usr/share/nginx/html/nginx.conf
COPY conf.d/default.conf /etc/nginx/conf.d/default.conf

# Vite bakes VITE_* vars at build time, but this image is built once and
# deployed to multiple environments with per-env values injected as container
# env vars. This entrypoint writes those values to a small script, loaded by
# index.html before the app bundle, so they're readable at container runtime.
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh

RUN export VITE_SPECS_API_DOMAIN=https://integration.hub.esante.gouv.fr/specs/api
# Fix permissions for nginx runtime directories to run as the nginx user
# provided by the base image. The hardened base image locks /var/lib/nginx
# and /var/log/nginx down to that same user already, but /var/cache/nginx and
# the copied-in html/config files need to be reassigned too.
RUN mkdir -p /var/cache/nginx && chown -R nginx:nginx /var/cache/nginx /usr/share/nginx/html /var/lib/nginx /var/log/nginx

# Expose port 8080 to allow incoming traffic
EXPOSE 8080

ENTRYPOINT ["/docker-entrypoint.sh"]

# Run as non-root user for better security
USER nginx
32 changes: 32 additions & 0 deletions tools/specs/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# specs-client

Viewer for the Hub Santé message schemas: fetches the schema list from the specs API and displays it.

## Stack

- Vite
- React + TypeScript
- Tailwind CSS v4
- TanStack Router
- Zustand

## Getting started

```bash
cp .env.example .env # set VITE_SPECS_API_DOMAIN
pnpm install
pnpm dev
```

- `pnpm dev` — start the dev server
- `pnpm build` — build for production
- `pnpm preview` — preview the production build

## Structure

- `src/main.tsx` — app entry point
- `src/router.tsx` — router; root loader fetches `${VITE_SPECS_API_DOMAIN}/api/schemas` into the schema store, shared by all routes
- `src/store/schema-store.ts` — Zustand store holding schemas (by name) and the selected schema
- `src/components/MessageList.tsx` — bottom tab bar listing schemas, selects one
- `src/components/MessageDetail.tsx` — middle panel, schema detail (placeholder for now)
- `src/components/ui/button.tsx` — shadcn-style Button component
25 changes: 25 additions & 0 deletions tools/specs/client/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
17 changes: 17 additions & 0 deletions tools/specs/client/conf.d/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
# SPA fallback: let client-side router handle unmatched paths
try_files $uri $uri/ /index.html;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
8 changes: 8 additions & 0 deletions tools/specs/client/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh
set -e

cat <<EOF > /usr/share/nginx/html/env-config.js
window.__ENV__ = { VITE_SPECS_API_DOMAIN: "${VITE_SPECS_API_DOMAIN}" };
EOF

exec nginx -c /usr/share/nginx/html/nginx.conf -g "daemon off;"
27 changes: 27 additions & 0 deletions tools/specs/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="fr" class="bg-background">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="https://use.typekit.net/jjx2ksb.css" />
<link
rel="stylesheet"
href="./style/print.css"
type="text/css"
media="print"
/>
<title>Hub Santé - Specs</title>
<meta
name="description"
content="Visualiseur des modèles de données du HubSanté."
/>
</head>
<body>
<div id="root"></div>
<script src="/env-config.js"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions tools/specs/client/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/cache/nginx/nginx.pid;


events {
worker_connections 1024;
}


http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;

keepalive_timeout 65;

include /etc/nginx/conf.d/*.conf;
}
33 changes: 33 additions & 0 deletions tools/specs/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "react-ts-tailwind-tanstack-boiler",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@base-ui/react": "^1.6.0",
"@tanstack/react-query": "^5.101.4",
"@tanstack/react-router": "^1.170.17",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.28.0",
"react": "^19",
"react-dom": "^19",
"tailwind-merge": "^3.3.1",
"zustand": "^5.0.3"
},
"devDependencies": {
"@tailwindcss/vite": "^4.3.2",
"@types/node": "^26.1.1",
"@types/react": "^19",
"@types/react-dom": "^19",
"@vitejs/plugin-react": "^6.0.3",
"tailwindcss": "^4.2.0",
"typescript": "5.7.3",
"vite": "^8.1.3"
}
}
Loading