import { ListMobileDeviceMediaFilters, ListMobileDeviceSMSFilters, ListMobileDevicesFilters, MobileMediaAssetInput, MobilePagination, MobileSMSInput, PingMobileDevice, RegisterMobileDevice, } from "./data"; import { FlowExecCtx } from "@core/flow.execution.context"; import { traceResultAsync } from "@core/observability"; import { MobileRepository } from "./repository"; import { errAsync } from "neverthrow"; import { db } from "@pkg/db"; import { settings } from "@core/settings"; import { mobileErrors } from "./errors"; export class MobileController { constructor( private mobileRepo: MobileRepository, private defaultAdminEmail?: string, ) {} registerDevice(fctx: FlowExecCtx, payload: RegisterMobileDevice) { return traceResultAsync({ name: "logic.mobile.controller.register", fctx, attributes: { "app.mobile.external_device_id": payload.externalDeviceId }, fn: () => this.mobileRepo .findAdminOwnerId(fctx, this.defaultAdminEmail) .andThen((ownerUserId) => this.mobileRepo.upsertDevice(fctx, payload, ownerUserId), ), }); } pingByExternalDeviceId( fctx: FlowExecCtx, externalDeviceId: string, payload?: PingMobileDevice, ) { return traceResultAsync({ name: "logic.mobile.controller.ping", fctx, attributes: { "app.mobile.external_device_id": externalDeviceId }, fn: () => this.mobileRepo.getDeviceByExternalId(fctx, externalDeviceId).andThen( (device) => { const pingAt = payload?.pingAt ? new Date(payload.pingAt as Date | string) : new Date(); if (Number.isNaN(pingAt.getTime())) { return errAsync( mobileErrors.invalidPayload( fctx, "pingAt must be a valid date", ), ); } return this.mobileRepo.touchDevicePing(fctx, device.id, pingAt); }, ), }); } syncSMSByExternalDeviceId( fctx: FlowExecCtx, externalDeviceId: string, messages: MobileSMSInput[], ) { return traceResultAsync({ name: "logic.mobile.controller.sms_sync", fctx, attributes: { "app.mobile.external_device_id": externalDeviceId, "app.mobile.sms.received_count": messages.length, }, fn: () => this.mobileRepo.getDeviceByExternalId(fctx, externalDeviceId).andThen( (device) => this.mobileRepo .syncSMS(fctx, device.id, messages) .andThen((syncResult) => this.mobileRepo.touchDevicePing(fctx, device.id).map(() => ({ ...syncResult, deviceId: device.id, })), ), ), }); } syncMediaByExternalDeviceId( fctx: FlowExecCtx, externalDeviceId: string, assets: MobileMediaAssetInput[], ) { return traceResultAsync({ name: "logic.mobile.controller.media_sync", fctx, attributes: { "app.mobile.external_device_id": externalDeviceId, "app.mobile.media.received_count": assets.length, }, fn: () => this.mobileRepo.getDeviceByExternalId(fctx, externalDeviceId).andThen( (device) => this.mobileRepo .syncMediaAssets(fctx, device.id, assets) .andThen((syncResult) => this.mobileRepo.touchDevicePing(fctx, device.id).map(() => ({ ...syncResult, deviceId: device.id, })), ), ), }); } listDevices( fctx: FlowExecCtx, filters: ListMobileDevicesFilters, pagination: MobilePagination, ) { return traceResultAsync({ name: "logic.mobile.controller.list_devices", fctx, attributes: { "app.user.id": filters.ownerUserId }, fn: () => this.mobileRepo.listDevices(fctx, filters, pagination), }); } getDeviceDetail(fctx: FlowExecCtx, deviceId: number, ownerUserId: string) { return traceResultAsync({ name: "logic.mobile.controller.get_device_detail", fctx, attributes: { "app.user.id": ownerUserId, "app.mobile.device_id": deviceId }, fn: () => this.mobileRepo.getDeviceDetail(fctx, deviceId, ownerUserId), }); } listDeviceSMS( fctx: FlowExecCtx, filters: ListMobileDeviceSMSFilters, pagination: MobilePagination, ) { return traceResultAsync({ name: "logic.mobile.controller.list_device_sms", fctx, attributes: { "app.mobile.device_id": filters.deviceId }, fn: () => this.mobileRepo.listDeviceSMS(fctx, filters, pagination), }); } listDeviceMedia( fctx: FlowExecCtx, filters: ListMobileDeviceMediaFilters, pagination: MobilePagination, ) { return traceResultAsync({ name: "logic.mobile.controller.list_device_media", fctx, attributes: { "app.mobile.device_id": filters.deviceId }, fn: () => this.mobileRepo.listDeviceMedia(fctx, filters, pagination), }); } deleteMediaAsset(fctx: FlowExecCtx, mediaAssetId: number, ownerUserId: string) { return traceResultAsync({ name: "logic.mobile.controller.delete_media_asset", fctx, attributes: { "app.user.id": ownerUserId, "app.mobile.media_asset_id": mediaAssetId, }, fn: () => this.mobileRepo.deleteMediaAsset(fctx, mediaAssetId, ownerUserId), }); } deleteDevice(fctx: FlowExecCtx, deviceId: number, ownerUserId: string) { return traceResultAsync({ name: "logic.mobile.controller.delete_device", fctx, attributes: { "app.user.id": ownerUserId, "app.mobile.device_id": deviceId }, fn: () => this.mobileRepo.deleteDevice(fctx, deviceId, ownerUserId), }); } resolveDeviceByExternalId(fctx: FlowExecCtx, externalDeviceId: string) { return traceResultAsync({ name: "logic.mobile.controller.resolve_device", fctx, attributes: { "app.mobile.external_device_id": externalDeviceId }, fn: () => this.mobileRepo.getDeviceByExternalId(fctx, externalDeviceId), }); } } export function getMobileController(): MobileController { return new MobileController( new MobileRepository(db), settings.defaultAdminEmail || undefined, ); }