feat: migrate to the new system.

This commit is contained in:
Hexagonn
2024-10-20 19:44:03 +07:00
parent ff86f2afde
commit be11864c21
68 changed files with 1293 additions and 2752 deletions

View File

@@ -0,0 +1,85 @@
import redis from "@/utils/redis";
import renderCard, { Parameters } from "@/utils/renderCard";
import { isSnowflake } from "@/utils/snowflake";
import { NextRequest } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(
req: NextRequest,
options: { params: { id: string[] } },
) {
const userId = options.params.id.join("/");
if (!userId)
return Response.json(
{
data: {
error: "No ID provided.",
},
success: false,
},
{
status: 400,
},
);
if (!isSnowflake(userId))
return Response.json(
{
data: {
error: "The ID you provide is not a valid snowflake.",
},
success: false,
},
{
status: 400,
},
);
let getUser: any = {};
getUser.data = await fetch(
`https://api.lanyard.rest/v1/users/${userId}`,
).then(async (res) => {
const data = await res.json();
if (!data.success) {
getUser.error = data.error;
}
return data;
});
if (getUser.error) {
return Response.json(
{
data: getUser.error,
success: false,
},
{
status: 400,
},
);
}
const params: Parameters = Object.fromEntries(
req.nextUrl.searchParams.entries(),
);
try {
let user = await redis.hget("users", userId);
if (!user) await redis.hset("users", userId, "true");
} catch {
null;
}
return new Response(await renderCard(getUser.data, params), {
headers: {
"Content-Type": "image/svg+xml; charset=utf-8",
"content-security-policy":
"default-src 'none'; img-src * data:; style-src 'unsafe-inline'",
},
status: 200,
});
}

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

108
src/app/globals.css Normal file
View File

