/* * Copyright (C) 2008-2020, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.juick.www.api; import com.juick.model.*; import com.juick.service.*; import com.juick.util.HttpBadRequestException; import com.juick.util.HttpForbiddenException; import io.swagger.v3.oas.annotations.Hidden; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import javax.inject.Inject; import java.util.Collections; import java.util.List; /** * Created by vitalyster on 24.10.2016. */ @RestController public class Notifications { private static final Logger logger = LoggerFactory.getLogger("www"); @Inject private PushQueriesService pushQueriesService; @Inject private MessagesService messagesService; @Inject private SubscriptionService subscriptionService; @Inject private UserService userService; @Inject private TelegramService telegramService; @Inject private User serviceUser; private User collectTokens(Integer uid) { User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE); user.setUnreadCount(messagesService.getUnread(user).size()); pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null))); pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null))); pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null))); List xmppJids = userService.getJIDsbyUID(uid).stream() .map(jid -> new ExternalToken(null, "xmpp", jid, null)) .toList(); user.getTokens().addAll(xmppJids); List tgIds = telegramService.getTelegramIdentifiers(Collections.singletonList(user)).stream() .map(tgId -> new ExternalToken(null, "durov", String.valueOf(tgId), null)) .toList(); user.getTokens().addAll(tgIds); return user; } @Hidden @RequestMapping(value = "/api/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public List doGet( @ModelAttribute(binding = false) User visitor, @RequestParam(required = false, defaultValue = "0") int uid, @RequestParam(required = false, defaultValue = "0") int mid, @RequestParam(required = false, defaultValue = "0") int rid) { if (!(visitor.equals(serviceUser))) { throw new HttpForbiddenException(); } if (uid > 0 && mid == 0) { // PM return Collections.singletonList(collectTokens(uid)); } else { if (mid > 0) { // reply Message msg = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new); List users = Collections.emptyList(); if (rid > 0) { Message op = messagesService.getMessage(mid).orElseThrow(IllegalStateException::new); Message reply = messagesService.getReply(mid, rid); if (reply != null) { users = subscriptionService.getUsersSubscribedToComments(op, reply); } else { logger.warn("Reply not found: {}/{}", mid, rid); } } else { users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg); } return users.stream().map(User::getUid) .map(this::collectTokens).toList(); } else { // read return Collections.singletonList(collectTokens(uid)); } } } @Hidden @RequestMapping(value = "/api/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE) public Status doDelete( @ModelAttribute User visitor, @RequestBody List list) { if (!visitor.equals(serviceUser)) { throw new HttpForbiddenException(); } list.forEach(t -> { switch (t.type()) { case "gcm" -> pushQueriesService.deleteGCMToken(t.token()); case "apns" -> pushQueriesService.deleteAPNSToken(t.token()); case "mpns" -> pushQueriesService.deleteMPNSToken(t.token()); default -> throw new HttpBadRequestException(); } }); return Status.OK; } @Hidden @RequestMapping(value = "/api/notifications/delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) public Status doDeleteTokens( @ModelAttribute User visitor, @RequestBody List list) { if (!visitor.equals(serviceUser)) { throw new HttpForbiddenException(); } list.forEach(t -> { switch (t.type()) { case "gcm" -> pushQueriesService.deleteGCMToken(t.token()); case "apns" -> pushQueriesService.deleteAPNSToken(t.token()); case "mpns" -> pushQueriesService.deleteMPNSToken(t.token()); default -> throw new HttpBadRequestException(); } }); return Status.OK; } @Hidden @RequestMapping(value = "/api/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) public Status doPut( @ModelAttribute User visitor, @RequestBody List list) { list.forEach(t -> { switch (t.type()) { case "gcm" -> pushQueriesService.addGCMToken(visitor.getUid(), t.token()); case "apns" -> pushQueriesService.addAPNSToken(visitor.getUid(), t.token()); case "mpns" -> pushQueriesService.addMPNSToken(visitor.getUid(), t.token()); default -> throw new HttpBadRequestException(); } }); return Status.OK; } @Deprecated @RequestMapping(value = "/api/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Status doAndroidRegister( @ModelAttribute User visitor, @RequestParam(name = "regid") String regId) { pushQueriesService.addGCMToken(visitor.getUid(), regId); return Status.OK; } @Deprecated @RequestMapping(value = "/api/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Status doWinphoneRegister( @ModelAttribute User visitor, @RequestParam(name = "url") String regId) { pushQueriesService.addMPNSToken(visitor.getUid(), regId); return Status.OK; } }