Fix build errors

This commit is contained in:
Fergal Moran
2024-09-29 21:23:03 +01:00
parent 919bf6cbf2
commit 84f6622e63
11 changed files with 63 additions and 274 deletions

View File

@@ -1,3 +1,10 @@
{
"reject": ["eslint"]
}
"reject": [
"eslint",
"typescript",
"@types/eslint",
"@typescript-eslint/eslint-plugin",
"@typescript-eslint/parser",
"eslint-config-next"
]
}

BIN
bun.lockb

Binary file not shown.

View File

@@ -13,7 +13,6 @@
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"magicui": "@/components/magicui"
"ui": "@/components/ui"
}
}

View File

@@ -100,21 +100,25 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@types/eslint": "^8.56.10",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.4",
"eslint-plugin-drizzle": "^0.2.3",
"@faker-js/faker": "^9.0.3",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
"@types/bun": "latest",
"@types/eslint": "^9.6.1",
"@types/jest": "^29.5.13",
"@types/node": "^22.7.4",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"drizzle-kit": "^0.24.2",
"eslint": "8.57.0",
"eslint-config-next": "^14.2.13",
"eslint-plugin-drizzle": "^0.2.3",
"jest": "^29.7.0",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
@@ -122,7 +126,7 @@
"tailwindcss": "^3.4.13",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
"typescript": "^5.5.3"
},
"ct3aMetadata": {
"initVersion": "7.37.0"

View File

@@ -1,101 +0,0 @@
import { POST } from "./route";
import { NextRequest, NextResponse } from "next/server";
import { StatusCodes } from "http-status-codes";
import fs from "fs";
import path from "path";
import { env } from "@/env";
const mockGetServerSession = require("next-auth").getServerSession;
jest.mock("next-auth", () => ({
getServerSession: jest.fn(),
}));
jest.mock("fs");
jest.mock("path");
describe("POST /api/upload/post", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should return UNAUTHORIZED if session is not found", async () => {
mockGetServerSession.mockResolvedValue(null);
const request = new NextRequest(
new URL(`${env.NEXT_PUBLIC_SITE_URL}/api/upload/post`),
);
const response = await POST(request);
expect(response).toEqual(
NextResponse.json({
error: "Unauthorized",
status: StatusCodes.UNAUTHORIZED,
}),
);
});
it("should return BAD_REQUEST if id is not provided", async () => {
mockGetServerSession.mockResolvedValue({ user: { name: "test" } });
const request = new NextRequest(
new URL(`${env.NEXT_PUBLIC_SITE_URL}/api/upload/post`),
);
const response = await POST(request);
expect(response).toEqual(
NextResponse.json({
error: "No post id in query",
status: StatusCodes.BAD_REQUEST,
}),
);
});
it("should return BAD_REQUEST if file is not found in form data", async () => {
mockGetServerSession.mockResolvedValue({ user: { name: "test" } });
const url = new URL(`${env.NEXT_PUBLIC_SITE_URL}/api/upload/post?id=123`);
const request = new NextRequest(url);
request.formData = jest.fn().mockResolvedValue(new FormData());
const response = await POST(request);
expect(response).toEqual(
NextResponse.json({
error: "Cannot find file in form data",
status: StatusCodes.BAD_REQUEST,
}),
);
});
it("should save the file and return success response", async () => {
mockGetServerSession.mockResolvedValue({ user: { name: "test" } });
const url = new URL(`${env.NEXT_PUBLIC_SITE_URL}/api/upload/post?id=123`);
const request = new NextRequest(url);
const file = new Blob(["file content"], { type: "image/png" });
const formData = new FormData();
formData.append("image", file, "test.png");
request.formData = jest.fn().mockResolvedValue(formData);
const buffer = Buffer.from(await file.arrayBuffer());
const filePath = "123.png";
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
jest.spyOn(path, 'resolve').mockReturnValue(`/mocked/path/${filePath}`);
const response = await POST(request);
expect(fs.writeFileSync).toHaveBeenCalledWith(
`/mocked/path/${filePath}`,
buffer,
);
expect(response).toEqual(
NextResponse.json({
success: true,
url: env.NEXT_PUBLIC_SITE_URL + `/i/${filePath}`,
}),
);
});
});

View File

@@ -1,79 +0,0 @@
import { ReactNode } from "react";
import { ArrowRightIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
const BentoGrid = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
return (
<div
className={cn(
"grid w-full auto-rows-[22rem] grid-cols-3 gap-4",
className,
)}
>
{children}
</div>
);
};
const BentoCard = ({
name,
className,
background,
Icon,
description,
href,
cta,
}: {
name: string;
className: string;
background: ReactNode;
Icon: any;
description: string;
href: string;
cta: string;
}) => (
<div
key={name}
className={cn(
"group relative col-span-3 flex flex-col justify-between overflow-hidden rounded-xl",
// light styles
"bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
// dark styles
"transform-gpu dark:bg-black dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]",
className,
)}
>
<div>{background}</div>
<div className="pointer-events-none z-10 flex transform-gpu flex-col gap-1 p-6 transition-all duration-300 group-hover:-translate-y-10">
<Icon className="h-12 w-12 origin-left transform-gpu text-neutral-700 transition-all duration-300 ease-in-out group-hover:scale-75" />
<h3 className="text-xl font-semibold text-neutral-700 dark:text-neutral-300">
{name}
</h3>
<p className="max-w-lg text-neutral-400">{description}</p>
</div>
<div
className={cn(
"pointer-events-none absolute bottom-0 flex w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100",
)}
>
<Button variant="ghost" asChild size="sm" className="pointer-events-auto">
<a href={href}>
{cta}
<ArrowRightIcon className="ml-2 h-4 w-4" />
</a>
</Button>
</div>
<div className="pointer-events-none absolute inset-0 transform-gpu transition-all duration-300 group-hover:bg-black/[.03] group-hover:dark:bg-neutral-800/10" />
</div>
);
export { BentoCard, BentoGrid };

