generated from avior/template-web-astro
Signed-off-by: Avior <git@avior.me>
This commit is contained in:
parent
f671a21d2f
commit
851ae7a528
26
nginx.conf
26
nginx.conf
@ -9,6 +9,16 @@ http {
|
|||||||
listen 3000;
|
listen 3000;
|
||||||
listen [::]:3000;
|
listen [::]:3000;
|
||||||
|
|
||||||
|
# Redirect from non-www to www
|
||||||
|
# if ($host ~ ^(?!www\.)(?<domain>.+)$) {
|
||||||
|
# return 301 $scheme://www.$domain$request_uri;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Redirect from www to non-www
|
||||||
|
if ($host ~ ^www\.(?<domain>.+)$) {
|
||||||
|
return 301 $scheme://$domain$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
@ -18,13 +28,23 @@ http {
|
|||||||
gzip on;
|
gzip on;
|
||||||
gzip_min_length 1000;
|
gzip_min_length 1000;
|
||||||
gzip_proxied expired no-cache no-store private auth;
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
gzip_types text/html text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
error_page 404 /404.html;
|
error_page 404 /404.html;
|
||||||
error_page 500 502 503 504 /500.html;
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
|
||||||
|
# Security headers (note: temporaly unvailable "prefetch-src 'self'; ")
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'; form-action 'self'; manifest-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src data: 'self' https://img.shields.io; font-src 'self'; connect-src 'self'; base-uri 'self';";
|
||||||
|
add_header X-Frame-Options "DENY";
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header X-Content-Type-Options "nosniff";
|
||||||
|
add_header Referrer-Policy "no-referrer";
|
||||||
|
add_header Permissions-Policy "geolocation=(), microphone=(), interest-cohort=()";
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
|
||||||
|
add_header X-Download-Options "noopen";
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri.html $uri/index.html /$uri /$uri/index.html /index.html;
|
try_files $uri $uri.html $uri/index.html /$uri /$uri/index.html =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Plausible script
|
# Plausible script
|
||||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -11,7 +11,7 @@
|
|||||||
"@dzeio/logger": "^3",
|
"@dzeio/logger": "^3",
|
||||||
"@dzeio/object-util": "^1",
|
"@dzeio/object-util": "^1",
|
||||||
"@dzeio/url-manager": "^1",
|
"@dzeio/url-manager": "^1",
|
||||||
"@fontsource-variable/lexend": "^5.0.12",
|
"@fontsource-variable/lexend": "^5.1.1",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"astro": "^3",
|
"astro": "^3",
|
||||||
"astro-seo": "^0.8.0",
|
"astro-seo": "^0.8.0",
|
||||||
@ -947,9 +947,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fontsource-variable/lexend": {
|
"node_modules/@fontsource-variable/lexend": {
|
||||||
"version": "5.0.12",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/lexend/-/lexend-5.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/lexend/-/lexend-5.1.1.tgz",
|
||||||
"integrity": "sha512-ib0Fvf4MK+Cpjq3xp9F8Xf/uigo08lPqPK/dU6s2RQk2U+IVuKB2Fbhceql7LFxC80OYlEjN+CyOZbSzqsirRQ=="
|
"integrity": "sha512-ixzYkzPiJRBAxYPsjIUBLBjS1zXBMF75qQwoVyEPWOtt5uyEHdT8jTpbh/b4ibsnQvqEVb/CToifmEXJfvShag=="
|
||||||
},
|
},
|
||||||
"node_modules/@istanbuljs/schema": {
|
"node_modules/@istanbuljs/schema": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"@dzeio/logger": "^3",
|
"@dzeio/logger": "^3",
|
||||||
"@dzeio/object-util": "^1",
|
"@dzeio/object-util": "^1",
|
||||||
"@dzeio/url-manager": "^1",
|
"@dzeio/url-manager": "^1",
|
||||||
"@fontsource-variable/lexend": "^5.0.12",
|
"@fontsource-variable/lexend": "^5.1.1",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"astro": "^3",
|
"astro": "^3",
|
||||||
"astro-seo": "^0.8.0",
|
"astro-seo": "^0.8.0",
|
||||||
|
5
public/robots.txt
Normal file
5
public/robots.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
User-Agent: *
|
||||||
|
Disallow:
|
||||||
|
Disallow: /cv
|
||||||
|
|
||||||
|
Sitemap: https://avior.me/sitemap.xml
|
BIN
src/assets/pages/cv/me.jpg
Normal file
BIN
src/assets/pages/cv/me.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
@ -13,6 +13,7 @@ export interface Props extends Omit<astroHTML.JSX.ImgHTMLAttributes, 'src'> {
|
|||||||
srcDark?: ImageMetadata | string
|
srcDark?: ImageMetadata | string
|
||||||
width?: number
|
width?: number
|
||||||
height?: number
|
height?: number
|
||||||
|
innerClass?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PictureResult = {
|
type PictureResult = {
|
||||||
@ -70,7 +71,7 @@ const props = objectOmit(Astro.props, 'src', 'srcDark', 'class')
|
|||||||
{res.light.formats.map((it) => (
|
{res.light.formats.map((it) => (
|
||||||
<source srcset={it.img.src} type={`image/${it.format}`} />
|
<source srcset={it.img.src} type={`image/${it.format}`} />
|
||||||
))}
|
))}
|
||||||
<img src={res.light.src.src} />
|
<img src={res.light.src.src} class={Astro.props.innerClass} />
|
||||||
</picture>
|
</picture>
|
||||||
) || (
|
) || (
|
||||||
<img {...props} class:list={[Astro.props.class, {'dark:hidden': res.dark}]} src={res.light.src as string} />
|
<img {...props} class:list={[Astro.props.class, {'dark:hidden': res.dark}]} src={res.light.src as string} />
|
||||||
@ -81,7 +82,7 @@ const props = objectOmit(Astro.props, 'src', 'srcDark', 'class')
|
|||||||
{res.dark.formats.map((it) => (
|
{res.dark.formats.map((it) => (
|
||||||
<source srcset={it.img.src} type={`image/${it.format}`} />
|
<source srcset={it.img.src} type={`image/${it.format}`} />
|
||||||
))}
|
))}
|
||||||
<img src={res.dark.src.src} />
|
<img src={res.dark.src.src} class={Astro.props.innerClass} />
|
||||||
</picture>
|
</picture>
|
||||||
) || (res.dark && (
|
) || (res.dark && (
|
||||||
<img {...props} class:list={[Astro.props.class, 'hidden', 'dark:block']} src={res.dark.src as string} />
|
<img {...props} class:list={[Astro.props.class, 'hidden', 'dark:block']} src={res.dark.src as string} />
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
import Head, { type Props as HeadProps } from './Head.astro'
|
import Head, { type Props as HeadProps } from './Head.astro'
|
||||||
|
|
||||||
|
import '@fontsource-variable/lexend'
|
||||||
|
|
||||||
export interface Props extends HeadProps {}
|
export interface Props extends HeadProps {}
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -13,3 +15,9 @@ export interface Props extends HeadProps {}
|
|||||||
<slot />
|
<slot />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
<style is:global>
|
||||||
|
html {
|
||||||
|
font-weight: 350;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
158
src/pages/cv.astro
Normal file
158
src/pages/cv.astro
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
---
|
||||||
|
import Base from 'layouts/Base.astro'
|
||||||
|
import Picture from 'components/Picture.astro'
|
||||||
|
import Me from 'assets/pages/cv/me.jpg'
|
||||||
|
import { objectMap } from '@dzeio/object-util'
|
||||||
|
|
||||||
|
const skills = {
|
||||||
|
'Langages': 'Typescript/JavaScript, Web (HTML/CSS), Kotlin, C++',
|
||||||
|
'Frameworks': 'Astro, NextJS, Android (MVVM), PlatformIO',
|
||||||
|
'Softwares': 'Git, VSCode, Zed',
|
||||||
|
'Tertiaire': 'Gestion de projet, Encadrement de collaborateurs'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const skillColor: Record<keyof typeof skills, string> = {
|
||||||
|
'Langages': 'amber',
|
||||||
|
'Frameworks': 'sky',
|
||||||
|
'Softwares': 'teal',
|
||||||
|
'Tertiaire': 'gray'
|
||||||
|
}
|
||||||
|
|
||||||
|
const passions: Array<string> = [
|
||||||
|
'Pokémon',
|
||||||
|
'Tennis',
|
||||||
|
'Programation'
|
||||||
|
]
|
||||||
|
|
||||||
|
const jobs: Array<{
|
||||||
|
company: string
|
||||||
|
start: string
|
||||||
|
end?: string
|
||||||
|
projects: Array<{
|
||||||
|
name: string
|
||||||
|
client?: string
|
||||||
|
description: string
|
||||||
|
technics?: Array<string>
|
||||||
|
}>
|
||||||
|
}> = [{
|
||||||
|
company: 'Aptatio',
|
||||||
|
start: '2019-09-30',
|
||||||
|
projects: [{
|
||||||
|
name: 'Ifremer LOOP',
|
||||||
|
client: 'Ifremer',
|
||||||
|
description: 'Développement d’une application de gestion des réponses des appels à données et participations aux groupes internationaux',
|
||||||
|
technics: [
|
||||||
|
'Gestion de projet',
|
||||||
|
'Conception de logiciel',
|
||||||
|
'Développement de logiciel Web (Framework Astro & Typescript)'
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
name: 'Gwaleen',
|
||||||
|
client: 'Ifremer & Aptatio',
|
||||||
|
description: 'Développement de plusieurs modules logiciel des Ichtyomètre éléctronique Gwaleen',
|
||||||
|
technics: [
|
||||||
|
'Création et maintenance d\'un Logiciel Android de Gestion de donnée provenant d\'Ichtyomètres Connecté (Kotlin)',
|
||||||
|
'Conception et Création d\'un protocol de communication entre 2 appareil Android en bluetooth pour de la donnée Vidéo (Kotlin)',
|
||||||
|
'Gestion et création du code embarqué dans les Ichtyomètres Gwaleen (C++)'
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
name: 'ImmerCité',
|
||||||
|
client: 'La cité des congrès de Nantes',
|
||||||
|
description: 'Création d\'ImmerCité, un logiciel immersif pour la présentation d\'événements, primé au concours international des Cités des Congrès et lauréat du 1er prix Innovation Award à la conférence annuelle de l\'AIPC au Costa Rica.',
|
||||||
|
technics: [
|
||||||
|
'Conception de logiciel',
|
||||||
|
'Développement de logiciel Web (Framework NextJS & Typescript)',
|
||||||
|
'Création d\'un engine de gestion 2D pour le placement et l\'automatisation des objets dans les salles de la cité',
|
||||||
|
'Gestion de la communication et synchronisation entre plusieurs ordinateurs (WebSocket & ExpressJS)',
|
||||||
|
'Programation de la 3D dans Unity puis Godot (C#, GDScript)'
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
name: 'Gestion de l\'infrastructure',
|
||||||
|
description: 'Gestion de l\'infrastructure Réseau de l\'entreprise',
|
||||||
|
technics: [
|
||||||
|
'mise en place d\'un SSO (Single sign-on)',
|
||||||
|
'Gestion de la donnée utilisateur/client',
|
||||||
|
'Mise en ligne des logiciels Client',
|
||||||
|
'Maintenance Préventive des serveurs'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
company: 'Auto-Entreprise',
|
||||||
|
start: '2019-04-01',
|
||||||
|
projects: [{
|
||||||
|
name: 'TCGdex',
|
||||||
|
description: 'Développement de TCGdex, une API open source multilingue (14 langues) pour le jeu de cartes Pokémon JCC, offrant des fonctionnalités avancées aux développeurs et amateurs du jeu.',
|
||||||
|
technics: [
|
||||||
|
'Récupération des données depuis une multitude de sources',
|
||||||
|
'Création et maintenance d\'une API avec + de 4 Millions de requettes par mois',
|
||||||
|
'Mise en place d\'outillages pour simplifier l\'usage de l\'API aux développeurs',
|
||||||
|
'Maintenance et gestion d\'une communauté autour du jeux de carte Pokémon'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
---
|
||||||
|
|
||||||
|
<Base>
|
||||||
|
<main class="container py-8">
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<div class="w-1/4 flex flex-col gap-4">
|
||||||
|
<Picture src={Me} innerClass="rounded-xl" />
|
||||||
|
<h1 class="font-bold text-5xl text-center">Florian Bouillon</h1>
|
||||||
|
<div>
|
||||||
|
<p>Compétences :</p>
|
||||||
|
<div class="hidden bg-amber-300 bg-sky-300 bg-teal-300"></div>
|
||||||
|
<ul class="pl-6 list-disc">
|
||||||
|
{objectMap(skills, (it, key) => (
|
||||||
|
<li>{key}: {it.split(',').map((it) => <span class:list={[`bg-${skillColor[key]}-300`, "inline-block px-2 py-1 m-1 text-black rounded-xl"]}>{it}</span>)}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>Passions :</p>
|
||||||
|
<ul class="pl-2 list-disc">
|
||||||
|
{passions.map((it) => (
|
||||||
|
<li>{it}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-3/4">
|
||||||
|
<div class="">
|
||||||
|
<h2 class="text-4xl font-bold">Expérience professionnel</h2>
|
||||||
|
{jobs.map((company) => (
|
||||||
|
<div class="mt-6">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h2 class="text-2xl font-semibold">{company.company}</h2>
|
||||||
|
<div class="italic">{company.start} - {company.end ?? 'Aujourd\'hui'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-2">
|
||||||
|
{company.projects.map((it, idx) => (
|
||||||
|
<div>
|
||||||
|
{idx > 0 && (
|
||||||
|
<hr class="my-2 border-t-2 border-gray-500" />
|
||||||
|
)}
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<p class="text-xl font-[550]">- {it.name}</p>
|
||||||
|
{it.client && (
|
||||||
|
<p>{it.client}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 pl-4">{it.description}</p>
|
||||||
|
{it.technics && (
|
||||||
|
<div class="pl-4 mt-2">
|
||||||
|
<p>Tâches:</p>
|
||||||
|
<ul class="list-disc pl-6">
|
||||||
|
{it.technics?.map((tech) => <li>{tech}</li>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</Layout>
|
@ -29,7 +29,7 @@ const clients = await Promise.all((await getCollection('clients')).map(async (it
|
|||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="prose dark:prose-invert max-w-none">
|
<div class="prose dark:prose-invert max-w-none">
|
||||||
<p class="font-semibold">Tâches :O :</p>
|
<p class="font-semibold">Tâches :</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{clients.map((client, index) => {
|
{clients.map((client, index) => {
|
||||||
|
@ -14,11 +14,9 @@ module.exports = {
|
|||||||
'2xl': '6rem',
|
'2xl': '6rem',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fontFamily: {
|
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Lexend Variable", "Lexend", ...defaultTheme.fontFamily.sans],
|
sans: ["Lexend Variable", "Lexend", ...defaultTheme.fontFamily.sans],
|
||||||
},
|
},
|
||||||
},
|
|
||||||
extend: {
|
extend: {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user