Point routes, scheduler, and push service at fcmTokensSqlite so the service no longer reads or writes the JSON store.
197 lines
5.4 KiB
TypeScript
197 lines
5.4 KiB
TypeScript
import express, { Router } from "express";
|
|
import { db } from "../db/fcmTokensSqlite.js";
|
|
import {
|
|
requireAuthOrNotificationLocalTest,
|
|
requireEndorserAuth,
|
|
} from "../middleware/auth.js";
|
|
import { errorMessage, formatElapsedMs } from "../util/formatElapsed.js";
|
|
import { maskToken } from "../util/maskToken.js";
|
|
|
|
export const notificationsRouter: express.Router = Router();
|
|
|
|
notificationsRouter.get("/", (_req, res) => {
|
|
res.json({ ok: true, resource: "notifications" });
|
|
});
|
|
|
|
notificationsRouter.post(
|
|
"/refresh",
|
|
requireAuthOrNotificationLocalTest,
|
|
requireEndorserAuth,
|
|
async (req, res) => {
|
|
const started = Date.now();
|
|
const userId = req.did;
|
|
if (userId === undefined) {
|
|
res.status(401).json({ success: false, message: "Unauthorized" });
|
|
return;
|
|
}
|
|
|
|
const { deviceId, fcmToken } = req.body as {
|
|
deviceId?: unknown;
|
|
fcmToken?: unknown;
|
|
};
|
|
|
|
const canonicalDeviceId =
|
|
typeof deviceId === "string" ? deviceId.trim() : undefined;
|
|
const token =
|
|
typeof fcmToken === "string" && fcmToken.length > 0
|
|
? fcmToken
|
|
: undefined;
|
|
|
|
console.log(
|
|
"[Refresh] Request received",
|
|
canonicalDeviceId !== undefined ? `deviceId=${canonicalDeviceId}` : "",
|
|
token !== undefined ? `token suffix=${maskToken(token)}` : ""
|
|
);
|
|
|
|
if (
|
|
(canonicalDeviceId === undefined || canonicalDeviceId.length === 0) &&
|
|
token === undefined
|
|
) {
|
|
console.log(
|
|
"[Refresh] Rejected in",
|
|
formatElapsedMs(Date.now() - started) + ":",
|
|
"deviceId or fcmToken is required"
|
|
);
|
|
res.status(400).json({ error: "deviceId or fcmToken is required" });
|
|
return;
|
|
}
|
|
|
|
const device = await db.resolveOwnedDevice(userId, {
|
|
deviceId: canonicalDeviceId,
|
|
fcmToken: token,
|
|
});
|
|
|
|
if (device === undefined) {
|
|
console.log(
|
|
"[Refresh] Device not found in",
|
|
formatElapsedMs(Date.now() - started),
|
|
canonicalDeviceId !== undefined
|
|
? `deviceId=${canonicalDeviceId}`
|
|
: "",
|
|
token !== undefined ? `token suffix=${maskToken(token)}` : ""
|
|
);
|
|
res.status(404).json({ error: "Device not found" });
|
|
return;
|
|
}
|
|
|
|
const now = Date.now();
|
|
res.json({
|
|
shouldNotify: true,
|
|
nextNotifications: [{ timestamp: now + 600000 }],
|
|
});
|
|
console.log(
|
|
"[Refresh] Completed in",
|
|
formatElapsedMs(Date.now() - started) + ",",
|
|
"deviceId=" + device.deviceId + ",",
|
|
"token suffix=" + maskToken(device.fcmToken)
|
|
);
|
|
}
|
|
);
|
|
|
|
notificationsRouter.post(
|
|
"/register",
|
|
requireAuthOrNotificationLocalTest,
|
|
requireEndorserAuth,
|
|
async (req, res) => {
|
|
const started = Date.now();
|
|
const userId = req.did;
|
|
if (userId === undefined) {
|
|
res.status(401).json({ success: false, message: "Unauthorized" });
|
|
return;
|
|
}
|
|
|
|
if (
|
|
req.body !== null &&
|
|
typeof req.body === "object" &&
|
|
"userId" in req.body
|
|
) {
|
|
console.log(
|
|
"[Register] Rejected in",
|
|
formatElapsedMs(Date.now() - started) + ":",
|
|
"userId must not be sent in the request body"
|
|
);
|
|
res.status(400).json({
|
|
error: "userId must not be sent in the request body",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { deviceId, fcmToken, platform, testMode } = req.body as {
|
|
deviceId?: unknown;
|
|
fcmToken?: unknown;
|
|
platform?: unknown;
|
|
testMode?: unknown;
|
|
};
|
|
|
|
if (typeof deviceId !== "string" || deviceId.trim().length === 0) {
|
|
console.log(
|
|
"[Register] Rejected in",
|
|
formatElapsedMs(Date.now() - started) + ":",
|
|
"deviceId is required"
|
|
);
|
|
res.status(400).json({ error: "deviceId is required" });
|
|
return;
|
|
}
|
|
if (typeof fcmToken !== "string" || fcmToken.length === 0) {
|
|
console.log(
|
|
"[Register] Rejected in",
|
|
formatElapsedMs(Date.now() - started) + ":",
|
|
"fcmToken is required"
|
|
);
|
|
res.status(400).json({ error: "fcmToken is required" });
|
|
return;
|
|
}
|
|
if (typeof platform !== "string" || platform.length === 0) {
|
|
console.log(
|
|
"[Register] Rejected in",
|
|
formatElapsedMs(Date.now() - started) + ":",
|
|
"platform is required"
|
|
);
|
|
res.status(400).json({ error: "platform is required" });
|
|
return;
|
|
}
|
|
|
|
const canonicalDeviceId = deviceId.trim();
|
|
console.log(
|
|
"[Register] Request received,",
|
|
"deviceId=" + canonicalDeviceId + ",",
|
|
"platform=" + platform + ",",
|
|
"token suffix=" + maskToken(fcmToken)
|
|
);
|
|
|
|
try {
|
|
const existing = await db.getByDeviceId(userId, canonicalDeviceId);
|
|
const action =
|
|
existing === undefined
|
|
? "create"
|
|
: existing.fcmToken !== fcmToken
|
|
? "update-token"
|
|
: "update";
|
|
|
|
await db.upsert({
|
|
userId,
|
|
deviceId: canonicalDeviceId,
|
|
fcmToken,
|
|
platform,
|
|
testMode: typeof testMode === "boolean" ? testMode : undefined,
|
|
updatedAt: new Date(),
|
|
});
|
|
res.sendStatus(200);
|
|
console.log(
|
|
"[Register] Completed in",
|
|
formatElapsedMs(Date.now() - started) + ",",
|
|
"deviceId=" + canonicalDeviceId + ",",
|
|
"action=" + action
|
|
);
|
|
} catch (err) {
|
|
console.error(
|
|
"[Register] Failed in",
|
|
formatElapsedMs(Date.now() - started) + ",",
|
|
"deviceId=" + canonicalDeviceId + ":",
|
|
errorMessage(err)
|
|
);
|
|
res.sendStatus(500);
|
|
}
|
|
}
|
|
);
|