View File

@@ -1,39 +0,0 @@
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
interface BlurIntProps {
word: string;
className?: string;
variant?: {
hidden: { filter: string; opacity: number };
visible: { filter: string; opacity: number };
};
duration?: number;
}
const BlurIn = ({ word, className, variant, duration = 1 }: BlurIntProps) => {
const defaultVariants = {
hidden: { filter: "blur(10px)", opacity: 0 },
visible: { filter: "blur(0px)", opacity: 1 },
};
const combinedVariants = variant || defaultVariants;
return (
<motion.h1
initial="hidden"
animate="visible"
transition={{ duration }}
variants={combinedVariants}
className={cn(
"font-display text-center text-4xl font-bold tracking-[-0.02em] drop-shadow-sm md:text-7xl md:leading-[5rem]",
className,
)}
>
{word}
</motion.h1>
);
};
export default BlurIn;

View File

@@ -27,7 +27,7 @@ const PostActions: React.FC<PostActionsProps> = ({ post }) => {
title="Downvote"
action={async () => {
await vote.mutateAsync({ slug: post.slug, up: false });
voteCount.refetch();
await voteCount.refetch();
}}
icon={<Icons.down className="h-6 w-6" />}
/>
@@ -35,7 +35,7 @@ const PostActions: React.FC<PostActionsProps> = ({ post }) => {
title="Favourite"
action={async () => {
await vote.mutateAsync({ slug: post.slug, up: false });
voteCount.refetch();
await voteCount.refetch();
}}
icon={<Icons.heart className="h-6 w-6" />}
/>

View File

@@ -1,8 +1,7 @@
import { api } from "@/trpc/server";
import BlurIn from "./magicui/blur-in";
import Link from "next/link";
import { Icons } from "./icons";
import { Post } from "@/lib/models/post";
import { type Post } from "@/lib/models/post";
import {
Card,
CardContent,
@@ -12,14 +11,15 @@ import {
} from "@/components/ui/card";
import { Button } from "./ui/button";
import VoteCount from "./widgets/vote-count";
import Loading from "@/components/widgets/loading";
export const TrendingPosts: React.FC = async () => {
const trendingPosts = await api.post.getTrending();
return trendingPosts.length !== 0 ? (
<div className="masonry sm:masonry-sm md:masonry-md lg:masonry-lg">
{trendingPosts.map((post: Post) => (
<div className="py-2">
<Link href={`/post/${post.slug}`} key={post.slug}>
<div className="py-2" key={post.slug}>
<Link href={`/post/${post.slug}`}>
<Card className="overflow-hidden">
<CardHeader className="p-0">
<img
@@ -52,9 +52,6 @@ export const TrendingPosts: React.FC = async () => {
))}
</div>
) : (
<BlurIn
word="No images yet"
className="scroll-m-20 text-3xl font-bold tracking-tight"
/>
<Loading />
);
};

View File

@@ -14,6 +14,7 @@ const VoteCount: React.FC<VoteCountProps> = ({ post }) => {
return (
<span>
{voteCount.data ? (
// eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions
`${voteCount.data.voteCount} votes`
) : (
<Icons.spinner className="animate-spin" />

View File

@@ -1,38 +1,38 @@
import { NextRequest, NextResponse, URLPattern } from "next/server";
// import { type NextRequest, NextResponse, URLPattern } from "next/server";
export { default } from "next-auth/middleware";
export const config = {
matcher: ["/upload(.*)", "/i/:id(.*)"],
};
const PATTERNS: [
URLPattern,
({ pathname }: { pathname: { groups: any } }) => any,
][] = [
[
new URLPattern({ pathname: "/i/:id" }),
({ pathname }: { pathname: { groups: any } }) => pathname.groups,
],
];
// export const config = {
// matcher: ["/upload(.*)", "/i/:id(.*)"],
// };
// const PATTERNS: [
// URLPattern,
// ({ pathname }: { pathname: { groups: any } }) => any,
// ][] = [
// [
// new URLPattern({ pathname: "/i/:id" }),
// ({ pathname }: { pathname: { groups: any } }) => pathname.groups,
// ],
// ];
const params = (url: string) => {
const input = url.split("?")[0];
let result = {};
// const params = (url: string) => {
// const input = url.split("?")[0];
// let result = {};
for (const [pattern, handler] of PATTERNS) {
if (!pattern) return;
const patternResult = pattern.exec(input);
if (patternResult !== null && "pathname" in patternResult) {
result = handler(patternResult);
break;
}
}
return result || {};
};
export async function middleware(request: NextRequest, response: NextResponse) {
const { id } = params(request.url) as { id?: string };
if (!id) {
return NextResponse.next();
}
return NextResponse.rewrite(new URL(`/uploads/${id}`, request.url));
}
// for (const [pattern, handler] of PATTERNS) {
// if (!pattern) return;
// const patternResult = pattern.exec(input);
// if (patternResult !== null && "pathname" in patternResult) {
// result = handler(patternResult);
// break;
// }
// }
// return result || {};
// };
// export async function middleware(request: NextRequest, response: NextResponse) {
// const { id } = params(request.url) as { id?: string };
// if (!id) {
// return NextResponse.next();
// }
// return NextResponse.rewrite(new URL(`/uploads/${id}`, request.url));
// }