Initial deploy setup — CV static site
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled

Dockerfile (nginx alpine), K3s manifests (cv namespace, NodePort 30111),
Gitea Actions workflow for build + rollout. Targets cv.arthurbarre.fr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ordinarthur 2026-05-12 16:45:11 +02:00
commit 851ab1fc55
9 changed files with 1963 additions and 0 deletions

View File

@ -0,0 +1,62 @@
name: Build & Deploy
on:
push:
branches: [main]
env:
REGISTRY: git.arthurbarre.fr
IMAGE: ordinarthur/cv
NAMESPACE: cv
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Gitea Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ordinarthur
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }}
- name: Install kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
- name: Deploy to K3s
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
kubectl apply -f k3s/cv/namespace.yml
kubectl -n $NAMESPACE create secret docker-registry gitea-registry \
--docker-server=$REGISTRY \
--docker-username=ordinarthur \
--docker-password=${{ secrets.REGISTRY_PASSWORD }} \
--dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f k3s/cv/deployment.yml
kubectl apply -f k3s/cv/service.yml
kubectl -n $NAMESPACE set image deployment/cv \
cv=$REGISTRY/$IMAGE:${{ github.sha }}
kubectl -n $NAMESPACE rollout status deployment/cv --timeout=120s

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pdf
.DS_Store
node_modules/

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM nginx:1.27-alpine
WORKDIR /usr/share/nginx/html
RUN rm -f index.html
COPY Resume-Arthur-barre-fr.html cv-short.html photo.jpg ./
RUN cp Resume-Arthur-barre-fr.html index.html
EXPOSE 80

1120
Resume-Arthur-barre-fr.html Normal file

File diff suppressed because it is too large Load Diff

706
cv-short.html Normal file
View File