@@ -0,0 +1,108 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #010103;
--foreground: #171717;
}
body {
color: var(--foreground);
background: var(--background);
font-family: "Poppins", sans-serif;
}
.input {
text-align: left;
border-radius: 8px;
border: none;
width: 100%;
font-size: 0.9rem;
padding: 0.45rem 0.75rem;
color: #aaabaf;
border: solid 1px rgba(255, 255, 255, 0.2);
background: #000;
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
transition: all ease-in-out 0.1s;
&:focus {
outline: 0;
border-color: rgba(255, 255, 255, 0.5);
}
}
.output {
margin: 15px 0;
color: #aaabaf;
word-break: break-word;
border-radius: 8px;
border: solid 1px #333;
padding: 8px;
background: #000;
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
font-family: Monospace, sans-serif;
}
.action {
font-size: 0.9rem;
padding: 5px 25px;
margin-right: 10px;
border-radius: 6px;
cursor: pointer;
color: #888;
border: solid 1px #333;
background: transparent;
transition: all ease-in-out 0.1s;
&:hover {
color: #e6e6e6;
border-color: #e6e6e6;
}
&:active {
color: #fff;
border-color: #fff;
}
}
.stat {
position: absolute;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
line-height: 1rem;
bottom: 1rem;
left: 50%;
transform: translate(-50%, 0);
background: #000;
padding: 1rem 1.25rem;
color: #fff;
border-radius: 0.55rem;
text-align: center;
box-shadow: 0 2px 15px -10px #a21caf;
min-width: 400px;
@media (max-width: 400px) {
font-size: 14px;
min-width: 365px;
padding: 0.75rem 1rem;
}
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 0.55rem;
border: 2px solid transparent;
background: linear-gradient(45deg, #be123c, #6b21a8, #3730a3) border-box;
-webkit-mask:
linear-gradient(#fff 0 0) padding-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
}

35
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,35 @@
import type { Metadata } from "next";
import "./globals.css";
import { Poppins } from "next/font/google";
const poppins = Poppins({
weight: ["400", "500", "600", "700"],
subsets: ["latin"],
display: "swap",
});
export const metadata: Metadata = {
title: "Lanyard for GitHub Profile",
description:
"Utilize Lanyard to display your Discord Presence in your GitHub Profile",
openGraph: {
title: "Lanyard for GitHub Profile",
description:
"Utilize Lanyard to display your Discord Presence in your GitHub Profile",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${poppins.className} antialiased`}>
{children}
</body>
</html>
);
}

75
src/app/page.tsx Normal file
View File

@@ -0,0 +1,75 @@
"use client";
import Image from "next/image";
import React, { useState, useEffect, useRef } from "react";
import { useSmoothCount } from "use-smooth-count";
import useSWR from "swr";
import { getUserCount } from "@/utils/actions";
export default function Home() {
const [userId, setUserId] = useState<null | string>(null);
const [userError, setUserError] = useState<string>();
const [copyState, setCopyState] = useState("Copy");
const userCount = useSWR("getUserCount", getUserCount);
const countRef = useRef<HTMLDivElement | null>(null);
const counter = useSmoothCount({
ref: countRef,
target: userCount.data || 0,
duration: 3,
curve: [0, 1, 0, 1],
});
const copy = () => {
navigator.clipboard.writeText(
`[![Discord Presence](https://lanyard.cnrad.dev/api/${userId})](https://discord.com/users/${userId})`,
);
setCopyState("Copied!");
setTimeout(() => setCopyState("Copy"), 1500);
};
return (
<>
<main className="flex min-h-screen max-w-[100vw] flex-col items-center">
<div className="mt-16 w-[80%] max-w-[28rem] rounded-md">
<p className="my-2 text-left text-3xl font-semibold text-[#cecece]">
lanyard profile readme 🏷
</p>
<p className="text-base text-[#aaabaf]">
Utilize Lanyard to display your Discord Presence in your
GitHub Profile
</p>
<br />
<input
className="input"
onChange={(e) => setUserId(e.target.value)}
placeholder="Enter your Discord ID"
/>
{userId && (
<>
<div className="output">
[![Discord Presence]({window.location.origin}
/api/{userId})](https://discord.com/users/
{userId})
</div>
<button className="action" onClick={copy}>
{copyState}
</button>
<button className="action">Option</button>
</>
)}
</div>
</main>
<footer className="stat">
Lanyard Profile Readme has{" "}
<div
style={{ fontWeight: "bold", width: "3.2rem" }}
ref={countRef}
/>{" "}
total users!
</footer>
</>
);
}

9
src/utils/actions.ts Normal file
View File

@@ -0,0 +1,9 @@
"use server";
import redis from "@/utils/redis";
export async function getUserCount() {
let users = await redis.hgetall("users");
let count = Object.keys(users);
return count.length;
}

View File

@@ -1,12 +1,12 @@
// probably the messiest code i've ever written but it works so :)
import { Badges } from "../public/assets/badges/BadgesEncoded";
import { getFlags } from "./getFlags";
import * as LanyardTypes from "./LanyardTypes";
import { encodeBase64 } from "./toBase64";
import { Badges } from "#/public/assets/badges/BadgesEncoded";
import { getFlags } from "@/utils/getFlags";
import * as LanyardTypes from "@/utils/LanyardTypes";
import { encodeBase64 } from "@/utils/toBase64";
import escape from "escape-html";
type Parameters = {
export type Parameters = {
theme?: string;
bg?: string;
clanbg?: string;

View File

@@ -4,6 +4,8 @@ const EPOCH = 1420070400000; // Discord's EPOCH
export function isSnowflake(snowflake: number | string): boolean {
snowflake = Number(snowflake);
return (
Number.isInteger(+snowflake) && snowflake > 4194304 && !isNaN(new Date(snowflake / 4194304 + EPOCH).getTime())
Number.isInteger(+snowflake) &&
snowflake > 4194304 &&
!isNaN(new Date(snowflake / 4194304 + EPOCH).getTime())
);
}