109 lines
4.6 KiB
JavaScript
109 lines
4.6 KiB
JavaScript
import fs from 'fs';
|
|
import User from '../models/community/user.js';
|
|
import PushNotificationDevice from '../models/community/push_notification_device.js';
|
|
import PushNotificationSetting from '../models/community/push_notification_setting.js';
|
|
|
|
const INVALID_TOKEN_CODES = new Set([
|
|
'messaging/invalid-registration-token',
|
|
'messaging/registration-token-not-registered',
|
|
]);
|
|
|
|
const EVENT_CONFIG = {
|
|
chatMessage: { preference: 'chatEnabled', title: 'Neue Nachricht', body: 'Du hast eine neue Chat-Nachricht.', route: 'chat' },
|
|
friendloginchanged: { preference: 'friendLoginEnabled', title: 'Freund online', body: 'Ein Freund ist jetzt online.', route: 'social' },
|
|
familychanged: { preference: 'falukantEnabled', title: 'Falukant', body: 'Deine Familie hat Neuigkeiten.', route: 'falukant' },
|
|
falukantUpdateFamily: { preference: 'falukantEnabled', title: 'Falukant', body: 'Deine Familie wurde aktualisiert.', route: 'falukant' },
|
|
falukantUpdateStatus: { preference: 'falukantEnabled', title: 'Falukant', body: 'Es gibt einen neuen Falukant-Status.', route: 'falukant' },
|
|
vocabReminder: { preference: 'vocabReminderEnabled', title: 'Vokabeltraining', body: 'Deine Wiederholung wartet auf dich.', route: 'vocab' },
|
|
};
|
|
|
|
let messagingPromise;
|
|
|
|
async function getMessaging() {
|
|
if (messagingPromise !== undefined) return messagingPromise;
|
|
messagingPromise = (async () => {
|
|
const inlineCredentials = process.env.FIREBASE_SERVICE_ACCOUNT_JSON;
|
|
const credentialPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;
|
|
if (!inlineCredentials && !credentialPath) return null;
|
|
try {
|
|
const { cert, getApps, initializeApp } = await import('firebase-admin/app');
|
|
const { getMessaging: getFirebaseMessaging } = await import('firebase-admin/messaging');
|
|
const serviceAccount = JSON.parse(inlineCredentials || fs.readFileSync(credentialPath, 'utf8'));
|
|
const app = getApps()[0] || initializeApp({ credential: cert(serviceAccount) });
|
|
return getFirebaseMessaging(app);
|
|
} catch (error) {
|
|
console.error('[push] Firebase Admin konnte nicht initialisiert werden:', error.message);
|
|
return null;
|
|
}
|
|
})();
|
|
return messagingPromise;
|
|
}
|
|
|
|
async function getUserByHashedId(hashedUserId) {
|
|
const user = await User.findOne({ where: { hashedId: hashedUserId } });
|
|
if (!user) throw new Error('usernotfound');
|
|
return user;
|
|
}
|
|
|
|
async function getSettings(userId) {
|
|
const [settings] = await PushNotificationSetting.findOrCreate({ where: { userId } });
|
|
return settings;
|
|
}
|
|
|
|
export async function registerDevice(hashedUserId, { token, enabled = true }) {
|
|
const user = await getUserByHashedId(hashedUserId);
|
|
const device = await PushNotificationDevice.findOne({ where: { token } });
|
|
if (device) {
|
|
await device.update({ userId: user.id, platform: 'android', enabled });
|
|
} else {
|
|
await PushNotificationDevice.create({ userId: user.id, token, platform: 'android', enabled });
|
|
}
|
|
return { registered: true };
|
|
}
|
|
|
|
export async function disableDevice(hashedUserId, token) {
|
|
const user = await getUserByHashedId(hashedUserId);
|
|
await PushNotificationDevice.update({ enabled: false }, { where: { userId: user.id, token } });
|
|
return { disabled: true };
|
|
}
|
|
|
|
export async function loadSettings(hashedUserId) {
|
|
const user = await getUserByHashedId(hashedUserId);
|
|
return (await getSettings(user.id)).toJSON();
|
|
}
|
|
|
|
export async function updateSettings(hashedUserId, values) {
|
|
const user = await getUserByHashedId(hashedUserId);
|
|
const settings = await getSettings(user.id);
|
|
await settings.update(values);
|
|
return settings.toJSON();
|
|
}
|
|
|
|
export async function dispatchRealtimeEvent(hashedUserId, event, data = {}) {
|
|
const config = EVENT_CONFIG[event];
|
|
if (!config) return;
|
|
const user = await getUserByHashedId(hashedUserId);
|
|
const settings = await getSettings(user.id);
|
|
if (!settings.enabled || !settings[config.preference]) return;
|
|
|
|
const messaging = await getMessaging();
|
|
if (!messaging) return;
|
|
const devices = await PushNotificationDevice.findAll({ where: { userId: user.id, enabled: true, platform: 'android' } });
|
|
await Promise.all(devices.map(async (device) => {
|
|
try {
|
|
await messaging.send({
|
|
token: device.token,
|
|
notification: { title: config.title, body: config.body },
|
|
data: { route: config.route, event, ...Object.fromEntries(Object.entries(data || {}).map(([key, value]) => [key, String(value)])) },
|
|
android: { priority: 'high', notification: { channelId: 'yourpart_updates' } },
|
|
});
|
|
} catch (error) {
|
|
if (INVALID_TOKEN_CODES.has(error.code)) {
|
|
await device.destroy();
|
|
} else {
|
|
console.error('[push] Zustellung fehlgeschlagen:', error.message);
|
|
}
|
|
}
|
|
}));
|
|
}
|