mirror of
https://github.com/fergalmoran/opengifame.git
synced 2025-12-22 09:38:44 +00:00
Fix build errors
This commit is contained in:
11
.ncurc.json
11
.ncurc.json
@@ -1,3 +1,10 @@
|
||||
{
|
||||
"reject": ["eslint"]
|
||||
}
|
||||
"reject": [
|
||||
"eslint",
|
||||
"typescript",
|
||||
"@types/eslint",
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"@typescript-eslint/parser",
|
||||
"eslint-config-next"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"magicui": "@/components/magicui"
|
||||
"ui": "@/components/ui"
|
||||
}
|
||||
}
|
||||
18
package.json
18
package.json
@@ -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"
|
||||
|
||||
@@ -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}`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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 };
|
||||
@@ -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;
|
||||
@@ -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" />}
|
||||
/>
|
||||
|
||||
@@ -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 />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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));
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user