mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
refactor: apply prettier conf
This commit is contained in:
@@ -6,7 +6,7 @@ const config = {
|
||||
webpack(config) {
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
use: ["@svgr/webpack"]
|
||||
use: ["@svgr/webpack"],
|
||||
});
|
||||
|
||||
return config;
|
||||
@@ -15,12 +15,12 @@ const config = {
|
||||
remotePatterns: [
|
||||
{ hostname: "localhost" },
|
||||
{ hostname: "t3.gstatic.com" },
|
||||
{ hostname: "lh3.googleusercontent.com" }
|
||||
{ hostname: "lh3.googleusercontent.com" },
|
||||
],
|
||||
formats: ["image/webp"]
|
||||
formats: ["image/webp"],
|
||||
},
|
||||
reactStrictMode: false,
|
||||
output: "standalone"
|
||||
output: "standalone",
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -18,7 +18,7 @@ interface LinkFaviconProps {
|
||||
export default function LinkFavicon({
|
||||
url,
|
||||
size = 32,
|
||||
noMargin = false
|
||||
noMargin = false,
|
||||
}: LinkFaviconProps) {
|
||||
const [isFailed, setFailed] = useState<boolean>(false);
|
||||
const [isLoading, setLoading] = useState<boolean>(true);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { makeRequest } from "lib/request";
|
||||
export default function LinkItem({
|
||||
link,
|
||||
toggleFavorite,
|
||||
index
|
||||
index,
|
||||
}: {
|
||||
link: Link;
|
||||
toggleFavorite: (linkId: Link["id"]) => void;
|
||||
@@ -28,8 +28,8 @@ export default function LinkItem({
|
||||
name,
|
||||
url,
|
||||
favorite: !favorite,
|
||||
categoryId: link.category.id
|
||||
}
|
||||
categoryId: link.category.id,
|
||||
},
|
||||
})
|
||||
.then(() => toggleFavorite(link.id))
|
||||
.catch(console.error);
|
||||
@@ -45,7 +45,7 @@ export default function LinkItem({
|
||||
type: "spring",
|
||||
stiffness: 260,
|
||||
damping: 20,
|
||||
delay: index * 0.05
|
||||
delay: index * 0.05,
|
||||
}}
|
||||
>
|
||||
<LinkFavicon url={url} />
|
||||
|
||||
@@ -3,11 +3,16 @@ import { useSession } from "next-auth/react";
|
||||
import PATHS from "constants/paths";
|
||||
import styles from "./navbar.module.scss";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import Image from "next/image";
|
||||
import { TFunctionParam } from "../../types/i18next";
|
||||
|
||||
export default function Navbar() {
|
||||
const { status } = useSession();
|
||||
const { data, status } = useSession();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const avatarLabel = t("common:avatar", {
|
||||
name: data?.user?.name,
|
||||
} as TFunctionParam);
|
||||
return (
|
||||
<nav className={styles["navbar"]}>
|
||||
<ul className="reset">
|
||||
@@ -21,9 +26,21 @@ export default function Navbar() {
|
||||
<LinkTag href={PATHS.TERMS}>{t("common:terms")}</LinkTag>
|
||||
</li>
|
||||
{status === "authenticated" ? (
|
||||
<li>
|
||||
<LinkTag href={PATHS.LOGOUT}>{t("common:logout")}</LinkTag>
|
||||
</li>
|
||||
<>
|
||||
<li className={styles["user"]}>
|
||||
<Image
|
||||
src={data.user.image}
|
||||
width={24}
|
||||
height={24}
|
||||
alt={avatarLabel}
|
||||
title={avatarLabel}
|
||||
/>
|
||||
{data.user.name}
|
||||
</li>
|
||||
<li>
|
||||
<LinkTag href={PATHS.LOGOUT}>{t("common:logout")}</LinkTag>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li>
|
||||
<LinkTag href={PATHS.LOGIN}>{t("common:login")}</LinkTag>
|
||||
|
||||
@@ -8,3 +8,13 @@
|
||||
gap: 1.5em;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.navbar .user {
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
justify-content: center;
|
||||
|
||||
& img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ type ApiHandlerMethod = ({
|
||||
req,
|
||||
res,
|
||||
session,
|
||||
user
|
||||
user,
|
||||
}: {
|
||||
req: NextApiRequest;
|
||||
res: NextApiResponse;
|
||||
@@ -72,7 +72,7 @@ function errorHandler(error: any, response: NextApiResponse) {
|
||||
function handlePrismaError({
|
||||
meta,
|
||||
code,
|
||||
message
|
||||
message,
|
||||
}: PrismaClientKnownRequestError) {
|
||||
switch (code) {
|
||||
case "P2002":
|
||||
|
||||
@@ -4,16 +4,24 @@ import { i18n } from "next-i18next";
|
||||
export async function makeRequest({
|
||||
method = "GET",
|
||||
url,
|
||||
body
|
||||
}: { method?: RequestInit["method"], url: string, body?: object | any[] }): Promise<any> {
|
||||
body,
|
||||
}: {
|
||||
method?: RequestInit["method"];
|
||||
url: string;
|
||||
body?: object | any[];
|
||||
}): Promise<any> {
|
||||
nProgress.start();
|
||||
const request = await fetch(url, {
|
||||
method, body: body ? JSON.stringify(body) : undefined, headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
method,
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
nProgress.done();
|
||||
|
||||
const data = await request.json();
|
||||
return request.ok ? data : Promise.reject(data?.error || i18n.t("common:generic-error"));
|
||||
}
|
||||
return request.ok
|
||||
? data
|
||||
: Promise.reject(data?.error || i18n.t("common:generic-error"));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default async function createUser(profile: Profile) {
|
||||
return await prisma.user.create({
|
||||
data: {
|
||||
email: profile.email,
|
||||
google_id: profile.sub
|
||||
}
|
||||
google_id: profile.sub,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default async function getUserByProfileProvider(profile: Profile) {
|
||||
return await prisma.user.findFirst({
|
||||
where: {
|
||||
google_id: profile.sub,
|
||||
email: profile.email
|
||||
}
|
||||
email: profile.email,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ export default async function updateUser(profile: Profile) {
|
||||
return await prisma.user.update({
|
||||
where: {
|
||||
email: profile.email,
|
||||
google_id: profile.sub
|
||||
google_id: profile.sub,
|
||||
},
|
||||
data: {
|
||||
email: profile.email,
|
||||
google_id: profile.sub
|
||||
}
|
||||
google_id: profile.sub,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,18 @@ import createUser from "lib/user/createUser";
|
||||
import updateUser from "lib/user/updateUser";
|
||||
import prisma from "utils/prisma";
|
||||
|
||||
const authLogger = (profile: Profile, ...args: any[]) => console.log("[AUTH]", profile.email, `(${profile.name} - ${profile.sub})`, ...args);
|
||||
const authLogger = (profile: Profile, ...args: any[]) =>
|
||||
console.log(
|
||||
"[AUTH]",
|
||||
profile.email,
|
||||
`(${profile.name} - ${profile.sub})`,
|
||||
...args,
|
||||
);
|
||||
const redirectUser = (errorKey: string) => `${PATHS.LOGIN}?error=${errorKey}`;
|
||||
|
||||
const checkProvider = (provider: string) => provider === "google";
|
||||
const checkAccountDataReceived = (profile: Profile) => !!profile?.sub && !!profile?.email;
|
||||
|
||||
const checkAccountDataReceived = (profile: Profile) =>
|
||||
!!profile?.sub && !!profile?.email;
|
||||
|
||||
export const authOptions = {
|
||||
providers: [
|
||||
@@ -22,16 +28,16 @@ export const authOptions = {
|
||||
params: {
|
||||
prompt: "consent",
|
||||
access_type: "offline",
|
||||
response_type: "code"
|
||||
}
|
||||
}
|
||||
})
|
||||
response_type: "code",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async session({ session }) {
|
||||
// check if stored in session still exist in db
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { email: session.user.email }
|
||||
where: { email: session.user.email },
|
||||
});
|
||||
return user ? session : undefined;
|
||||
},
|
||||
@@ -62,12 +68,12 @@ export const authOptions = {
|
||||
console.error(error);
|
||||
return redirectUser("AUTH_ERROR");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
signIn: PATHS.LOGIN,
|
||||
error: PATHS.LOGIN,
|
||||
signOut: PATHS.LOGOUT
|
||||
}
|
||||
signOut: PATHS.LOGOUT,
|
||||
},
|
||||
} as NextAuthOptions;
|
||||
export default NextAuth(authOptions);
|
||||
|
||||
@@ -42,13 +42,17 @@ export default async function handler(
|
||||
}
|
||||
|
||||
if (isBase64Image(faviconPath)) {
|
||||
console.log("[Favicon]", `[first: ${faviconRequestUrl}]`, "base64, convert it to buffer");
|
||||
console.log(
|
||||
"[Favicon]",
|
||||
`[first: ${faviconRequestUrl}]`,
|
||||
"base64, convert it to buffer",
|
||||
);
|
||||
const buffer = convertBase64ToBuffer(faviconPath);
|
||||
return sendImage(res, {
|
||||
buffer,
|
||||
type: "image/x-icon",
|
||||
size: buffer.length,
|
||||
url: faviconPath
|
||||
url: faviconPath,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,7 +69,9 @@ export default async function handler(
|
||||
const errorMessage = error?.message || "Unable to retrieve favicon";
|
||||
console.log("[Favicon]", `[second: ${faviconRequestUrl}]`, errorMessage);
|
||||
|
||||
const readStream = createReadStream(resolve(process.cwd(), "./public/empty-image.png"));
|
||||
const readStream = createReadStream(
|
||||
resolve(process.cwd(), "./public/empty-image.png"),
|
||||
);
|
||||
res.writeHead(206);
|
||||
readStream.pipe(res);
|
||||
}
|
||||
@@ -89,15 +95,11 @@ async function downloadImageFromUrl(url: string): Promise<Favicon> {
|
||||
buffer: Buffer.from(await blob.arrayBuffer()),
|
||||
url: request.url,
|
||||
type: blob.type,
|
||||
size: blob.size
|
||||
size: blob.size,
|
||||
};
|
||||
}
|
||||
|
||||
function sendImage(res: NextApiResponse, {
|
||||
buffer,
|
||||
type,
|
||||
size
|
||||
}: Favicon) {
|
||||
function sendImage(res: NextApiResponse, { buffer, type, size }: Favicon) {
|
||||
res.setHeader("Content-Type", type);
|
||||
res.setHeader("Content-Length", size);
|
||||
res.send(buffer);
|
||||
@@ -110,13 +112,16 @@ const rels = [
|
||||
"apple-touch-icon-precomposed",
|
||||
"apple-touch-startup-image",
|
||||
"mask-icon",
|
||||
"fluid-icon"
|
||||
"fluid-icon",
|
||||
];
|
||||
|
||||
function findFaviconPath(text: string) {
|
||||
const document = parse(text);
|
||||
const favicon = Array.from(document.getElementsByTagName("link")).find(
|
||||
(element) => rels.includes(element.getAttribute("rel")) && element.getAttribute("href"));
|
||||
(element) =>
|
||||
rels.includes(element.getAttribute("rel")) &&
|
||||
element.getAttribute("href"),
|
||||
);
|
||||
|
||||
if (!favicon) {
|
||||
throw new Error("No link/href attribute found");
|
||||
|
||||
@@ -13,7 +13,7 @@ import { withAuthentication } from "utils/session";
|
||||
import { makeRequest } from "lib/request";
|
||||
|
||||
export default function PageCreateCategory({
|
||||
categoriesCount
|
||||
categoriesCount,
|
||||
}: {
|
||||
categoriesCount: number;
|
||||
}) {
|
||||
@@ -40,9 +40,11 @@ export default function PageCreateCategory({
|
||||
makeRequest({
|
||||
url: PATHS.API.CATEGORY,
|
||||
method: "POST",
|
||||
body: { name }
|
||||
body: { name },
|
||||
})
|
||||
.then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`))
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -79,8 +81,8 @@ export const getServerSideProps = withAuthentication(
|
||||
props: {
|
||||
session,
|
||||
categoriesCount,
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -36,9 +36,11 @@ export default function PageEditCategory({ category }: { category: Category }) {
|
||||
makeRequest({
|
||||
url: `${PATHS.API.CATEGORY}/${category.id}`,
|
||||
method: "PUT",
|
||||
body: { name }
|
||||
body: { name },
|
||||
})
|
||||
.then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`))
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -73,8 +75,8 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!category) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME
|
||||
}
|
||||
destination: PATHS.HOME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,8 +84,8 @@ export const getServerSideProps = withAuthentication(
|
||||
props: {
|
||||
session,
|
||||
category: JSON.parse(JSON.stringify(category)),
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { withAuthentication } from "utils/session";
|
||||
import { makeRequest } from "lib/request";
|
||||
|
||||
export default function PageRemoveCategory({
|
||||
category
|
||||
category,
|
||||
}: {
|
||||
category: Category;
|
||||
}) {
|
||||
@@ -37,7 +37,7 @@ export default function PageRemoveCategory({
|
||||
|
||||
makeRequest({
|
||||
url: `${PATHS.API.CATEGORY}/${category.id}`,
|
||||
method: "DELETE"
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((data) => router.push(PATHS.HOME))
|
||||
.catch(setError)
|
||||
@@ -90,8 +90,8 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!category) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME
|
||||
}
|
||||
destination: PATHS.HOME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ export const getServerSideProps = withAuthentication(
|
||||
props: {
|
||||
session,
|
||||
category: JSON.parse(JSON.stringify(category)),
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { withAuthentication } from "utils/session";
|
||||
import { makeRequest } from "lib/request";
|
||||
|
||||
export default function PageCreateLink({
|
||||
categories
|
||||
categories,
|
||||
}: {
|
||||
categories: Category[];
|
||||
}) {
|
||||
@@ -54,9 +54,11 @@ export default function PageCreateLink({
|
||||
makeRequest({
|
||||
url: PATHS.API.LINK,
|
||||
method: "POST",
|
||||
body: { name, url, favorite, categoryId }
|
||||
body: { name, url, favorite, categoryId },
|
||||
})
|
||||
.then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`))
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -94,7 +96,7 @@ export default function PageCreateLink({
|
||||
onChangeCallback={(value: number) => setCategoryId(value)}
|
||||
options={categories.map(({ id, name }) => ({
|
||||
label: name,
|
||||
value: id
|
||||
value: id,
|
||||
}))}
|
||||
/>
|
||||
<Checkbox
|
||||
@@ -114,8 +116,8 @@ export const getServerSideProps = withAuthentication(
|
||||
if (categories.length === 0) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME
|
||||
}
|
||||
destination: PATHS.HOME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -123,8 +125,8 @@ export const getServerSideProps = withAuthentication(
|
||||
props: {
|
||||
session,
|
||||
categories: JSON.parse(JSON.stringify(categories)),
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { makeRequest } from "lib/request";
|
||||
|
||||
export default function PageEditLink({
|
||||
link,
|
||||
categories
|
||||
categories,
|
||||
}: {
|
||||
link: Link;
|
||||
categories: Category[];
|
||||
@@ -59,7 +59,7 @@ export default function PageEditLink({
|
||||
link.url,
|
||||
name,
|
||||
submitted,
|
||||
url
|
||||
url,
|
||||
]);
|
||||
|
||||
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
||||
@@ -70,9 +70,11 @@ export default function PageEditLink({
|
||||
makeRequest({
|
||||
url: `${PATHS.API.LINK}/${link.id}`,
|
||||
method: "PUT",
|
||||
body: { name, url, favorite, categoryId }
|
||||
body: { name, url, favorite, categoryId },
|
||||
})
|
||||
.then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`))
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -109,7 +111,7 @@ export default function PageEditLink({
|
||||
onChangeCallback={(value: number) => setCategoryId(value)}
|
||||
options={categories.map(({ id, name }) => ({
|
||||
label: name,
|
||||
value: id
|
||||
value: id,
|
||||
}))}
|
||||
/>
|
||||
<Checkbox
|
||||
@@ -132,8 +134,8 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME
|
||||
}
|
||||
destination: PATHS.HOME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,8 +144,8 @@ export const getServerSideProps = withAuthentication(
|
||||
session,
|
||||
link: JSON.parse(JSON.stringify(link)),
|
||||
categories: JSON.parse(JSON.stringify(categories)),
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -33,9 +33,11 @@ export default function PageRemoveLink({ link }: { link: Link }) {
|
||||
|
||||
makeRequest({
|
||||
url: `${PATHS.API.LINK}/${link.id}`,
|
||||
method: "DELETE"
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`))
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -97,8 +99,8 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME
|
||||
}
|
||||
destination: PATHS.HOME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,8 +108,8 @@ export const getServerSideProps = withAuthentication(
|
||||
props: {
|
||||
session,
|
||||
link: JSON.parse(JSON.stringify(link)),
|
||||
...(await getServerSideTranslation(locale))
|
||||
}
|
||||
...(await getServerSideTranslation(locale)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ import Image from "next/image";
|
||||
import { FcGoogle } from "react-icons/fc";
|
||||
import styles from "styles/login.module.scss";
|
||||
import { getSession } from "utils/session";
|
||||
import { TFunctionParam } from "../types/i18next";
|
||||
|
||||
interface SignInProps {
|
||||
providers: Provider[];
|
||||
@@ -43,7 +44,7 @@ export default function SignIn({ providers }: SignInProps) {
|
||||
key={id}
|
||||
>
|
||||
<FcGoogle size={"1.5em"} />{" "}
|
||||
{t("login:continue-with", { provider: name } as undefined)}
|
||||
{t("login:continue-with", { provider: name } as TFunctionParam)}
|
||||
</ButtonLink>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user