mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
release: v1.32.0 — hardening and scalability (#580)
* release: prepare v1.32.0 * test: stabilize water capture re-entry
This commit is contained in:
1 parent
b01822be48
commit
62eb431d66
372 files changed
+25178
-8561
No files matched your search
@@ -3,6 +3,7 @@ import { apiSuccess, apiError, safeJson } from "@/lib/api-response";
|
||||
import { decrypt, encrypt } from "@/lib/crypto";
|
||||
import { deleteTelegramWebhook, setTelegramWebhook } from "@/lib/telegram";
|
||||
import { telegramSettingsSchema } from "@/lib/validations/telegram";
|
||||
import { notificationChannelEnabledSchema } from "@/lib/validations/notifications";
|
||||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod/v4";
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
@@ -33,6 +34,64 @@ export const GET = apiHandler(async () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function registerTelegramWebhook(botToken: string) {
|
||||
const appUrl = process.env.APP_URL ?? process.env.NEXT_PUBLIC_APP_URL;
|
||||
if (!appUrl) {
|
||||
return apiError(
|
||||
"Server configuration error: APP_URL (or NEXT_PUBLIC_APP_URL) is missing.",
|
||||
500,
|
||||
);
|
||||
}
|
||||
|
||||
let appBaseUrl: URL;
|
||||
try {
|
||||
appBaseUrl = new URL(appUrl);
|
||||
} catch {
|
||||
return apiError("Server configuration error: APP_URL is invalid.", 500);
|
||||
}
|
||||
|
||||
if (appBaseUrl.protocol !== "https:") {
|
||||
return apiError(
|
||||
"Telegram webhook requires a public HTTPS URL (no http/localhost).",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
const localHosts: Record<string, true> = {
|
||||
localhost: true,
|
||||
"127.0.0.1": true,
|
||||
"::1": true,
|
||||
};
|
||||
if (localHosts[appBaseUrl.hostname.toLowerCase()]) {
|
||||
return apiError(
|
||||
"Telegram webhook requires a publicly reachable domain (localhost is not allowed).",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
appBaseUrl.port &&
|
||||
!["80", "88", "443", "8443"].includes(appBaseUrl.port)
|
||||
) {
|
||||
return apiError(
|
||||
"Telegram webhook only allows ports 80, 88, 443, or 8443.",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
const webhookUrl = new URL("/api/telegram/webhook", appBaseUrl).toString();
|
||||
const webhookSecret = process.env.TELEGRAM_WEBHOOK_SECRET;
|
||||
const ok = await setTelegramWebhook(botToken, webhookUrl, webhookSecret);
|
||||
if (!ok) {
|
||||
return apiError(
|
||||
"Failed to set Telegram webhook. Check bot token and reachability.",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Telegram notification settings.
|
||||
*/
|
||||
@@ -54,6 +113,45 @@ export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
});
|
||||
|
||||
if (jsonError) return jsonError;
|
||||
|
||||
const enabledOnly = notificationChannelEnabledSchema.safeParse(body);
|
||||
if (enabledOnly.success) {
|
||||
const { enabled } = enabledOnly.data;
|
||||
if (enabled && (!current.telegramBotToken || !current.telegramChatId)) {
|
||||
return apiError(
|
||||
"Bot token and chat ID are required when Telegram is enabled",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
const currentTokenPlain = current.telegramBotToken
|
||||
? decrypt(current.telegramBotToken)
|
||||
: null;
|
||||
if (enabled && currentTokenPlain) {
|
||||
const webhookError = await registerTelegramWebhook(currentTokenPlain);
|
||||
if (webhookError) return webhookError;
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { telegramEnabled: enabled },
|
||||
});
|
||||
await prisma.notificationChannel.updateMany({
|
||||
where: { userId: user.id, type: "TELEGRAM" },
|
||||
data: { enabled },
|
||||
});
|
||||
|
||||
if (!enabled && currentTokenPlain) {
|
||||
await deleteTelegramWebhook(currentTokenPlain).catch(() => {});
|
||||
}
|
||||
|
||||
annotate({
|
||||
action: { name: "settings.telegram.update" },
|
||||
meta: { enabled },
|
||||
});
|
||||
return apiSuccess({ updated: true });
|
||||
}
|
||||
|
||||
const result = z.safeParse(telegramSettingsSchema, body);
|
||||
if (!result.success) {
|
||||
return apiError("Invalid input", 422);
|
||||
@@ -81,59 +179,8 @@ export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
const tokenForWebhook = trimmedToken || currentTokenPlain;
|
||||
|
||||
if (enabled && tokenForWebhook) {
|
||||
const appUrl = process.env.APP_URL ?? process.env.NEXT_PUBLIC_APP_URL;
|
||||
if (!appUrl) {
|
||||
return apiError(
|
||||
"Server configuration error: APP_URL (or NEXT_PUBLIC_APP_URL) is missing.",
|
||||
500,
|
||||
);
|
||||
}
|
||||
|
||||
let appBaseUrl: URL;
|
||||
try {
|
||||
appBaseUrl = new URL(appUrl);
|
||||
} catch {
|
||||
return apiError("Server configuration error: APP_URL is invalid.", 500);
|
||||
}
|
||||
|
||||
if (appBaseUrl.protocol !== "https:") {
|
||||
return apiError(
|
||||
"Telegram webhook requires a public HTTPS URL (no http/localhost).",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
const localHosts = new Set(["localhost", "127.0.0.1", "::1"]);
|
||||
if (localHosts.has(appBaseUrl.hostname.toLowerCase())) {
|
||||
return apiError(
|
||||
"Telegram webhook requires a publicly reachable domain (localhost is not allowed).",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
appBaseUrl.port &&
|
||||
!["80", "88", "443", "8443"].includes(appBaseUrl.port)
|
||||
) {
|
||||
return apiError(
|
||||
"Telegram webhook only allows ports 80, 88, 443, or 8443.",
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
const webhookUrl = new URL("/api/telegram/webhook", appBaseUrl).toString();
|
||||
const webhookSecret = process.env.TELEGRAM_WEBHOOK_SECRET;
|
||||
const ok = await setTelegramWebhook(
|
||||
tokenForWebhook,
|
||||
webhookUrl,
|
||||
webhookSecret,
|
||||
);
|
||||
if (!ok) {
|
||||
return apiError(
|
||||
"Failed to set Telegram webhook. Check bot token and reachability.",
|
||||
422,
|
||||
);
|
||||
}
|
||||
const webhookError = await registerTelegramWebhook(tokenForWebhook);
|
||||
if (webhookError) return webhookError;
|
||||
}
|
||||
|
||||
const data: Record<string, unknown> = { telegramEnabled: enabled };
|
||||
|
||||
Reference in new issue
Block a user