-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
296 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,3 +40,4 @@ next-env.d.ts | |
|
||
# local | ||
.envrc | ||
*.local |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generate access code | ||
|
||
scope="user-top-read" | ||
redirect_uri="https://vahor.fr" | ||
|
||
echo "Using client_id: $SPOTIFY_CLIENT_ID" | ||
|
||
url="https://accounts.spotify.com/authorize?client_id=$SPOTIFY_CLIENT_ID&response_type=code&redirect_uri=$redirect_uri&scope=$scope" | ||
|
||
echo "Go to this URL: $url" | ||
echo "Paste the code here:" | ||
read code | ||
|
||
echo "Using code: $code" | ||
|
||
res=$(curl -s -d client_id=$SPOTIFY_CLIENT_ID -d client_secret=$SPOTIFY_CLIENT_SECRET -d grant_type=authorization_code -d code=$code -d redirect_uri=$redirect_uri https://accounts.spotify.com/api/token) | ||
|
||
echo $res | jq |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,79 @@ | ||
import A from "@/components/A"; | ||
import { CurrentlyListeningSvg } from "@/components/CurrentlyListeningSvg"; | ||
import { SpotifyTopTrackBadge } from "@/components/SpotifyTopArtist"; | ||
import { UrlBadge } from "@/components/UrlBadge"; | ||
import { JsonLd } from "@/components/jsonld/profile-page"; | ||
import { GITHUB_PROFILE, TWITTER_PROFILE } from "@/lib/constants"; | ||
import { author } from "@/lib/jsonld"; | ||
import { allPosts } from "contentlayer/generated"; | ||
import Link from "next/link"; | ||
|
||
const postCount = allPosts.length; | ||
|
||
export default function Home() { | ||
return ( | ||
<div> | ||
<main className="py-16 mx-auto container post-content"> | ||
<JsonLd jsonLd={author} /> | ||
<ul> | ||
{allPosts.map((post) => ( | ||
<li key={post._raw.flattenedPath}> | ||
<h2>{post.title}</h2> | ||
<Link href={post.url}>{post.url}</Link> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
<h1 className="text-2xl text-black dark:text-white font-semibold mb-8"> | ||
Nathan David | ||
</h1> | ||
<section> | ||
<p> | ||
<span>Hello 👋🏻</span> | ||
<span> | ||
{" "} | ||
Je suis un développeur fullstack passioné par les automatisations, | ||
la performance et la simplicité. | ||
</span> | ||
</p> | ||
</section> | ||
<section className="mt-8"> | ||
<p> | ||
Je réalise actuellement un master informatique à{" "} | ||
<UrlBadge url="https://www.mewo.fr" title="Mewo" />, effectué en | ||
alternance chez{" "} | ||
<UrlBadge url="https://www.sesamm.com/" title="SESAMm" /> où j'occupe | ||
le poste de développeur fullstack depuis plus de 2ans. | ||
</p> | ||
</section> | ||
|
||
<section className="mt-8"> | ||
<p> | ||
En parallèle, je réalise des projets personnels que je documente sur | ||
ce site. Vous pouvez retrouver mes{" "} | ||
<span className="font-semibold">{postCount} articles</span> sur la | ||
page <A href="/tag/all">blog</A>. | ||
</p> | ||
<p className="mt-2"> | ||
Pour l'instant, j'essaie d'apprendre{" "} | ||
<UrlBadge url="https://www.rust-lang.org/" title="Rust" /> et{" "} | ||
<UrlBadge url="https://go.dev/" title="Go" />. | ||
</p> | ||
</section> | ||
|
||
<section className="mt-8"> | ||
<p> | ||
En dehors de la programation, j'aime regarder des séries, lire des | ||
livres et écouter de la musique. | ||
</p> | ||
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 sm:mt-5 gap-2"> | ||
<div className="hidden sm:flex justify-end"> | ||
<CurrentlyListeningSvg /> | ||
</div> | ||
<div className="sm:hidden font-semibold"> | ||
<p>En ce moment j'écoute:</p> | ||
</div> | ||
<SpotifyTopTrackBadge /> | ||
</div> | ||
</section> | ||
|
||
<section className="mt-8"> | ||
<p> | ||
Vous pouvez me retrouver sur {/* TODO Download SVG and use it here */} | ||
<UrlBadge url={GITHUB_PROFILE} title="GitHub" />{" "} | ||
<UrlBadge url={TWITTER_PROFILE} title="Twitter" /> | ||
</p> | ||
</section> | ||
</main> | ||
); | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { getSpotifyAccessToken } from "@/lib/spotify"; | ||
import Image from "next/image"; | ||
import { z } from "zod"; | ||
|
||
const API_URL = | ||
"https://api.spotify.com/v1/me/top/tracks?time_range=short_term&limit=1&offset=0"; | ||
|
||
const itemSchema = z.object({ | ||
name: z.string(), | ||
external_urls: z.object({ | ||
spotify: z.string(), | ||
}), | ||
artists: z.array( | ||
z.object({ | ||
name: z.string(), | ||
}), | ||
), | ||
|
||
album: z.object({ | ||
name: z.string(), | ||
images: z.array( | ||
z.object({ | ||
url: z.string(), | ||
height: z.number(), | ||
width: z.number(), | ||
}), | ||
), | ||
}), | ||
}); | ||
const itemsSchema = z.array(itemSchema); | ||
|
||
async function getTopTrack() { | ||
const token = await getSpotifyAccessToken(); | ||
const res = await fetch(API_URL, { | ||
method: "GET", | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}); | ||
if (!res.ok) { | ||
console.error(await res.text()); | ||
throw new Error("Failed to fetch data from Spotify API"); | ||
} | ||
const data = await res.json(); | ||
const items = itemsSchema.parse(data.items); | ||
return items; | ||
} | ||
|
||
export async function SpotifyTopTrackBadge() { | ||
const tracks = await getTopTrack(); | ||
|
||
if (tracks.length === 0) { | ||
return null; | ||
} | ||
|
||
const topTrack = tracks[0]; | ||
|
||
return ( | ||
<a | ||
className="flex flew-row rounded-md border border-neutral-200 dark:border-neutral-700 gap-4 hover:border-neutral-300 hover:dark:border-neutral-600 bg-accent text-accent-foreground p-2" | ||
title="Ma musique préférée du moment" | ||
href={topTrack.external_urls.spotify} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
<Image | ||
src={topTrack.album.images[0].url} | ||
alt={topTrack.album.name} | ||
width={64} | ||
height={64} | ||
unoptimized={true} | ||
className="inline-block rounded-lg" | ||
/> | ||
<div> | ||
<div className="font-semibold">{topTrack.name}</div> | ||
<div className="text-sm text-neutral-700 dark:text-neutral-300"> | ||
{topTrack.artists.map((artist, i) => ( | ||
<span key={artist.name}> | ||
{artist.name} | ||
{i < topTrack.artists.length - 1 ? ", " : ""} | ||
</span> | ||
))} | ||
</div> | ||
</div> | ||
</a> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { extractMetaTags } from "@/lib/scraper"; | ||
import Image from "next/image"; | ||
|
||
interface UrlBadgeProps { | ||
url: string; | ||
title: string; | ||
favicon?: string; | ||
} | ||
|
||
export const UrlBadge = async ({ url, title, favicon }: UrlBadgeProps) => { | ||
const metadata = favicon ? { favicon, title } : await extractMetaTags(url); | ||
|
||
return ( | ||
<a | ||
href={url} | ||
className="rounded-md border border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 hover:dark:border-neutral-600 bg-accent text-accent-foreground p-1 !no-underline space-x-2 leading-4 text-sm" | ||
aria-label={title} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
<Image | ||
src={metadata.favicon} | ||
alt={metadata.title} | ||
width={16} | ||
height={16} | ||
unoptimized={true} | ||
className="inline-block rounded-lg" | ||
/> | ||
<span>{title}</span> | ||
</a> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { env } from "@/env"; | ||
import cache from "memory-cache"; | ||
|
||
const getCachedAccessToken = () => { | ||
const cachedToken = cache.get("spotifyAccessToken"); | ||
if (cachedToken) { | ||
return cachedToken; | ||
} | ||
return null; | ||
}; | ||
|
||
export async function getSpotifyAccessToken() { | ||
const cachedToken = getCachedAccessToken(); | ||
if (cachedToken) { | ||
return cachedToken; | ||
} | ||
|
||
const response = await fetch("https://accounts.spotify.com/api/token", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
}, | ||
body: new URLSearchParams({ | ||
grant_type: "refresh_token", | ||
client_id: env.SPOTIFY_CLIENT_ID, | ||
client_secret: env.SPOTIFY_CLIENT_SECRET, | ||
refresh_token: env.SPOTIFY_REFRESH_TOKEN, | ||
}), | ||
}); | ||
|
||
if (!response.ok) { | ||
console.error( | ||
"Failed to refresh Spotify access token", | ||
await response.text(), | ||
); | ||
throw new Error("Failed to refresh Spotify access token"); | ||
} | ||
|
||
const data = await response.json(); | ||
const { access_token, expires_in } = data; | ||
cache.put("spotifyAccessToken", access_token, expires_in * 1000); | ||
|
||
console.log("Spotify access token refreshed"); | ||
|
||
return access_token; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters