KI-AGENT: Mobile Push Registrierung anbinden

This commit is contained in:
2026-05-22 17:34:52 +02:00
parent 5400fd7ad5
commit cacfce4d15
14 changed files with 630 additions and 20 deletions

View File

@@ -0,0 +1,101 @@
import { createHash, createHmac } from "node:crypto"
import { secrets } from "../utils/secrets"
type PushServerDevicePlatform = "web" | "ios" | "android"
export type RegisterPushServerDeviceInput = {
localDeviceId: string
platform: PushServerDevicePlatform
providerToken?: string
subscription?: Record<string, unknown>
meta?: Record<string, unknown>
}
export type RegisterPushServerDeviceResult = {
centralDeviceId: string
status: string
}
export type SendPushServerMessageInput = {
idempotencyKey: string
devices: string[]
priority?: "normal" | "high"
ttlSeconds?: number
collapseKey?: string
notification?: {
title?: string
body?: string
}
data?: Record<string, unknown>
}
function configured() {
return Boolean(secrets.PUSH_SERVER_URL && secrets.PUSH_SERVER_INSTANCE_ID && secrets.PUSH_SERVER_SECRET)
}
function normalizeBaseUrl() {
return String(secrets.PUSH_SERVER_URL || "").replace(/\/+$/, "")
}
function bodyHash(body: string) {
return createHash("sha256").update(body).digest("hex")
}
function signature(method: string, path: string, timestamp: string, body: string) {
const canonical = [
method.toUpperCase(),
path,
timestamp,
bodyHash(body),
secrets.PUSH_SERVER_INSTANCE_ID,
].join("\n")
return createHmac("sha256", secrets.PUSH_SERVER_SECRET).update(canonical).digest("hex")
}
async function requestPushServer<T>(method: "POST" | "DELETE", path: string, payload?: unknown): Promise<T> {
if (!configured()) {
throw new Error("Zentraler Push-Server ist nicht konfiguriert")
}
const body = payload === undefined ? "" : JSON.stringify(payload)
const timestamp = new Date().toISOString()
const response = await fetch(`${normalizeBaseUrl()}${path}`, {
method,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"X-Fedeo-Instance-Id": secrets.PUSH_SERVER_INSTANCE_ID,
"X-Fedeo-Timestamp": timestamp,
"X-Fedeo-Signature": signature(method, path, timestamp, body),
},
body: body || undefined,
})
const text = await response.text()
const data = text ? JSON.parse(text) : null
if (!response.ok) {
const message = data?.message || data?.error || `Push-Server Anfrage fehlgeschlagen (${response.status})`
throw new Error(message)
}
return data as T
}
export const pushServerClient = {
configured,
registerDevice(input: RegisterPushServerDeviceInput) {
return requestPushServer<RegisterPushServerDeviceResult>("POST", "/v1/devices", input)
},
sendPush(input: SendPushServerMessageInput) {
return requestPushServer<{ accepted: number; rejected: number; deliveryJobId: string }>("POST", "/v1/push", {
priority: "normal",
ttlSeconds: 3600,
...input,
})
},
}