chore: add prettier formatting
All checks were successful
CI / Lint, typecheck and build (push) Successful in 9m56s

This commit is contained in:
zv
2026-06-14 20:26:56 +02:00
parent 8bbd9397a1
commit ee55521803
79 changed files with 2451 additions and 969 deletions

View File

@@ -1,6 +1,12 @@
import { NextResponse } from "next/server";
import { fetchMeteoWarnings } from "@/lib/server-warnings";
import { getPushSubscriptions, hasSentWarning, markWarningSent, pruneSentWarnings, removePushSubscription } from "@/lib/push-store";
import {
getPushSubscriptions,
hasSentWarning,
markWarningSent,
pruneSentWarnings,
removePushSubscription,
} from "@/lib/push-store";
import { isWebPushConfigured, sendWarningNotification } from "@/lib/push-service";
import { warningMatchesCounty } from "@/lib/warning-regions";
import type { WeatherWarning } from "@/types/imgw";
@@ -40,8 +46,12 @@ export async function GET(request: Request) {
try {
const now = Date.now();
const warnings = (await fetchMeteoWarnings(AbortSignal.timeout(12_000))).filter((warning) => isRelevantWarning(warning, now));
const subscriptions = getPushSubscriptions().filter((subscription) => subscription.region === "PL" && subscription.enabled && subscription.province);
const warnings = (await fetchMeteoWarnings(AbortSignal.timeout(12_000))).filter((warning) =>
isRelevantWarning(warning, now),
);
const subscriptions = getPushSubscriptions().filter(
(subscription) => subscription.region === "PL" && subscription.enabled && subscription.province,
);
const activeWarningIds = new Set(warnings.map((warning) => warning.id));
let sent = 0;
let skipped = 0;
@@ -50,9 +60,11 @@ export async function GET(request: Request) {
pruneSentWarnings(activeWarningIds);
for (const subscription of subscriptions) {
const matchingWarnings = warnings.filter((warning) => (
subscription.countyTeryt ? warningMatchesCounty(warning, subscription.countyTeryt) : subscription.province !== null && warning.provinces.includes(subscription.province)
));
const matchingWarnings = warnings.filter((warning) =>
subscription.countyTeryt
? warningMatchesCounty(warning, subscription.countyTeryt)
: subscription.province !== null && warning.provinces.includes(subscription.province),
);
for (const warning of matchingWarnings) {
if (hasSentWarning(subscription.endpoint, warning.id)) {
skipped += 1;
@@ -65,7 +77,8 @@ export async function GET(request: Request) {
sent += 1;
} catch (error) {
failed += 1;
const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
const statusCode =
typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint);
}
}
@@ -80,9 +93,12 @@ export async function GET(request: Request) {
failed,
});
} catch (error) {
return NextResponse.json({
error: "Unable to check IMGW meteorological warnings.",
details: getErrorMessage(error),
}, { status: 502 });
return NextResponse.json(
{
error: "Unable to check IMGW meteorological warnings.",
details: getErrorMessage(error),
},
{ status: 502 },
);
}
}

View File

@@ -1,7 +1,12 @@
import { NextResponse } from "next/server";
import { fetchServerForecast } from "@/lib/server-forecast";
import { fetchMeteoWarnings } from "@/lib/server-warnings";
import { getPushSubscriptions, hasSentMorningBrief, markMorningBriefSent, removePushSubscription } from "@/lib/push-store";
import {
getPushSubscriptions,
hasSentMorningBrief,
markMorningBriefSent,
removePushSubscription,
} from "@/lib/push-store";
import { isWebPushConfigured, sendMorningBriefNotification } from "@/lib/push-service";
import { buildWeatherBrief } from "@/lib/weather-brief";
import { DEFAULT_TEMPERATURE_UNIT, DEFAULT_WIND_SPEED_UNIT } from "@/lib/weather-utils";
@@ -49,18 +54,19 @@ export async function GET(request: Request) {
const now = new Date();
const briefTime = normalizeTime(process.env.NOTIFICATIONS_MORNING_BRIEF_TIME, DEFAULT_MORNING_BRIEF_TIME);
const subscriptions = getPushSubscriptions().filter((subscription) => (
subscription.morningBriefEnabled
&& Number.isFinite(subscription.latitude)
&& Number.isFinite(subscription.longitude)
));
const subscriptions = getPushSubscriptions().filter(
(subscription) =>
subscription.morningBriefEnabled &&
Number.isFinite(subscription.latitude) &&
Number.isFinite(subscription.longitude),
);
let warningsUnavailable = false;
const hasPolishSubscriptions = subscriptions.some((subscription) => subscription.region === "PL");
const warnings = hasPolishSubscriptions
? await fetchMeteoWarnings(AbortSignal.timeout(12_000)).catch(() => {
warningsUnavailable = true;
return [];
})
warningsUnavailable = true;
return [];
})
: [];
let sent = 0;
let skipped = 0;
@@ -79,7 +85,11 @@ export async function GET(request: Request) {
}
try {
const forecast = await fetchServerForecast(subscription.latitude as number, subscription.longitude as number, subscription.region);
const forecast = await fetchServerForecast(
subscription.latitude as number,
subscription.longitude as number,
subscription.region,
);
const brief = buildWeatherBrief({
forecast,
warnings: subscription.region === "PL" ? warnings : [],
@@ -100,7 +110,8 @@ export async function GET(request: Request) {
sent += 1;
} catch (error) {
failed += 1;
const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
const statusCode =
typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint);
}
}

View File

@@ -2,7 +2,12 @@ import { NextResponse } from "next/server";
import { upsertPushSubscription, removePushSubscription } from "@/lib/push-store";
import { isWebPushConfigured } from "@/lib/push-service";
import { normalizeProvinceName } from "@/lib/provinces";
import { DEFAULT_TEMPERATURE_UNIT, DEFAULT_WIND_SPEED_UNIT, isTemperatureUnit, isWindSpeedUnit } from "@/lib/weather-utils";
import {
DEFAULT_TEMPERATURE_UNIT,
DEFAULT_WIND_SPEED_UNIT,
isTemperatureUnit,
isWindSpeedUnit,
} from "@/lib/weather-utils";
import type { Language } from "@/lib/i18n";
import type { BrowserPushSubscription } from "@/types/notifications";
import type { WeatherRegion } from "@/types/weather-region";
@@ -12,10 +17,12 @@ export const runtime = "nodejs";
function isBrowserPushSubscription(value: unknown): value is BrowserPushSubscription {
if (!value || typeof value !== "object") return false;
const subscription = value as Partial<BrowserPushSubscription>;
return typeof subscription.endpoint === "string"
&& subscription.endpoint.startsWith("https://")
&& typeof subscription.keys?.p256dh === "string"
&& typeof subscription.keys.auth === "string";
return (
typeof subscription.endpoint === "string" &&
subscription.endpoint.startsWith("https://") &&
typeof subscription.keys?.p256dh === "string" &&
typeof subscription.keys.auth === "string"
);
}
export async function POST(request: Request) {
@@ -23,7 +30,7 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Web Push is not configured." }, { status: 503 });
}
const body = await request.json().catch(() => null) as {
const body = (await request.json().catch(() => null)) as {
subscription?: unknown;
province?: unknown;
region?: unknown;
@@ -63,7 +70,8 @@ export async function POST(request: Request) {
tomorrowBriefEnabled: body?.tomorrowBriefEnabled === true,
latitude: typeof body?.latitude === "number" && Number.isFinite(body.latitude) ? body.latitude : null,
longitude: typeof body?.longitude === "number" && Number.isFinite(body.longitude) ? body.longitude : null,
locationName: typeof body?.locationName === "string" && body.locationName.trim() ? body.locationName.trim().slice(0, 80) : null,
locationName:
typeof body?.locationName === "string" && body.locationName.trim() ? body.locationName.trim().slice(0, 80) : null,
timezone: typeof body?.timezone === "string" && body.timezone.trim() ? body.timezone.trim().slice(0, 80) : null,
countyTeryt: typeof body?.countyTeryt === "string" && /^\d{4}$/.test(body.countyTeryt) ? body.countyTeryt : null,
temperatureUnit: isTemperatureUnit(rawTemperatureUnit) ? rawTemperatureUnit : DEFAULT_TEMPERATURE_UNIT,
@@ -76,7 +84,7 @@ export async function POST(request: Request) {
}
export async function DELETE(request: Request) {
const body = await request.json().catch(() => null) as { endpoint?: unknown } | null;
const body = (await request.json().catch(() => null)) as { endpoint?: unknown } | null;
if (typeof body?.endpoint !== "string" || !body.endpoint) {
return NextResponse.json({ error: "Invalid push subscription endpoint." }, { status: 400 });
}

View File

@@ -9,7 +9,7 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Web Push is not configured." }, { status: 503 });
}
const body = await request.json().catch(() => null) as { endpoint?: unknown } | null;
const body = (await request.json().catch(() => null)) as { endpoint?: unknown } | null;
if (typeof body?.endpoint !== "string" || !body.endpoint) {
return NextResponse.json({ error: "Invalid push subscription endpoint." }, { status: 400 });
}
@@ -23,7 +23,8 @@ export async function POST(request: Request) {
await sendTestNotification(subscription);
return NextResponse.json({ ok: true });
} catch (error) {
const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
const statusCode =
typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint);
return NextResponse.json({ error: "Unable to send test notification." }, { status: 502 });
}

View File

@@ -1,7 +1,12 @@
import { NextResponse } from "next/server";
import { fetchServerForecast } from "@/lib/server-forecast";
import { fetchMeteoWarnings } from "@/lib/server-warnings";
import { getPushSubscriptions, hasSentTomorrowBrief, markTomorrowBriefSent, removePushSubscription } from "@/lib/push-store";
import {
getPushSubscriptions,
hasSentTomorrowBrief,
markTomorrowBriefSent,
removePushSubscription,
} from "@/lib/push-store";
import { isWebPushConfigured, sendTomorrowBriefNotification } from "@/lib/push-service";
import { buildTomorrowWeatherBrief } from "@/lib/weather-brief";
import { DEFAULT_TEMPERATURE_UNIT, DEFAULT_WIND_SPEED_UNIT } from "@/lib/weather-utils";
@@ -37,14 +42,17 @@ function getLocalDateParts(timeZone: string | null, date = new Date(), dayOffset
const time = `${part("hour")}:${part("minute")}`;
if (dayOffset === 0) return { dateKey, time };
const shiftedDate = new Date(Date.UTC(Number(part("year")), Number(part("month")) - 1, Number(part("day")) + dayOffset, 12));
const shiftedDate = new Date(
Date.UTC(Number(part("year")), Number(part("month")) - 1, Number(part("day")) + dayOffset, 12),
);
const shiftedParts = new Intl.DateTimeFormat("en-CA", {
timeZone: timeZone || "Europe/Warsaw",
year: "numeric",
month: "2-digit",
day: "2-digit",
}).formatToParts(shiftedDate);
const shiftedPart = (type: Intl.DateTimeFormatPartTypes) => shiftedParts.find((entry) => entry.type === type)?.value ?? "";
const shiftedPart = (type: Intl.DateTimeFormatPartTypes) =>
shiftedParts.find((entry) => entry.type === type)?.value ?? "";
return { dateKey: `${shiftedPart("year")}-${shiftedPart("month")}-${shiftedPart("day")}`, time };
}
@@ -58,18 +66,19 @@ export async function GET(request: Request) {
const now = new Date();
const briefTime = normalizeTime(process.env.NOTIFICATIONS_TOMORROW_BRIEF_TIME, DEFAULT_TOMORROW_BRIEF_TIME);
const subscriptions = getPushSubscriptions().filter((subscription) => (
subscription.tomorrowBriefEnabled
&& Number.isFinite(subscription.latitude)
&& Number.isFinite(subscription.longitude)
));
const subscriptions = getPushSubscriptions().filter(
(subscription) =>
subscription.tomorrowBriefEnabled &&
Number.isFinite(subscription.latitude) &&
Number.isFinite(subscription.longitude),
);
let warningsUnavailable = false;
const hasPolishSubscriptions = subscriptions.some((subscription) => subscription.region === "PL");
const warnings = hasPolishSubscriptions
? await fetchMeteoWarnings(AbortSignal.timeout(12_000)).catch(() => {
warningsUnavailable = true;
return [];
})
warningsUnavailable = true;
return [];
})
: [];
let sent = 0;
let skipped = 0;
@@ -88,7 +97,11 @@ export async function GET(request: Request) {
}
try {
const forecast = await fetchServerForecast(subscription.latitude as number, subscription.longitude as number, subscription.region);
const forecast = await fetchServerForecast(
subscription.latitude as number,
subscription.longitude as number,
subscription.region,
);
const brief = buildTomorrowWeatherBrief({
forecast,
warnings: subscription.region === "PL" ? warnings : [],
@@ -109,7 +122,8 @@ export async function GET(request: Request) {
sent += 1;
} catch (error) {
failed += 1;
const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
const statusCode =
typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : null;
if (statusCode === 404 || statusCode === 410) removePushSubscription(subscription.endpoint);
}
}