@ -0,0 +1,706 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Arthur Barré — Fullstack Developer · Résumé</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #F4F2EC;
--ink: #0A0A0A;
--muted: #6B6B66;
--accent: #FF4D00;
}
* { box-sizing: border-box; }
html, body {
margin: 0; padding: 0;
background: var(--bg);
color: var(--ink);
font-family: 'Inter', system-ui, sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 8pt;
line-height: 1.4;
}
a { color: inherit; text-decoration: none; }
.page {
width: 210mm;
height: 297mm;
padding: 8mm;
margin: 0 auto;
background: var(--bg);
position: relative;
}
.frame {
border: 1px solid var(--ink);
height: calc(297mm - 16mm);
display: flex;
flex-direction: column;
overflow: hidden;
}
.label, .mono {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.topbar {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
border-bottom: 1px solid var(--ink);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.topbar > div { padding: 6px 10px; border-right: 1px solid var(--ink); }
.topbar > div:last-child { border-right: none; text-align: right; }
.topbar .val { color: var(--ink); display: block; margin-top: 2px; }
.topbar .accent { color: var(--accent); }
.hero {
display: grid;
grid-template-columns: 8fr 4fr;
border-bottom: 1px solid var(--ink);
}
.hero-left { padding: 14px 18px; border-right: 1px solid var(--ink); display: flex; flex-direction: column; justify-content: space-between; }
.hero h1 {
font-family: 'Inter', sans-serif;
font-weight: 300;
font-size: 38pt;
line-height: 0.92;
letter-spacing: -0.035em;
margin: 6px 0 0;
}
.hero h1 em { font-style: italic; color: var(--accent); font-weight: 300; }
.hero .pitch {
margin-top: 12px;
font-size: 8.5pt;
line-height: 1.5;
color: var(--ink);
max-width: 56ch;
}
.hero .pitch b { font-weight: 600; }
.hero .pitch .accent { color: var(--accent); font-weight: 500; }
.hero-meta {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-top: 14px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.hero-meta .k { color: var(--muted); margin-bottom: 2px; }
.hero-meta .v { color: var(--ink); }
.hero-meta .v.accent { color: var(--accent); }
.hero-right { display: flex; flex-direction: column; }
.specimen-cell { padding: 10px 12px; border-bottom: 1px solid var(--ink); }
.specimen-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.specimen-photo-wrap {
position: relative;
margin-top: 6px;
border: 1px solid var(--ink);
overflow: hidden;
aspect-ratio: 1 / 1;
width: 100%;
background: #fff;
}
.specimen-photo {
width: 100%; height: 100%;
object-fit: cover;
display: block;
filter: grayscale(0.12) contrast(1.02);
}
.contact-cell {
padding: 10px 12px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink);
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
justify-content: center;
}
.contact-cell .k { color: var(--muted); }
.contact-cell a:hover { color: var(--accent); }
.skills-strip {
padding: 10px 14px;
border-bottom: 1px solid var(--ink);
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.skills-strip .chip {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 3px 7px;
border: 1px solid var(--ink);
border-radius: 999px;
line-height: 1;
}
.skills-strip .chip.accent { color: var(--accent); border-color: var(--accent); }
.skills-strip .chip.solid { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.skills-strip .skills-lead {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
margin-right: 4px;
}
.body-grid {
display: grid;
grid-template-columns: 7fr 5fr;
flex: 1;
min-height: 0;
}
.col-left { border-right: 1px solid var(--ink); display: flex; flex-direction: column; }
.col-right { display: flex; flex-direction: column; }
.col-section-title {
padding: 6px 14px;
border-bottom: 1px solid var(--ink);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
display: flex;
justify-content: space-between;
align-items: center;
color: var(--ink);
}
.col-section-title .num-tag { color: var(--accent); font-weight: 500; }
.col-section-title .count { color: var(--muted); }
.xp {
padding: 8px 14px;
border-bottom: 1px solid var(--ink);
display: grid;
grid-template-columns: 28px 1fr;
gap: 10px;
}
.xp:last-child { border-bottom: none; }
.xp .num {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 9pt;
color: var(--ink);
line-height: 1.2;
}
.xp h3 {
margin: 0;
font-family: 'Inter', sans-serif;
font-weight: 500;
font-size: 10.5pt;
letter-spacing: -0.012em;
line-height: 1.15;
}
.xp h3 .role-sep { color: var(--muted); font-weight: 300; }
.xp .when {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
color: var(--accent);
letter-spacing: 0.06em;
text-transform: uppercase;
margin-top: 2px;
}
.xp .when .where { color: var(--muted); margin-left: 4px; }
.xp ul {
margin: 5px 0 0;
padding-left: 12px;
font-size: 7.8pt;
line-height: 1.4;
}
.xp ul li { margin: 1px 0; }
.xp ul li b { font-weight: 600; }
.pj {
padding: 8px 14px;
border-bottom: 1px solid var(--ink);
}
.pj:last-child { border-bottom: none; }
.pj-head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 8px;
}
.pj h3 {
margin: 0;
font-family: 'Inter', sans-serif;
font-weight: 500;
font-size: 10pt;
letter-spacing: -0.012em;
}
.pj .url-mono {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
color: var(--accent);
letter-spacing: 0.04em;
}
.pj .pj-meta {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
margin-top: 1px;
}
.pj .pj-desc {
margin-top: 4px;
font-size: 7.8pt;
line-height: 1.35;
}
.pj .pj-desc b { font-weight: 600; }
.pj .pj-stack {
margin-top: 4px;
display: flex;
flex-wrap: wrap;
gap: 3px;
}
.pj .pj-stack .chip {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 6.5pt;
letter-spacing: 0.05em;
text-transform: uppercase;
padding: 2px 5px;
border: 1px solid var(--ink);
border-radius: 999px;
line-height: 1;
}
.pj .pj-stack .chip.accent { color: var(--accent); border-color: var(--accent); }
.formations {
display: grid;
grid-template-columns: 1fr 1fr;
border-top: 1px solid var(--ink);
}
.formations .fo {
padding: 8px 14px;
border-right: 1px solid var(--ink);
display: grid;
grid-template-columns: 28px 1fr auto;
gap: 10px;
align-items: center;
}
.formations .fo:last-child { border-right: none; }
.formations .fo .num {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 8pt;
}
.formations .fo h4 {
margin: 0;
font-family: 'Inter', sans-serif;
font-weight: 500;
font-size: 9pt;
line-height: 1.2;
}
.formations .fo .school {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 6.8pt;
color: var(--accent);
letter-spacing: 0.06em;
text-transform: uppercase;
margin-top: 1px;
}
.formations .fo .when {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 6.8pt;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
text-align: right;
}
.footer {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
border-top: 1px solid var(--ink);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 7pt;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.footer > div { padding: 6px 12px; border-right: 1px solid var(--ink); }
.footer > div:last-child { border-right: none; text-align: right; color: var(--accent); }
@page { size: A4; margin: 0; }
@media print {
body { background: var(--bg); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.page {
width: 210mm;
height: 297mm;
page-break-after: auto;
break-after: auto;
margin: 0;
overflow: hidden;
}
.print-btn, .switch-btn, .lang-toggle { display: none !important; }
a { color: inherit; }
}
/* DOWNLOAD BUTTON */
.print-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
background: var(--accent);
color: #fff;
border: 1px solid var(--ink);
padding: 12px 18px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 9pt;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
box-shadow: 4px 4px 0 var(--ink);
transition: transform 0.1s ease, box-shadow 0.1s ease;
}
.print-btn:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
.print-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.print-btn svg { vertical-align: middle; margin-right: 6px; }
/* VERSION SWITCHER (top-right) */
.switch-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
background: var(--bg);
color: var(--ink);
border: 1px solid var(--ink);
padding: 10px 16px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 8.5pt;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
box-shadow: 4px 4px 0 var(--ink);
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease, color 0.1s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.switch-btn:hover { background: var(--accent); color: #fff; transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
.switch-btn .arrow { display: inline-block; transition: transform 0.15s ease; }
.switch-btn:hover .arrow { transform: translateX(3px); }
/* LANG TOGGLE (bottom-right, left of download) */
.lang-toggle {
position: fixed;
bottom: 20px;
right: 220px;
z-index: 9999;
background: var(--bg);
color: var(--ink);
border: 1px solid var(--ink);
padding: 12px 16px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-size: 9pt;
letter-spacing: 0.1em;
text-transform: uppercase;
cursor: pointer;
box-shadow: 4px 4px 0 var(--ink);
transition: transform 0.1s ease, box-shadow 0.1s ease;
display: inline-flex;
align-items: center;
gap: 6px;
}
.lang-toggle:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
.lang-toggle:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.lang-toggle .lang-opt { opacity: 0.4; transition: opacity 0.1s ease, color 0.1s ease; cursor: pointer; }
.lang-toggle .lang-opt.active { opacity: 1; color: var(--accent); font-weight: 600; }
.lang-toggle .sep { color: var(--muted); opacity: 0.5; }
</style>
</head>
<body>
<div class="page">
<div class="frame">
<div class="topbar">
<div><span data-en="Document">Document</span><span class="val">CV / 2026</span></div>
<div><span data-en="Format">Format</span><span class="val" data-en="Single-page">Single-page</span></div>
<div><span data-en="Based in">Basé à</span><span class="val accent">Marseille — FR</span></div>
<div><span data-en="Contact">Contact</span><span class="val">contact.arthurbarre@gmail.com</span></div>
</div>
<div class="hero">
<div class="hero-left">
<div>
<div class="label" data-en="[ Profile ] — Fullstack Developer">[ Profil ] — Fullstack Developer</div>
<h1>Arthur Barré <em></em><br/>Fullstack <em>Developer.</em></h1>
<p class="pitch" data-en='<b>7 years of experience</b> shipping <span class="accent">end-to-end</span> products — from Node / Symfony / Python back-ends to polished React / Vue front-ends, all the way to self-hosted Proxmox / K3s infrastructure. I <b>ship</b>, I <b>measure</b>, and I love working fast with teams aiming for <span class="accent">product excellence</span>.'>
<b>7 ans d'expérience</b> à concevoir des produits <span class="accent">end-to-end</span> — du back-end Node / Symfony / Python à un front-end React / Vue ciselé, jusqu'à l'infra auto-hébergée Proxmox / K3s. Je <b>livre</b>, je <b>mesure</b>, et j'aime travailler vite avec des équipes qui visent <span class="accent">l'excellence produit</span>.
</p>
</div>
<div class="hero-meta">
<div><div class="k" data-en="Role">Role</div><div class="v">Fullstack</div></div>
<div><div class="k" data-en="Years">Years</div><div class="v" data-en="7+ yrs">7+ ans</div></div>
<div><div class="k" data-en="Focus">Focus</div><div class="v" data-en="SaaS · AI · Product">SaaS · IA · Produit</div></div>
<div><div class="k" data-en="Status">Status</div><div class="v accent" data-en="Open · Permanent / Freelance">Ouvert · CDI / Freelance</div></div>
</div>
</div>
<div class="hero-right">
<div class="specimen-cell">
<div class="specimen-header">
<span>[ FIG. A ]</span>
<span style="color:var(--accent);" data-en="Age 25">25 ans</span>
</div>
<div class="specimen-photo-wrap">
<img class="specimen-photo" src="photo.jpg" alt="Arthur Barré" />
</div>
</div>
<div class="contact-cell">
<div><span class="k" data-en="Tel —">Tél —</span> 06 13 17 61 17</div>
<div><span class="k">Mail —</span> <a href="mailto:contact.arthurbarre@gmail.com">contact.arthurbarre@gmail.com</a></div>
<div><span class="k">Site —</span> <a href="https://arthurbarre.fr" style="color:var(--accent);">arthurbarre.fr</a></div>
<div><span class="k" data-en="Loc —">Lieu —</span> Marseille 13001</div>
</div>
</div>
</div>
<div class="skills-strip">
<span class="skills-lead">Stack —</span>
<span class="chip solid">TypeScript</span>
<span class="chip accent">React</span>
<span class="chip accent">Next.js</span>
<span class="chip">Vue.js</span>
<span class="chip">Node.js</span>
<span class="chip">AdonisJS</span>
<span class="chip">Fastify</span>
<span class="chip">Symfony</span>
<span class="chip">Python</span>
<span class="chip">PostgreSQL</span>
<span class="chip">MongoDB</span>
<span class="chip">GraphQL</span>
<span class="chip">Docker</span>
<span class="chip">K3s</span>
<span class="chip">Proxmox</span>
<span class="chip">Stripe</span>
<span class="chip">React Native</span>
<span class="chip">IoT</span>
</div>
<div class="body-grid">
<div class="col-left">
<div class="col-section-title">
<span><span class="num-tag">[ 01 ]</span> &nbsp; <span data-en="Work experience">Expériences professionnelles</span></span>
<span class="count" data-en="03 items · 2019 — Present">03 items · 2019 — Présent</span>
</div>
<div class="xp">
<div class="num">03</div>
<div>
<h3 data-en='Fullstack Developer <span class="role-sep">— Permanent</span>'>Développeur Fullstack <span class="role-sep">— CDI</span></h3>
<div class="when" data-en='2023 — Present <span class="where">· thecamp · Aix-en-Provence</span>'>2023 — Présent <span class="where">· thecamp · Aix-en-Provence</span></div>
<ul data-en='<li>Full-stack dev on a <b>multi-tech</b> environment (React, Vue, PHP/Symfony, Python, Node) — front-end, back-end and <b>IoT</b>.</li><li>Designed &amp; shipped a Python-based <b>BMS</b> (Building Management System) — centralizes and translates equipment data.</li><li>Integrated a <b>Symfony</b> app with an interactive front-end to visualize and control building state.</li><li>Tech choices, <b>software architecture</b>, scalability and maintainability across a Docker + Linux stack.</li>'>
<li>Dev full-stack sur un environnement <b>multi-technos</b> (React, Vue, PHP/Symfony, Python, Node) — front-end, back-end et <b>IoT</b>.</li>
<li>Conception &amp; mise en prod du système de <b>GTB</b> (Gestion Technique du Bâtiment) en Python — centralise et traduit les données équipements.</li>
<li>Intégration d'une <b>application Symfony</b> avec restitution dans un front-end interactif pour visualiser et piloter l'état des bâtiments.</li>
<li>Choix techniques, <b>architecture logicielle</b>, scalabilité et maintenabilité dans un environnement Docker + Linux.</li>
</ul>
</div>
</div>
<div class="xp">
<div class="num">02</div>
<div>
<h3 data-en='Fullstack Developer <span class="role-sep">— Apprenticeship</span>'>Développeur Fullstack <span class="role-sep">— Alternance</span></h3>
<div class="when">2021 — 2023 <span class="where">· talents'in &amp; between · Marseille</span></div>
<ul data-en='<li>Built a <b>REST API</b> in Java/Spring Boot encoding business rules for a complex HR product.</li><li>Consumed the APIs from a Vue.js front-end for clear end-to-end data flow ownership.</li><li>Led the <b>new tech stack</b> post-acquisition by Between: <b>Java + Spring Boot + Vue.js</b>.</li><li>Migrated dev environment (Mac → Linux) and rolled out collaborative <b>SCRUM</b> practices.</li>'>
<li>Développement d'une <b>API REST</b> en Java/Spring Boot intégrant les règles métier d'un produit RH complexe.</li>
<li>Consommation des API côté front (Vue.js) pour une vision claire du flux et des données end-to-end.</li>
<li>Pilotage de la <b>nouvelle stack technique</b> après rachat par Between : <b>Java + Spring Boot + Vue.js</b>.</li>
<li>Adaptation d'environnement de dev (Mac → Linux) et mise en place de pratiques collaboratives <b>SCRUM</b>.</li>
</ul>
</div>
</div>
<div class="xp">
<div class="num">01</div>
<div>
<h3 data-en='Frontend Developer <span class="role-sep">— Apprenticeship</span>'>Développeur Frontend <span class="role-sep">— Alternance</span></h3>
<div class="when">2019 — 2021 <span class="where">· EasyMovie · Paris</span></div>
<ul data-en='<li>Built &amp; maintained a <b>React component library</b> used across every product page.</li><li>Worked with <b>cross-functional teams</b> (PO, Design, iOS, Android, QA, DevOps) in a structured production cycle.</li><li>Full cycle <b>Develop → Pre-Prod → Production</b> with systematic peer code reviews.</li>'>
<li>Dev &amp; maintenance d'une <b>librairie de composants React</b> intégrée dans toutes les pages produit.</li>
<li>Collab avec équipes <b>pluridisciplinaires</b> (PO, Design, iOS, Android, QA, DevOps) dans un cycle de prod structuré.</li>
<li>Cycle complet <b>Develop → Pre-Prod → Production</b> avec revues de code croisées systématiques.</li>
</ul>
</div>
</div>
</div>
<div class="col-right">
<div class="col-section-title">
<span><span class="num-tag">[ 02 ]</span> &nbsp; <span data-en="Projects &amp; Freelance">Projets &amp; Freelance</span></span>
<span class="count">03 selects</span>
</div>
<div class="pj">
<div class="pj-head">
<h3>Rubis</h3>
<a class="url-mono" href="https://rubis.pro">→ rubis.pro</a>
</div>
<div class="pj-meta" data-en="SaaS B2B · Freelance · 2025 — Ongoing">SaaS B2B · Freelance · 2025 — En cours</div>
<div class="pj-desc" data-en="<b>SaaS for automated dunning of unpaid invoices</b> for French SMBs, designed and built end-to-end. Self-hosted infrastructure on Proxmox + K3s via Gitea CI."><b>SaaS de relance automatique de factures impayées</b> pour TPE-PME, conçu et développé end-to-end. Infra auto-hébergée sur Proxmox + K3s via Gitea CI.</div>
<div class="pj-stack">
<span class="chip accent">AdonisJS</span>
<span class="chip accent">React 19</span>
<span class="chip">Astro 6</span>
<span class="chip">Tailwind v4</span>
<span class="chip">Postgres</span>
<span class="chip">K3s</span>
<span class="chip">Stripe</span>
</div>
</div>
<div class="pj">
<div class="pj-head">
<h3>Rebours Studio</h3>
<a class="url-mono" href="https://rebours.studio">→ rebours.studio</a>
</div>
<div class="pj-meta" data-en="E-commerce · Freelance · 2025">E-commerce · Freelance · 2025</div>
<div class="pj-desc" data-en="Minimalist <b>showcase + e-commerce</b> site for a Paris-based art furniture studio. Custom <b>Stripe checkout</b> and premium visual identity.">Site <b>vitrine + e-commerce</b> minimaliste pour un atelier parisien de mobilier d'art. <b>Tunnel de paiement Stripe</b> sur-mesure et identité visuelle haut de gamme.</div>
<div class="pj-stack">
<span class="chip accent">Next.js</span>
<span class="chip">React</span>
<span class="chip">Tailwind</span>
<span class="chip">Stripe</span>
<span class="chip">API media</span>
</div>
</div>
<div class="pj">
<div class="pj-head">
<h3>Freedge.app</h3>
<a class="url-mono" href="https://freedge.app">→ freedge.app</a>
</div>
<div class="pj-meta" data-en="AI SaaS · Personal · 2024">SaaS IA · Perso · 2024</div>
<div class="pj-desc" data-en="<b>AI-powered recipe generation</b> end-to-end: Fastify + Prisma, <b>GPT-4 Turbo &amp; DALL·E 3</b> integration, multi-tier Stripe billing."><b>Génération de recettes par IA</b> end-to-end : Fastify + Prisma, intégration <b>GPT-4 Turbo &amp; DALL·E 3</b>, paiement Stripe multi-plans.</div>
<div class="pj-stack">
<span class="chip accent">Fastify</span>
<span class="chip accent">OpenAI</span>
<span class="chip">React</span>
<span class="chip">Prisma</span>
<span class="chip">Stripe</span>
</div>
</div>
</div>
</div>
<div class="formations">
<div class="fo">
<div class="num">01</div>
<div>
<h4 data-en='Software Architect <span style="color:var(--muted);font-weight:300;">— Master&#39;s</span>'>Architecte Logiciel <span style="color:var(--muted);font-weight:300;">— Master 2</span></h4>
<div class="school">Epitech · Marseille</div>
</div>
<div class="when">2021<br/>2023</div>
</div>
<div class="fo">
<div class="num">02</div>
<div>
<h4 data-en='Multimedia Project Manager <span style="color:var(--muted);font-weight:300;">— Bachelor&#39;s</span>'>Chef de Projet Multimédia <span style="color:var(--muted);font-weight:300;">— Bachelor</span></h4>
<div class="school">Hetic · Paris</div>
</div>
<div class="when">2018<br/>2021</div>
</div>
</div>
<div class="footer">
<div>Arthur Barré</div>
<div>arthurbarre.fr</div>
<div>06 13 17 61 17</div>
<div data-en="Available — let's build">Disponible — let's build</div>
</div>
</div>
</div>
<a class="switch-btn" href="Resume-Arthur-barre-fr.html" aria-label="Voir la version longue">
<span data-en="Long version · 3 pages">Version longue · 3 pages</span>
<span class="arrow"></span>
</a>
<button class="lang-toggle" aria-label="Toggle language">
<span class="lang-opt" data-lang="en">EN</span>
<span class="sep">/</span>
<span class="lang-opt active" data-lang="fr">FR</span>
</button>
<button class="print-btn" onclick="window.print()" aria-label="Télécharger en PDF">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter">
<path d="M12 3v12m0 0l-5-5m5 5l5-5M5 21h14"/>
</svg>
<span data-en="Download PDF">Download PDF</span>
</button>
<script>
(function() {
const STORAGE_KEY = 'cv-lang';
const detect = () => {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved === 'fr' || saved === 'en') return saved;
const nav = (navigator.language || 'fr').toLowerCase();
return nav.startsWith('fr') ? 'fr' : 'en';
};
let current = detect();
const apply = (lang) => {
document.documentElement.setAttribute('lang', lang);
document.querySelectorAll('[data-en]').forEach(el => {
if (!el.hasAttribute('data-fr')) el.setAttribute('data-fr', el.innerHTML);
el.innerHTML = lang === 'en' ? el.getAttribute('data-en') : el.getAttribute('data-fr');
});
document.querySelectorAll('.lang-toggle .lang-opt').forEach(o => {
o.classList.toggle('active', o.dataset.lang === lang);
});
document.title = lang === 'en'
? 'Arthur Barré — Fullstack Developer · Résumé'
: 'Arthur Barré — Fullstack Developer · Résumé';
};
document.addEventListener('DOMContentLoaded', () => {
apply(current);
const btn = document.querySelector('.lang-toggle');
if (!btn) return;
btn.addEventListener('click', () => {
current = current === 'fr' ? 'en' : 'fr';
localStorage.setItem(STORAGE_KEY, current);
apply(current);
});
});
})();
</script>
</body>
</html>

45
k3s/cv/deployment.yml Normal file
View File

@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cv
namespace: cv
labels:
app: cv
spec:
replicas: 1
selector:
matchLabels:
app: cv
template:
metadata:
labels:
app: cv
spec:
imagePullSecrets:
- name: gitea-registry
containers:
- name: cv
image: git.arthurbarre.fr/ordinarthur/cv:latest
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
resources:
requests:
memory: "16Mi"
cpu: "10m"
limits:
memory: "64Mi"
cpu: "100m"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 30

4
k3s/cv/namespace.yml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: cv

14
k3s/cv/service.yml Normal file
View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: cv
namespace: cv
spec:
type: NodePort
selector:
app: cv
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30111

BIN
photo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB