app router

This commit is contained in:
2025-06-20 05:15:23 +02:00
parent efc88bb5b8
commit e3326998c4
24 changed files with 391 additions and 383 deletions
-1
View File
@@ -1 +0,0 @@
public-hoist-pattern[]=*@nextui-org/*
-7
View File
@@ -1,7 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"jsxSingleQuote": true
}
+4 -3
View File
@@ -1,16 +1,17 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json", "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"vcs": { "vcs": {
"enabled": false, "enabled": true,
"clientKind": "git", "clientKind": "git",
"useIgnoreFile": false "useIgnoreFile": true
}, },
"files": { "files": {
"ignoreUnknown": false "ignoreUnknown": false
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"indentStyle": "tab" "indentStyle": "tab",
"lineWidth": 80
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,
+1 -1
View File
@@ -4,4 +4,4 @@ const config = {
}, },
}; };
module.exports = config; export default config;
+120
View File
@@ -0,0 +1,120 @@
import { type NextRequest, NextResponse } from "next/server";
import { subjects } from "@/utils/subjects";
export async function GET(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
const vizsgatargy = searchParams.get("vizsgatargy");
const ev = searchParams.get("ev");
const idoszak = searchParams.get("idoszak");
const szint = searchParams.get("szint");
if (!vizsgatargy || !ev || !idoszak || !szint) {
const missingParams = [];
if (!vizsgatargy) missingParams.push("vizsgatargy");
if (!ev) missingParams.push("ev");
if (!idoszak) missingParams.push("idoszak");
if (!szint) missingParams.push("szint");
return NextResponse.json(
{ error: `Hiányzó paraméterek: ${missingParams.join(", ")}` },
{ status: 400 },
);
}
if (parseInt(ev, 10) <= 2012) {
return NextResponse.json({ error: "Érvénytelen év" }, { status: 400 });
}
const validSubjects = subjects.map((subject) => subject.value);
if (!validSubjects.includes(vizsgatargy)) {
return NextResponse.json(
{ error: "Érvénytelen vizsgatárgy" },
{ status: 400 },
);
}
let honap: string;
switch (idoszak) {
case "osz":
honap = "okt";
break;
case "tavasz":
honap = "maj";
break;
default:
return NextResponse.json(
{ error: "Érvénytelen időszak" },
{ status: 400 },
);
}
let prefix: string;
switch (szint) {
case "emelt":
prefix = `e_${vizsgatargy}`;
break;
case "kozep":
prefix = `k_${vizsgatargy}`;
break;
default:
return NextResponse.json(
{ error: "Érvénytelen szint" },
{ status: 400 },
);
}
const protocol =
req.headers.get("x-forwarded-proto") === "https" ? "https" : "http";
const host = req.headers.get("host");
const baseUrl = `https://dload-oktatas.educatio.hu/erettsegi/feladatok_${ev}${idoszak}_${szint}/`;
const proxiedUrl = `${protocol}://${host}/api/proxy?link=${encodeURI(baseUrl)}`;
const shortev = ev.slice(-2);
const feladat = "fl";
const utmutato = "ut";
const forras = "for";
const megoldas = "meg";
let flPdfUrl: string | undefined,
utPdfUrl: string | undefined,
flZipUrl: string | undefined,
utZipUrl: string | undefined,
flMp3Url: string | undefined;
switch (vizsgatargy) {
case "inf":
case "infoism":
case "digkult":
flZipUrl = `${baseUrl}${prefix}${forras}_${shortev}${honap}_${feladat}.zip`;
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utZipUrl = `${baseUrl}${prefix}${megoldas}_${shortev}${honap}_${utmutato}.zip`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
break;
case "angol":
case "nemet":
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
flMp3Url = `${baseUrl}${prefix}_${shortev}${honap}_${feladat}.mp3`;
break;
default:
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
break;
}
return NextResponse.json(
{ flPdfUrl, utPdfUrl, flZipUrl, utZipUrl, flMp3Url },
{
status: 200,
headers: { "Cache-Control": "s-maxage=31536000" },
},
);
} catch (e: unknown) {
console.error("An unexpected error occurred in the API route:", e);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
}
}
+79
View File
@@ -0,0 +1,79 @@
import { type NextRequest, NextResponse } from "next/server";
import { Agent, fetch } from "undici";
const insecureAgent = new Agent({
connect: {
rejectUnauthorized: false,
},
});
export async function GET(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
const link = searchParams.get("link");
if (!link) {
return NextResponse.json(
{ error: "Hiányzó paraméter: link" },
{ status: 400 },
);
}
let url: URL;
try {
url = new URL(link);
} catch (_error) {
return NextResponse.json(
{ error: "Érvénytelen link formátum" },
{ status: 400 },
);
}
if (url.hostname !== "dload-oktatas.educatio.hu") {
return NextResponse.json({ error: "Érvénytelen link" }, { status: 400 });
}
const externalResponse = await fetch(link, {
method: "GET",
dispatcher: insecureAgent,
});
if (!externalResponse.ok) {
return NextResponse.json(
{ error: "Hiba történt a külső forrás lekérése során." },
{ status: externalResponse.status },
);
}
const body = await externalResponse.arrayBuffer();
const contentType = externalResponse.headers.get("content-type");
const headers = new Headers();
headers.set("Cache-Control", "s-maxage=31536000, stale-while-revalidate");
if (contentType) {
headers.set("Content-Type", contentType);
if (contentType === "application/pdf") {
const filename = url.pathname.split("/").pop() ?? "document.pdf";
headers.set("Content-Disposition", `inline; filename="${filename}"`);
}
}
return new Response(body, {
status: 200,
headers: headers,
});
} catch (e: unknown) {
console.error("Proxy Error:", e);
if (e instanceof Error) {
return NextResponse.json(
{ error: "Internal Server Error", message: e.message },
{ status: 500 },
);
}
return NextResponse.json(
{ error: "An unexpected internal error occurred" },
{ status: 500 },
);
}
}
+73
View File
@@ -0,0 +1,73 @@
import { type NextRequest, NextResponse } from "next/server";
import { Agent, fetch } from "undici";
const insecureAgent = new Agent({
connect: {
rejectUnauthorized: false,
},
});
const ALLOWED_HOSTS = [
"localhost:3000",
"erettsegi.albert.lol",
"dload-oktatas.educatio.hu",
];
export async function GET(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
const link = searchParams.get("link");
if (!link) {
return NextResponse.json(
{ error: "Hiányzó paraméter: link" },
{ status: 400 },
);
}
let url: URL;
try {
url = new URL(link);
} catch (_error) {
return NextResponse.json(
{ error: "Érvénytelen link formátum" },
{ status: 400 },
);
}
if (!ALLOWED_HOSTS.includes(url.host)) {
return NextResponse.json({ error: "Érvénytelen link" }, { status: 400 });
}
const response = await fetch(link, {
method: "HEAD",
dispatcher: insecureAgent,
});
return NextResponse.json({ status: response.status }, { status: 200 });
} catch (e: unknown) {
console.error("Validation Error:", e);
if (e instanceof Error) {
const cause = e.cause as { code?: unknown } | undefined;
return NextResponse.json(
{
error: "Internal Server Error",
message: e.message,
cause: cause?.code ? String(cause.code) : undefined,
stack: process.env.NODE_ENV === "development" ? e.stack : undefined,
},
{ status: 500 },
);
}
return NextResponse.json(
{
error: "Internal Server Error",
message: "An unexpected error occurred",
details: String(e),
},
{ status: 500 },
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import type { Metadata, Viewport } from "next";
import Script from "next/script";
import { Providers } from "@/app/providers";
import { Inter } from "next/font/google";
import "@/styles/globals.css";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
export const metadata: Metadata = {
metadataBase: new URL("https://erettsegi.albert.lol"),
title: "Érettségi kereső",
description: "Egyszerű keresés és letöltés az érettségi feladatsorokhoz. 🏫",
icons: {
icon: "/favicon.ico",
},
openGraph: {
title: "Érettségi kereső",
description:
"Egyszerű keresés és letöltés az érettségi feladatsorokhoz. 🏫",
url: "https://erettsegi.albert.lol",
images: "/logo.png",
},
};
export const viewport: Viewport = {
themeColor: "#121212",
width: "device-width",
initialScale: 1,
maximumScale: 1,
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="hu" suppressHydrationWarning>
<Script
defer
src="https://analytics.albert.lol/script.js"
data-website-id="7b196f47-39c9-4b8e-8dfd-b6e707282eea"
/>
<body className={`${inter.variable} antialiased font-sans`}>
<Providers>{children}</Providers>
</body>
</html>
);
}
+7 -11
View File
@@ -1,14 +1,10 @@
"use client";
import { Button } from "@heroui/button"; import { Button } from "@heroui/button";
import { useRouter } from "next/router";
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
import Link from "next/link";
export default function ErrorPage() { export default function NotFound() {
const router = useRouter();
const handleBack = () => {
router.push("/");
};
return ( return (
<> <>
<main className="dark:bg-[#121212] text-foreground bg-background py-5"> <main className="dark:bg-[#121212] text-foreground bg-background py-5">
@@ -22,9 +18,9 @@ export default function ErrorPage() {
<div className="text-2xl font-semibold text-gray-600"> <div className="text-2xl font-semibold text-gray-600">
<p className="mt-2">Az keresett oldal nem található.</p> <p className="mt-2">Az keresett oldal nem található.</p>
<p className="mt-8 text-center"> <p className="mt-8 text-center">
<Button color="primary" onPress={handleBack}> <Link href="/">
Vissza <Button color="primary">Vissza</Button>
</Button> </Link>
</p> </p>
</div> </div>
</div> </div>
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { ButtonGroup, Divider } from "@heroui/react"; import { ButtonGroup, Divider } from "@heroui/react";
import { useEffect } from "react"; import { useEffect } from "react";
import { Mp3Button, PdfButton, ZipButton } from "@/components/Buttons"; import { Mp3Button, PdfButton, ZipButton } from "@/components/Buttons";
+14
View File
@@ -0,0 +1,14 @@
"use client";
import { HeroUIProvider } from "@heroui/react";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<HeroUIProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem={true}>
{children}
</ThemeProvider>
</HeroUIProvider>
);
}
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { Button } from "@heroui/react"; import { Button } from "@heroui/react";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import type { ButtonProps } from "@/utils/props"; import type { ButtonProps } from "@/utils/props";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { Source } from "@/components/Source"; import { Source } from "@/components/Source";
import { ThemeSwitcher } from "@/components/ThemeSwitcher"; import { ThemeSwitcher } from "@/components/ThemeSwitcher";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { Select, SelectItem } from "@heroui/react"; import { Select, SelectItem } from "@heroui/react";
import type { SelectorProps } from "@/utils/props"; import type { SelectorProps } from "@/utils/props";
import type { ChangeEvent } from "react"; import type { ChangeEvent } from "react";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { Button } from "@heroui/button"; import { Button } from "@heroui/button";
import { VscGithubInverted } from "react-icons/vsc"; import { VscGithubInverted } from "react-icons/vsc";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { Button } from "@heroui/button"; import { Button } from "@heroui/button";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { VscColorMode } from "react-icons/vsc"; import { VscColorMode } from "react-icons/vsc";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { useState } from "react"; import { useState } from "react";
import useYears from "@/hooks/useYears"; import useYears from "@/hooks/useYears";
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { useEffect } from "react"; import { useEffect } from "react";
export default function useYears( export default function useYears(
-30
View File
@@ -1,30 +0,0 @@
import { HeroUIProvider } from "@heroui/react";
import type { AppProps } from "next/app";
import { Inter } from "next/font/google";
import Head from "next/head";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import "@/styles/globals.css";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>Érettségi kereső</title>
</Head>
<HeroUIProvider className={`${inter.variable} font-sans`}>
<NextThemesProvider
attribute="class"
defaultTheme="dark"
enableSystem={true}
>
<Component {...pageProps} />
</NextThemesProvider>
</HeroUIProvider>
</>
);
}
-35
View File
@@ -1,35 +0,0 @@
import { Head, Html, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="hu">
<Head>
<meta name="theme-color" content="#121212" />
<meta name="title" content="Érettségi kereső" />
<meta name="og:title" content="Érettségi kereső" />
<meta property="og:url" content="https://erettsegi.albert.lol" />
<meta
name="description"
content="Egyszerű keresés és letöltés az érettségi feladatsorokhoz. 🏫"
/>
<meta
name="og:description"
content="Egyszerű keresés és letöltés az érettségi feladatsorokhoz. 🏫"
/>
<script
defer
src="https://analytics.albert.lol/script.js"
data-website-id="7b196f47-39c9-4b8e-8dfd-b6e707282eea"
></script>
<link rel="icon" href="/favicon.ico" />
<meta property="image" content="/logo.png" />
<meta property="og:image" content="/logo.png" />
<meta name="author" content="albert" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
-122
View File
@@ -1,122 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { subjects } from "@/utils/subjects";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { vizsgatargy, ev, idoszak, szint } = req.query as {
vizsgatargy: string;
ev: string;
idoszak: string;
szint: string;
};
const secure = req.headers["x-forwarded-proto"] === "https";
const protocol = secure ? "https" : "http";
const address = req.headers.host;
const baseUrl = `https://dload-oktatas.educatio.hu/erettsegi/feladatok_${ev}${idoszak}_${szint}/`;
const proxiedUrl = `${protocol}://${address}/api/proxy?link=${encodeURI(
baseUrl,
)}`;
const missingParams = [];
if (!ev) missingParams.push("ev");
if (!szint) missingParams.push("szint");
if (!idoszak) missingParams.push("idoszak");
if (!vizsgatargy) missingParams.push("vizsgatargy");
if (missingParams.length > 0) {
return res
.status(400)
.json({ error: `Hiányzó paraméterek: ${missingParams.join(", ")}` });
}
if (ev <= "2012") {
return res.status(400).json({ error: "Érvénytelen év" });
}
const validSubjects = subjects.map((subject) => subject.value);
if (!vizsgatargy || !validSubjects.includes(vizsgatargy)) {
return res.status(400).json({ error: "Érvénytelen vizsgatárgy" });
}
let honap: string;
switch (idoszak) {
case "osz":
honap = "okt";
break;
case "tavasz":
honap = "maj";
break;
default:
return res.status(400).json({ error: "Érvénytelen időszak" });
}
let prefix: string;
switch (szint) {
case "emelt":
prefix = `e_${vizsgatargy}`;
break;
case "kozep":
prefix = `k_${vizsgatargy}`;
break;
default:
return res.status(400).json({ error: "Érvénytelen szint" });
}
const feladat = "fl";
const utmutato = "ut";
const forras = "for";
const megoldas = "meg";
const shortev = ev.slice(-2);
let flPdfUrl: string | undefined,
utPdfUrl: string | undefined,
flZipUrl: string | undefined,
utZipUrl: string | undefined,
flMp3Url: string | undefined;
switch (vizsgatargy) {
case "inf":
case "infoism":
case "digkult":
flZipUrl = `${baseUrl}${prefix}${forras}_${shortev}${honap}_${feladat}.zip`;
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utZipUrl = `${baseUrl}${prefix}${megoldas}_${shortev}${honap}_${utmutato}.zip`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
break;
case "angol":
case "nemet":
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
flMp3Url = `${baseUrl}${prefix}_${shortev}${honap}_${feladat}.mp3`;
break;
default:
flPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`;
utPdfUrl = `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`;
break;
}
res.setHeader("Cache-Control", "s-maxage=31536000");
res.status(200).json({ flPdfUrl, utPdfUrl, flZipUrl, utZipUrl, flMp3Url });
} catch (e: unknown) {
if (e instanceof Error) {
let causeCode: string | undefined;
if (e.cause && typeof e.cause === "object" && "code" in e.cause) {
causeCode = String((e.cause as { code: unknown }).code);
}
res.status(500).json({
error: "Internal Server Error",
message: e.message,
cause: causeCode,
stack: process.env.NODE_ENV === "development" ? e.stack : undefined,
});
} else {
res.status(500).json({
error: "Internal Server Error",
message: "An unexpected error occurred",
details: String(e),
});
}
}
}
-77
View File
@@ -1,77 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { Agent, fetch } from "undici";
const insecureAgent = new Agent({
connect: {
rejectUnauthorized: false,
},
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const { link } = req.query as { link: string };
let missingParam: string | null = null;
if (!link) {
missingParam = "link";
}
if (missingParam) {
return res
.status(400)
.json({ error: `Hiányzó paraméter: ${missingParam}` });
}
const domain = link.split("/")[2];
if (domain !== "dload-oktatas.educatio.hu") {
return res.status(400).json({ error: "Érvénytelen link" });
}
try {
res.setHeader("Cache-Control", "s-maxage=31536000");
const response = await fetch(link, {
method: "GET",
dispatcher: insecureAgent,
});
const contentType = response.headers.get("content-type");
if (contentType === "application/pdf") {
const filename = link.split("/").pop() ?? "document.pdf";
res.setHeader("Content-Type", contentType);
res.setHeader("Content-Disposition", `inline; filename="${filename}"`);
}
if (response.ok) {
const arrayBuffer: ArrayBuffer = await response.arrayBuffer();
const buffer: Buffer = Buffer.from(arrayBuffer);
res.send(buffer);
} else {
res
.status(response.status)
.json({ error: "Hiba történt a lekérés során." });
}
} catch (e: unknown) {
if (e instanceof Error) {
let causeCode: string | undefined;
if (e.cause && typeof e.cause === "object" && "code" in e.cause) {
causeCode = String((e.cause as { code: unknown }).code);
}
res.status(500).json({
error: "Internal Server Error",
message: e.message,
cause: causeCode,
stack: process.env.NODE_ENV === "development" ? e.stack : undefined,
});
} else {
res.status(500).json({
error: "Internal Server Error",
message: "An unexpected error occurred",
details: String(e),
});
}
}
}
-70
View File
@@ -1,70 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { Agent, fetch } from "undici";
const insecureAgent = new Agent({
connect: {
rejectUnauthorized: false,
},
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const { link } = req.query as { link: string };
let missingParam: string | null = null;
if (!link) {
missingParam = "link";
}
if (missingParam) {
return res
.status(400)
.json({ error: `Hiányzó paraméter: ${missingParam}` });
}
const domain = link.split("/")[2];
if (
domain !== "localhost:3000" &&
domain !== "erettsegi.albert.lol" &&
domain !== "dload-oktatas.educatio.hu"
) {
return res.status(400).json({ error: "Érvénytelen link" });
}
try {
const { protocol, host } = new URL(link);
if (!protocol || !host) {
return res.status(400).json({ error: "Érvénytelen link" });
}
const response = await fetch(link, {
method: "HEAD",
dispatcher: insecureAgent,
});
const status = response.status;
res.status(200).json({ status });
} catch (e: unknown) {
if (e instanceof Error) {
let causeCode: string | undefined;
if (e.cause && typeof e.cause === "object" && "code" in e.cause) {
causeCode = String((e.cause as { code: unknown }).code);
}
res.status(500).json({
error: "Internal Server Error",
message: e.message,
cause: causeCode,
stack: process.env.NODE_ENV === "development" ? e.stack : undefined,
});
} else {
res.status(500).json({
error: "Internal Server Error",
message: "An unexpected error occurred",
details: String(e),
});
}
}
}
+25 -26
View File
@@ -1,27 +1,36 @@
{ {
"compilerOptions": { "compilerOptions": {
/* Base Options: */ /* Basic Options */
"esModuleInterop": true, "target": "esnext",
"skipLibCheck": true, "lib": ["dom", "dom.iterable", "esnext"],
"target": "es2022",
"allowJs": true, "allowJs": true,
"resolveJsonModule": true, "skipLibCheck": true,
"moduleDetection": "force", "jsx": "preserve",
"isolatedModules": true, "incremental": true,
"plugins": [
{
"name": "next"
}
],
/* Strictness */ /* Strictness */
"strict": true, "strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"checkJs": true,
/* Bundled projects */ /* Module Resolution */
"lib": ["dom", "dom.iterable", "ES2022"], "module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
/* Code Style */
"forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": true,
"module": "ESNext", "esModuleInterop": true,
"moduleResolution": "Bundler",
"jsx": "preserve",
"plugins": [{ "name": "next" }],
"incremental": true,
/* Path Aliases */ /* Path Aliases */
"baseUrl": ".", "baseUrl": ".",
@@ -29,16 +38,6 @@
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": [ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
".eslintrc.cjs",
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.cjs",
"**/*.js",
".next/types/**/*.ts",
"next.config.mjs",
"postcss.config.mjs"
],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }