Minor updates

This commit is contained in:
2025-06-20 21:43:42 +02:00
parent e3326998c4
commit 4e1328147b
17 changed files with 344 additions and 369 deletions
+61 -68
View File
@@ -9,12 +9,46 @@ export async function GET(req: NextRequest) {
const idoszak = searchParams.get("idoszak");
const szint = searchParams.get("szint");
if (!vizsgatargy && !ev && !idoszak && !szint) {
const currentYear = new Date().getFullYear();
return NextResponse.json({
parameters: {
vizsgatargy: {
type: "string",
required: true,
values: subjects.map((s) => ({
value: s.value,
label: s.label,
})),
},
ev: {
type: "integer",
required: true,
values: `2013 - ${currentYear}`,
},
idoszak: {
type: "string",
required: true,
values: ["tavasz", "osz"],
},
szint: {
type: "string",
required: true,
values: ["kozep", "emelt"],
},
},
example:
"/api/erettsegi?vizsgatargy=mat&ev=2023&idoszak=tavasz&szint=kozep",
});
}
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");
const missingParams = [
!vizsgatargy && "vizsgatargy",
!ev && "ev",
!idoszak && "idoszak",
!szint && "szint",
].filter(Boolean);
return NextResponse.json(
{ error: `Hiányzó paraméterek: ${missingParams.join(", ")}` },
@@ -34,41 +68,16 @@ export async function GET(req: NextRequest) {
);
}
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 honap = idoszak === "osz" ? "okt" : "maj";
const prefix = szint === "emelt" ? `e_${vizsgatargy}` : `k_${vizsgatargy}`;
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 proxiedUrl = `${protocol}://${host}/api/proxy?link=${encodeURI(
baseUrl,
)}`;
const shortev = ev.slice(-2);
const feladat = "fl";
@@ -76,40 +85,24 @@ export async function GET(req: NextRequest) {
const forras = "for";
const megoldas = "meg";
let flPdfUrl: string | undefined,
utPdfUrl: string | undefined,
flZipUrl: string | undefined,
utZipUrl: string | undefined,
flMp3Url: string | undefined;
const urls = {
flPdfUrl: `${proxiedUrl}${prefix}_${shortev}${honap}_${feladat}.pdf`,
utPdfUrl: `${proxiedUrl}${prefix}_${shortev}${honap}_${utmutato}.pdf`,
flZipUrl: ["inf", "infoism", "digkult"].includes(vizsgatargy)
? `${baseUrl}${prefix}${forras}_${shortev}${honap}_${feladat}.zip`
: undefined,
utZipUrl: ["inf", "infoism", "digkult"].includes(vizsgatargy)
? `${baseUrl}${prefix}${megoldas}_${shortev}${honap}_${utmutato}.zip`
: undefined,
flMp3Url: ["angol", "nemet"].includes(vizsgatargy)
? `${baseUrl}${prefix}_${shortev}${honap}_${feladat}.mp3`
: 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" },
},
);
return NextResponse.json(urls, {
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(
+16 -14
View File
@@ -13,10 +13,15 @@ export async function GET(req: NextRequest) {
const link = searchParams.get("link");
if (!link) {
return NextResponse.json(
{ error: "Hiányzó paraméter: link" },
{ status: 400 },
);
return NextResponse.json({
parameters: {
link: {
type: "string",
required: true,
},
},
example: `/api/proxy?link=https://dload-oktatas.educatio.hu/erettsegi/feladatok_2023tavasz_kozep/k_mat_23maj_fl.pdf`,
});
}
let url: URL;
@@ -48,8 +53,9 @@ export async function GET(req: NextRequest) {
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");
const headers = new Headers({
"Cache-Control": "s-maxage=31536000, stale-while-revalidate",
});
if (contentType) {
headers.set("Content-Type", contentType);
@@ -61,18 +67,14 @@ export async function GET(req: NextRequest) {
return new Response(body, {
status: 200,
headers: 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 },
);
}
const errorMessage =
e instanceof Error ? e.message : "An unexpected internal error occurred";
return NextResponse.json(
{ error: "An unexpected internal error occurred" },
{ error: "Internal Server Error", message: errorMessage },
{ status: 500 },
);
}
+17 -24
View File
@@ -19,10 +19,16 @@ export async function GET(req: NextRequest) {
const link = searchParams.get("link");
if (!link) {
return NextResponse.json(
{ error: "Hiányzó paraméter: link" },
{ status: 400 },
);
return NextResponse.json({
parameters: {
link: {
type: "string",
required: true,
allowed_hosts: ALLOWED_HOSTS,
},
},
example: `/api/validate?link=https://dload-oktatas.educatio.hu/erettsegi/feladatok_2023tavasz_kozep/k_mat_23maj_fl.pdf`,
});
}
let url: URL;
@@ -48,26 +54,13 @@ export async function GET(req: NextRequest) {
} 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 },
);
}
const errorResponse = {
error: "Internal Server Error",
message: e instanceof Error ? e.message : "An unexpected error occurred",
...(process.env.NODE_ENV === "development" &&
e instanceof Error && { stack: e.stack }),
};
return NextResponse.json(
{
error: "Internal Server Error",
message: "An unexpected error occurred",
details: String(e),
},
{ status: 500 },
);
return NextResponse.json(errorResponse, { status: 500 });
}
}
+4 -1
View File
@@ -3,6 +3,7 @@ import Script from "next/script";
import { Providers } from "@/app/providers";
import { Inter } from "next/font/google";
import "@/styles/globals.css";
import { AppProvider } from "@/ctx/app";
const inter = Inter({
subsets: ["latin"],
@@ -45,7 +46,9 @@ export default function RootLayout({
data-website-id="7b196f47-39c9-4b8e-8dfd-b6e707282eea"
/>
<body className={`${inter.variable} antialiased font-sans`}>
<Providers>{children}</Providers>
<AppProvider>
<Providers>{children}</Providers>
</AppProvider>
</body>
</html>
);
+9 -17
View File
@@ -8,23 +8,15 @@ export default function NotFound() {
return (
<>
<main className="dark:bg-[#121212] text-foreground bg-background py-5">
<h1 className="text-7xl font-bold text-blue-400 text-center mt-16">
404
</h1>
<div className="flex min-h-screen flex-col items-center justify-between">
<div className="container mx-auto">
<div className="flex flex-col items-center justify-center">
<div className="mt-5 mb-3">
<div className="text-2xl font-semibold text-gray-600">
<p className="mt-2">Az keresett oldal nem található.</p>
<p className="mt-8 text-center">
<Link href="/">
<Button color="primary">Vissza</Button>
</Link>
</p>
</div>
</div>
</div>
<div className="flex min-h-screen flex-col items-center justify-center">
<h1 className="text-7xl font-bold text-blue-400">404</h1>
<div className="mt-5 mb-3 text-center">
<p className="text-2xl font-semibold text-gray-600">
Az keresett oldal nem található.
</p>
<Link href="/" className="mt-8 inline-block">
<Button color="primary">Vissza a főoldalra</Button>
</Link>
</div>
</div>
<Footer />
+56 -57
View File
@@ -1,45 +1,38 @@
"use client";
import { ButtonGroup, Divider } from "@heroui/react";
import { useEffect } from "react";
import { Mp3Button, PdfButton, ZipButton } from "@/components/Buttons";
import { useContext, useEffect, useCallback } from "react";
import { Resource } from "@/components/Resources";
import { Footer } from "@/components/Footer";
import {
LevelSelector,
PeriodSelector,
SubjectSelector,
YearSelector,
} from "@/components/Selectors";
import { useAppState } from "@/hooks/useState";
import useYears from "@/hooks/useYears";
import { Selector, periodItems, levelItems } from "@/components/Selectors";
import { AppContext } from "@/ctx/app";
import { fetchData } from "@/utils/fetch";
import { subjects } from "@/utils/subjects";
import useYears from "@/hooks/useYears";
export default function Home() {
const { state, dispatch } = useContext(AppContext);
const {
flPdfLink,
setflPdfLink,
utPdfLink,
setutPdfLink,
flZipLink,
setflZipLink,
utZipLink,
setutZipLink,
flMp3Link,
setflMp3Link,
selectedSubject,
setSelectedSubject,
selectedYear,
setSelectedYear,
selectedPeriod,
setSelectedPeriod,
selectedLevel,
setSelectedLevel,
years,
setYears,
} = useAppState();
} = state;
useYears(setYears);
const setYearsCallback = useCallback(
(newYears: string[]) => {
dispatch({ type: "SET_YEARS", payload: newYears });
},
[dispatch],
);
useYears(setYearsCallback);
useEffect(() => {
if (selectedLevel && selectedPeriod && selectedSubject && selectedYear) {
@@ -48,24 +41,16 @@ export default function Home() {
selectedYear,
selectedPeriod,
selectedLevel,
setflZipLink,
setutZipLink,
setflPdfLink,
setutPdfLink,
setflMp3Link,
dispatch,
);
}
}, [
selectedLevel,
selectedPeriod,
selectedSubject,
selectedYear,
setutPdfLink,
setflZipLink,
setutZipLink,
setflPdfLink,
setflMp3Link,
]);
}, [selectedLevel, selectedPeriod, selectedSubject, selectedYear, dispatch]);
const subjectItems = subjects.map((subject) => ({
value: subject.value,
label: subject.label,
}));
const yearItems = years.map((year) => ({ value: year, label: year }));
return (
<main className="dark:bg-[#121212] text-foreground bg-background py-5">
@@ -76,50 +61,64 @@ export default function Home() {
<div className="container mx-auto">
<div className="flex flex-col items-center justify-center">
<div className="mt-5 mb-3">
<SubjectSelector
selectedSubject={selectedSubject}
setSelectedSubject={setSelectedSubject}
subjects={subjects}
<Selector
label="Tárgy"
selectedValue={selectedSubject}
onSelectionChange={(subject) =>
dispatch({ type: "SET_SELECTED_SUBJECT", payload: subject })
}
items={subjectItems}
/>
</div>
<div className="mb-3">
<YearSelector
selectedYear={selectedYear}
setSelectedYear={setSelectedYear}
years={years}
<Selector
label="Év"
selectedValue={selectedYear}
onSelectionChange={(year) =>
dispatch({ type: "SET_SELECTED_YEAR", payload: year })
}
items={yearItems}
/>
</div>
<div className="mb-3">
<PeriodSelector
selectedPeriod={selectedPeriod}
setSelectedPeriod={setSelectedPeriod}
<Selector
label="Időszak"
selectedValue={selectedPeriod}
onSelectionChange={(period) =>
dispatch({ type: "SET_SELECTED_PERIOD", payload: period })
}
items={periodItems}
/>
</div>
<div className="mb-3">
<LevelSelector
selectedLevel={selectedLevel}
setSelectedLevel={setSelectedLevel}
<Selector
label="Szint"
selectedValue={selectedLevel}
onSelectionChange={(level) =>
dispatch({ type: "SET_SELECTED_LEVEL", payload: level })
}
items={levelItems}
/>
</div>
<div className="space-x-3">
<ButtonGroup>
<PdfButton label="Feladatlap" link={flPdfLink} />
<Resource label="Feladatlap" link={flPdfLink} />
<Divider orientation="vertical" />
<PdfButton label="Útmutató" link={utPdfLink} />
<Resource label="Útmutató" link={utPdfLink} />
</ButtonGroup>
</div>
{["inf", "infoism", "digkult"].includes(selectedSubject) && (
<div className="space-x-3">
<ButtonGroup>
<ZipButton label="Forrás" link={flZipLink} />
<Resource label="Forrás" link={flZipLink} />
<Divider orientation="vertical" />
<ZipButton label="Megoldás" link={utZipLink} />
<Resource label="Megoldás" link={utZipLink} />
</ButtonGroup>
</div>
)}
{["angol", "nemet"].includes(selectedSubject) && (
<div className="space-x-3">
<Mp3Button label="Hang" link={flMp3Link} />
<Resource label="Hang" link={flMp3Link} />
</div>
)}
</div>