prism/server/routes/ntfy.ts

71 lines
2.2 KiB
TypeScript

import { createGroup, sendGroupMessage } from '@/modules/signal';
import { getGroupId, register } from '@/modules/store';
import { withAuth } from '@/utils/auth';
import { logError, logVerbose } from '@/utils/log';
const handleNtfyPublish = async (req: Request) => {
try {
const url = new URL(req.url);
const topic = decodeURIComponent(url.pathname.slice(1));
if (!topic || topic.includes('/')) {
return new Response('Invalid topic', { status: 400 });
}
let message = await req.text();
if (!message) {
return new Response('Message required', { status: 400 });
}
const contentType = req.headers.get('content-type') || '';
let title = req.headers.get('X-Title') || req.headers.get('Title') || req.headers.get('t');
let androidPackage =
req.headers.get('X-Package') || req.headers.get('Package') || req.headers.get('p');
if (contentType.includes('application/x-www-form-urlencoded')) {
const params = new URLSearchParams(message);
message = params.get('message') || message;
title = title || params.get('title') || params.get('t');
androidPackage = androidPackage || params.get('package') || params.get('p');
}
const topicKey = `ntfy-${topic}`;
let groupId = getGroupId(topicKey);
if (!groupId) {
groupId = await createGroup(topic);
register(topicKey, groupId, topic);
logVerbose(`Created new group for ntfy topic: ${topic}`);
}
await sendGroupMessage(groupId, message, {
androidPackage: androidPackage || undefined,
title: title || undefined,
});
logVerbose(`Sent ntfy message to topic ${topic}: ${title || message.substring(0, 50)}`);
return new Response(
JSON.stringify({
id: Date.now().toString(),
time: Math.floor(Date.now() / 1000),
event: 'message',
topic,
message,
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
);
} catch (error) {
logError('Failed to handle ntfy publish:', error);
return new Response('Internal server error', { status: 500 });
}
};
export const ntfyRoutes = {
'/:topic': {
POST: withAuth(handleNtfyPublish),
},
};