diff --git a/nginx.conf b/nginx.conf index 8997778..b510ad3 100644 --- a/nginx.conf +++ b/nginx.conf @@ -9,6 +9,16 @@ http { listen 3000; listen [::]:3000; + # Redirect from non-www to www + # if ($host ~ ^(?!www\.)(?.+)$) { + # return 301 $scheme://www.$domain$request_uri; + # } + + # Redirect from www to non-www + if ($host ~ ^www\.(?.+)$) { + return 301 $scheme://$domain$request_uri; + } + server_name _; root /usr/share/nginx/html; @@ -18,13 +28,23 @@ http { gzip on; gzip_min_length 1000; 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 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 / { - 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 diff --git a/package-lock.json b/package-lock.json index 000fc3e..89eea89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@dzeio/logger": "^3", "@dzeio/object-util": "^1", "@dzeio/url-manager": "^1", - "@fontsource-variable/lexend": "^5.0.12", + "@fontsource-variable/lexend": "^5.1.1", "@tailwindcss/typography": "^0.5.10", "astro": "^3", "astro-seo": "^0.8.0", @@ -947,9 +947,9 @@ } }, "node_modules/@fontsource-variable/lexend": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/@fontsource-variable/lexend/-/lexend-5.0.12.tgz", - "integrity": "sha512-ib0Fvf4MK+Cpjq3xp9F8Xf/uigo08lPqPK/dU6s2RQk2U+IVuKB2Fbhceql7LFxC80OYlEjN+CyOZbSzqsirRQ==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@fontsource-variable/lexend/-/lexend-5.1.1.tgz", + "integrity": "sha512-ixzYkzPiJRBAxYPsjIUBLBjS1zXBMF75qQwoVyEPWOtt5uyEHdT8jTpbh/b4ibsnQvqEVb/CToifmEXJfvShag==" }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", diff --git a/package.json b/package.json index 1dbefd8..8850c9f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@dzeio/logger": "^3", "@dzeio/object-util": "^1", "@dzeio/url-manager": "^1", - "@fontsource-variable/lexend": "^5.0.12", + "@fontsource-variable/lexend": "^5.1.1", "@tailwindcss/typography": "^0.5.10", "astro": "^3", "astro-seo": "^0.8.0", diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..4722dd2 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +User-Agent: * +Disallow: +Disallow: /cv + +Sitemap: https://avior.me/sitemap.xml diff --git a/src/assets/pages/cv/me.jpg b/src/assets/pages/cv/me.jpg new file mode 100644 index 0000000..e864a32 Binary files /dev/null and b/src/assets/pages/cv/me.jpg differ diff --git a/src/components/Picture.astro b/src/components/Picture.astro index 8855d84..35da940 100644 --- a/src/components/Picture.astro +++ b/src/components/Picture.astro @@ -13,6 +13,7 @@ export interface Props extends Omit { srcDark?: ImageMetadata | string width?: number height?: number + innerClass?: string } type PictureResult = { @@ -70,7 +71,7 @@ const props = objectOmit(Astro.props, 'src', 'srcDark', 'class') {res.light.formats.map((it) => ( ))} - + ) || ( @@ -81,7 +82,7 @@ const props = objectOmit(Astro.props, 'src', 'srcDark', 'class') {res.dark.formats.map((it) => ( ))} - + ) || (res.dark && ( diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index f621904..82cf014 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -1,6 +1,8 @@ --- import Head, { type Props as HeadProps } from './Head.astro' +import '@fontsource-variable/lexend' + export interface Props extends HeadProps {} --- @@ -13,3 +15,9 @@ export interface Props extends HeadProps {} + + diff --git a/src/pages/cv.astro b/src/pages/cv.astro new file mode 100644 index 0000000..7f5ebd1 --- /dev/null +++ b/src/pages/cv.astro @@ -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 = { + 'Langages': 'amber', + 'Frameworks': 'sky', + 'Softwares': 'teal', + 'Tertiaire': 'gray' +} + +const passions: Array = [ + 'Pokémon', + 'Tennis', + 'Programation' +] + +const jobs: Array<{ + company: string + start: string + end?: string + projects: Array<{ + name: string + client?: string + description: string + technics?: Array +}> +}> = [{ + 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' + ] + }] +}] +--- + + +
+
+
+ +

Florian Bouillon

+
+

Compétences :

+ +
    + {objectMap(skills, (it, key) => ( +
  • {key}: {it.split(',').map((it) => {it})}
  • + ))} +
+
+
+

Passions :

+
    + {passions.map((it) => ( +
  • {it}
  • + ))} +
+
+
+
+
+

Expérience professionnel

+ {jobs.map((company) => ( +
+
+

{company.company}

+
{company.start} - {company.end ?? 'Aujourd\'hui'}
+
+
+ {company.projects.map((it, idx) => ( +
+ {idx > 0 && ( +
+ )} +
+

- {it.name}

+ {it.client && ( +

{it.client}

+ )} +
+

{it.description}

+ {it.technics && ( +
+

Tâches:

+
    + {it.technics?.map((tech) =>
  • {tech}
  • )} +
+
+ )} +
+ ))} +
+
+ ))} +
+
+
+
+ diff --git a/src/pages/index.astro b/src/pages/index.astro index f542cdc..c80e41c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -29,7 +29,7 @@ const clients = await Promise.all((await getCollection('clients')).map(async (it
-

Tâches :O :

+

Tâches :

{clients.map((client, index) => { diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 724a8f8..6adbefc 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -15,9 +15,7 @@ module.exports = { } }, fontFamily: { - fontFamily: { - sans: ["Lexend Variable", "Lexend", ...defaultTheme.fontFamily.sans], - }, + sans: ["Lexend Variable", "Lexend", ...defaultTheme.fontFamily.sans], }, extend: { },