mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-01-17 15:20:10 +00:00
321 lines
9.6 KiB
TypeScript
321 lines
9.6 KiB
TypeScript
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
import UserPushService, {
|
|
Service as UserPushServiceType,
|
|
} from "../Services/UserPushService";
|
|
import PushNotificationService from "../Services/PushNotificationService";
|
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
import {
|
|
ExpressRequest,
|
|
ExpressResponse,
|
|
NextFunction,
|
|
OneUptimeRequest,
|
|
} from "../Utils/Express";
|
|
import Response from "../Utils/Response";
|
|
import BaseAPI from "./BaseAPI";
|
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
import ObjectID from "../../Types/ObjectID";
|
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
import PushNotificationMessage from "../../Types/PushNotification/PushNotificationMessage";
|
|
|
|
export default class UserPushAPI extends BaseAPI<
|
|
UserPush,
|
|
UserPushServiceType
|
|
> {
|
|
public constructor() {
|
|
super(UserPush, UserPushService);
|
|
|
|
this.router.post(
|
|
`/user-push/register`,
|
|
UserMiddleware.getUserMiddleware,
|
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
try {
|
|
req = req as OneUptimeRequest;
|
|
|
|
if (!req.body.deviceToken) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device token is required"),
|
|
);
|
|
}
|
|
|
|
if (!req.body.deviceType || req.body.deviceType !== "web") {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Only web device type is supported"),
|
|
);
|
|
}
|
|
|
|
if (!req.body.projectId) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Project ID is required"),
|
|
);
|
|
}
|
|
|
|
// Check if device is already registered
|
|
const existingDevice: UserPush | null = await this.service.findOneBy({
|
|
query: {
|
|
userId: (req as OneUptimeRequest).userAuthorization!.userId!,
|
|
projectId: new ObjectID(req.body.projectId),
|
|
deviceToken: req.body.deviceToken,
|
|
},
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
select: {
|
|
_id: true,
|
|
},
|
|
});
|
|
|
|
if (existingDevice) {
|
|
// Mark as used and return a specific response indicating device was already registered
|
|
throw new BadDataException(
|
|
"This device is already registered for push notifications",
|
|
);
|
|
}
|
|
|
|
// Create new device registration
|
|
const userPush: UserPush = new UserPush();
|
|
userPush.userId = (
|
|
req as OneUptimeRequest
|
|
).userAuthorization!.userId!;
|
|
userPush.projectId = new ObjectID(req.body.projectId);
|
|
userPush.deviceToken = req.body.deviceToken;
|
|
userPush.deviceType = req.body.deviceType;
|
|
userPush.deviceName = req.body.deviceName || "Unknown Device";
|
|
userPush.isVerified = true; // For web push, we consider it verified immediately
|
|
|
|
const savedDevice: UserPush = await this.service.create({
|
|
data: userPush,
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
});
|
|
|
|
return Response.sendJsonObjectResponse(req, res, {
|
|
success: true,
|
|
deviceId: savedDevice._id!.toString(),
|
|
});
|
|
} catch (error: any) {
|
|
next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
this.router.post(
|
|
`/user-push/:deviceId/test-notification`,
|
|
UserMiddleware.getUserMiddleware,
|
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
try {
|
|
req = req as OneUptimeRequest;
|
|
|
|
if (!req.params["deviceId"]) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device ID is required"),
|
|
);
|
|
}
|
|
|
|
// Get the device
|
|
const device: UserPush | null = await this.service.findOneById({
|
|
id: new ObjectID(req.params["deviceId"]),
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
select: {
|
|
userId: true,
|
|
deviceName: true,
|
|
deviceToken: true,
|
|
deviceType: true,
|
|
isVerified: true,
|
|
projectId: true,
|
|
},
|
|
});
|
|
|
|
if (!device) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device not found"),
|
|
);
|
|
}
|
|
|
|
// Check if the device belongs to the current user
|
|
if (
|
|
device.userId?.toString() !==
|
|
(req as OneUptimeRequest).userAuthorization!.userId!.toString()
|
|
) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Unauthorized access to device"),
|
|
);
|
|
}
|
|
|
|
if (!device.isVerified) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device is not verified"),
|
|
);
|
|
}
|
|
|
|
try {
|
|
// Send test notification
|
|
const testMessage: PushNotificationMessage =
|
|
PushNotificationUtil.createGenericNotification({
|
|
title: "Test Notification from OneUptime",
|
|
body: "This is a test notification to verify your device is working correctly.",
|
|
clickAction: "/dashboard",
|
|
tag: "test-notification",
|
|
requireInteraction: false,
|
|
});
|
|
|
|
await PushNotificationService.sendPushNotification(
|
|
{
|
|
devices: [
|
|
{
|
|
token: device.deviceToken!,
|
|
...(device.deviceName && {
|
|
name: device.deviceName,
|
|
}),
|
|
},
|
|
],
|
|
message: testMessage,
|
|
deviceType: device.deviceType!,
|
|
},
|
|
{
|
|
isSensitive: false,
|
|
projectId: device.projectId!,
|
|
userId: device.userId!,
|
|
},
|
|
);
|
|
} catch (error: any) {
|
|
throw new BadDataException(
|
|
`Failed to send test notification: ${error.message}`,
|
|
);
|
|
}
|
|
|
|
return Response.sendJsonObjectResponse(req, res, {
|
|
success: true,
|
|
message: "Test notification sent successfully",
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
this.router.post(
|
|
`/user-push/:deviceId/verify`,
|
|
UserMiddleware.getUserMiddleware,
|
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
try {
|
|
req = req as OneUptimeRequest;
|
|
|
|
if (!req.params["deviceId"]) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device ID is required"),
|
|
);
|
|
}
|
|
|
|
const device: UserPush | null = await this.service.findOneById({
|
|
id: new ObjectID(req.params["deviceId"]),
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
select: {
|
|
userId: true,
|
|
},
|
|
});
|
|
|
|
if (!device) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device not found"),
|
|
);
|
|
}
|
|
|
|
// Check if the device belongs to the current user
|
|
if (
|
|
device.userId?.toString() !==
|
|
(req as OneUptimeRequest).userAuthorization!.userId!.toString()
|
|
) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Unauthorized access to device"),
|
|
);
|
|
}
|
|
|
|
await this.service.verifyDevice(device._id!.toString());
|
|
|
|
return Response.sendEmptySuccessResponse(req, res);
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
this.router.post(
|
|
`/user-push/:deviceId/unverify`,
|
|
UserMiddleware.getUserMiddleware,
|
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
try {
|
|
req = req as OneUptimeRequest;
|
|
|
|
if (!req.params["deviceId"]) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device ID is required"),
|
|
);
|
|
}
|
|
|
|
const device: UserPush | null = await this.service.findOneById({
|
|
id: new ObjectID(req.params["deviceId"]),
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
select: {
|
|
userId: true,
|
|
},
|
|
});
|
|
|
|
if (!device) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Device not found"),
|
|
);
|
|
}
|
|
|
|
// Check if the device belongs to the current user
|
|
if (
|
|
device.userId?.toString() !==
|
|
(req as OneUptimeRequest).userAuthorization!.userId!.toString()
|
|
) {
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Unauthorized access to device"),
|
|
);
|
|
}
|
|
|
|
await this.service.unverifyDevice(device._id!.toString());
|
|
|
|
return Response.sendEmptySuccessResponse(req, res);
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
}
|