This commit is contained in:
xds
2025-10-16 15:06:20 +03:00
commit 040da34ff7
78 changed files with 3934 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
package space.luminic.finance.services
import com.interaso.webpush.VapidKeys
import com.interaso.webpush.WebPushService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.awaitSingle
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.bson.types.ObjectId
import org.slf4j.LoggerFactory
import org.springframework.dao.DuplicateKeyException
import org.springframework.stereotype.Service
import space.luminic.finance.models.PushMessage
import space.luminic.finance.models.Subscription
import space.luminic.finance.models.SubscriptionDTO
import space.luminic.finance.models.User
import space.luminic.finance.repos.SubscriptionRepo
import space.luminic.finance.services.VapidConstants.VAPID_PRIVATE_KEY
import space.luminic.finance.services.VapidConstants.VAPID_PUBLIC_KEY
import space.luminic.finance.services.VapidConstants.VAPID_SUBJECT
import kotlin.collections.forEach
import kotlin.jvm.javaClass
import kotlin.text.orEmpty
object VapidConstants {
const val VAPID_PUBLIC_KEY =
"BKmMyBUhpkcmzYWcYsjH_spqcy0zf_8eVtZo60f7949TgLztCmv3YD0E_vtV2dTfECQ4sdLdPK3ICDcyOkCqr84"
const val VAPID_PRIVATE_KEY = "YeJH_0LhnVYN6RdxMidgR6WMYlpGXTJS3HjT9V3NSGI"
const val VAPID_SUBJECT = "mailto:voroninvyu@gmail.com"
}
@Service
class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
private val logger = LoggerFactory.getLogger(javaClass)
private val pushService =
WebPushService(
subject = VAPID_SUBJECT,
vapidKeys = VapidKeys.fromUncompressedBytes(VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY)
)
suspend fun sendToSpaceOwner(ownerId: String, message: PushMessage) = coroutineScope {
val ownerTokens = subscriptionRepo.findByUserIdAndIsActive(ObjectId(ownerId)).collectList().awaitSingle()
ownerTokens.forEach { token ->
launch(Dispatchers.IO) { // Теперь мы точно в корутин скоупе
try {
sendNotification(token.endpoint, token.p256dh, token.auth, message)
} catch (e: Exception) {
logger.error("Ошибка при отправке уведомления: ${e.message}", e)
}
}
}
}
suspend fun sendNotification(endpoint: String, p256dh: String, auth: String, payload: PushMessage) {
try {
pushService.send(
payload = Json.encodeToString(payload),
endpoint = endpoint,
p256dh = p256dh,
auth = auth
)
logger.info("Уведомление успешно отправлено на endpoint: $endpoint")
} catch (e: Exception) {
logger.error("Ошибка при отправке уведомления на endpoint $endpoint: ${e.message}")
throw e
}
}
suspend fun sendToAll(payload: PushMessage) {
subscriptionRepo.findAll().collectList().awaitSingle().forEach { sub ->
try {
sendNotification(sub.endpoint, sub.p256dh, sub.auth, payload)
} catch (e: Exception) {
sub.isActive = false
subscriptionRepo.save(sub).awaitSingle()
}
}
}
suspend fun subscribe(subscriptionDTO: SubscriptionDTO, user: User): String {
val subscription = Subscription(
id = null,
user = user,
endpoint = subscriptionDTO.endpoint,
auth = subscriptionDTO.keys["auth"].orEmpty(),
p256dh = subscriptionDTO.keys["p256dh"].orEmpty(),
isActive = true
)
return try {
val savedSubscription = subscriptionRepo.save(subscription).awaitSingle()
"Subscription created with ID: ${savedSubscription.id}"
} catch (e: DuplicateKeyException) {
logger.info("Subscription already exists. Skipping.")
"Subscription already exists. Skipping."
} catch (e: Exception) {
logger.error("Error while saving subscription: ${e.message}")
throw kotlin.RuntimeException("Error while saving subscription")
}
}
}