+ bot + notifications
This commit is contained in:
@@ -56,6 +56,8 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson")
|
implementation("com.google.code.gson:gson")
|
||||||
implementation("io.micrometer:micrometer-registry-prometheus")
|
implementation("io.micrometer:micrometer-registry-prometheus")
|
||||||
|
|
||||||
|
implementation("org.telegram:telegrambots:6.9.7.1")
|
||||||
|
implementation("org.telegram:telegrambots-spring-boot-starter:6.9.7.1")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,20 @@ package space.luminic.budgerapp
|
|||||||
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.cache.annotation.EnableCaching
|
import org.springframework.cache.annotation.EnableCaching
|
||||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories
|
||||||
import org.springframework.scheduling.annotation.EnableAsync
|
import org.springframework.scheduling.annotation.EnableAsync
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
import org.springframework.scheduling.annotation.EnableScheduling
|
||||||
import java.util.TimeZone
|
import space.luminic.budgerapp.configs.TelegramBotProperties
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SpringBootApplication(scanBasePackages = ["space.luminic.budgerapp"])
|
@SpringBootApplication(scanBasePackages = ["space.luminic.budgerapp"])
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
|
@EnableConfigurationProperties(TelegramBotProperties::class)
|
||||||
@EnableMongoRepositories(basePackages = ["space.luminic.budgerapp.repos"])
|
@EnableMongoRepositories(basePackages = ["space.luminic.budgerapp.repos"])
|
||||||
class BudgerAppApplication
|
class BudgerAppApplication
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package space.luminic.budgerapp.configs
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.telegram.telegrambots.meta.TelegramBotsApi
|
||||||
|
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession
|
||||||
|
import space.luminic.budgerapp.services.BotService
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class TelegramBotConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun telegramBotsApi(myBot: BotService): TelegramBotsApi {
|
||||||
|
val botsApi = TelegramBotsApi(DefaultBotSession::class.java)
|
||||||
|
botsApi.registerBot(myBot)
|
||||||
|
return botsApi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "telegram.bot")
|
||||||
|
data class TelegramBotProperties(
|
||||||
|
val username: String,
|
||||||
|
val token: String
|
||||||
|
)
|
||||||
@@ -10,10 +10,7 @@ import org.springframework.web.bind.annotation.*
|
|||||||
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
||||||
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.*
|
||||||
import space.luminic.budgerapp.services.CategoryService
|
import space.luminic.budgerapp.services.*
|
||||||
import space.luminic.budgerapp.services.FinancialService
|
|
||||||
import space.luminic.budgerapp.services.RecurrentService
|
|
||||||
import space.luminic.budgerapp.services.SpaceService
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -22,7 +19,8 @@ class SpaceController(
|
|||||||
private val spaceService: SpaceService,
|
private val spaceService: SpaceService,
|
||||||
private val financialService: FinancialService,
|
private val financialService: FinancialService,
|
||||||
private val categoryService: CategoryService,
|
private val categoryService: CategoryService,
|
||||||
private val recurrentService: RecurrentService
|
private val recurrentService: RecurrentService,
|
||||||
|
private val authService: AuthService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(SpaceController::class.java)
|
private val log = LoggerFactory.getLogger(SpaceController::class.java)
|
||||||
@@ -57,13 +55,16 @@ class SpaceController(
|
|||||||
|
|
||||||
@DeleteMapping("/{spaceId}")
|
@DeleteMapping("/{spaceId}")
|
||||||
suspend fun deleteSpace(@PathVariable spaceId: String) {
|
suspend fun deleteSpace(@PathVariable spaceId: String) {
|
||||||
return spaceService.deleteSpace(spaceService.isValidRequest(spaceId))
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
|
return spaceService.deleteSpace(space)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/invite")
|
@PostMapping("/{spaceId}/invite")
|
||||||
suspend fun inviteSpace(@PathVariable spaceId: String): SpaceInvite {
|
suspend fun inviteSpace(@PathVariable spaceId: String): SpaceInvite {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.createInviteSpace(spaceId)
|
return spaceService.createInviteSpace(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +75,15 @@ class SpaceController(
|
|||||||
|
|
||||||
@DeleteMapping("/{spaceId}/leave")
|
@DeleteMapping("/{spaceId}/leave")
|
||||||
suspend fun leaveSpace(@PathVariable spaceId: String) {
|
suspend fun leaveSpace(@PathVariable spaceId: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.leaveSpace(spaceId)
|
return spaceService.leaveSpace(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/members/kick/{username}")
|
@DeleteMapping("/{spaceId}/members/kick/{username}")
|
||||||
suspend fun kickMembers(@PathVariable spaceId: String, @PathVariable username: String) {
|
suspend fun kickMembers(@PathVariable spaceId: String, @PathVariable username: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.kickMember(spaceId, username)
|
return spaceService.kickMember(spaceId, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +93,8 @@ class SpaceController(
|
|||||||
//
|
//
|
||||||
@GetMapping("/{spaceId}/budgets")
|
@GetMapping("/{spaceId}/budgets")
|
||||||
suspend fun getBudgets(@PathVariable spaceId: String): List<Budget> {
|
suspend fun getBudgets(@PathVariable spaceId: String): List<Budget> {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.getBudgets(spaceId).awaitSingleOrNull().orEmpty()
|
return financialService.getBudgets(spaceId).awaitSingleOrNull().orEmpty()
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -98,7 +102,8 @@ class SpaceController(
|
|||||||
@GetMapping("/{spaceId}/budgets/{id}")
|
@GetMapping("/{spaceId}/budgets/{id}")
|
||||||
suspend fun getBudget(@PathVariable spaceId: String, @PathVariable id: String): BudgetDTO? {
|
suspend fun getBudget(@PathVariable spaceId: String, @PathVariable id: String): BudgetDTO? {
|
||||||
log.info("Getting budget for spaceId=$spaceId, id=$id")
|
log.info("Getting budget for spaceId=$spaceId, id=$id")
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.getBudget(spaceId, id)
|
return financialService.getBudget(spaceId, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +112,10 @@ class SpaceController(
|
|||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
||||||
): Budget? {
|
): Budget? {
|
||||||
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.createBudget(
|
return financialService.createBudget(
|
||||||
spaceService.isValidRequest(spaceId),
|
space,
|
||||||
budgetCreationDTO.budget,
|
budgetCreationDTO.budget,
|
||||||
budgetCreationDTO.createRecurrent
|
budgetCreationDTO.createRecurrent
|
||||||
)
|
)
|
||||||
@@ -116,7 +123,8 @@ class SpaceController(
|
|||||||
|
|
||||||
@DeleteMapping("/{spaceId}/budgets/{id}")
|
@DeleteMapping("/{spaceId}/budgets/{id}")
|
||||||
suspend fun deleteBudget(@PathVariable spaceId: String, @PathVariable id: String) {
|
suspend fun deleteBudget(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
financialService.deleteBudget(spaceId, id)
|
financialService.deleteBudget(spaceId, id)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -128,7 +136,8 @@ class SpaceController(
|
|||||||
@PathVariable catId: String,
|
@PathVariable catId: String,
|
||||||
@RequestBody limit: LimitValue,
|
@RequestBody limit: LimitValue,
|
||||||
): BudgetCategory {
|
): BudgetCategory {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.setCategoryLimit(spaceId, budgetId, catId, limit.limit)
|
return financialService.setCategoryLimit(spaceId, budgetId, catId, limit.limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +183,8 @@ class SpaceController(
|
|||||||
|
|
||||||
@PostMapping("/{spaceId}/transactions")
|
@PostMapping("/{spaceId}/transactions")
|
||||||
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.createTransaction(space, transaction)
|
return financialService.createTransaction(space, transaction)
|
||||||
|
|
||||||
|
|
||||||
@@ -184,14 +194,16 @@ class SpaceController(
|
|||||||
suspend fun editTransaction(
|
suspend fun editTransaction(
|
||||||
@PathVariable spaceId: String, @PathVariable id: String, @RequestBody transaction: Transaction
|
@PathVariable spaceId: String, @PathVariable id: String, @RequestBody transaction: Transaction
|
||||||
): Transaction {
|
): Transaction {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
transaction.space = space
|
transaction.space = space
|
||||||
return financialService.editTransaction(transaction)
|
return financialService.editTransaction(transaction, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/transactions/{id}")
|
@DeleteMapping("/{spaceId}/transactions/{id}")
|
||||||
suspend fun deleteTransaction(@PathVariable spaceId: String, @PathVariable id: String) {
|
suspend fun deleteTransaction(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
val transaction = financialService.getTransactionById(id)
|
val transaction = financialService.getTransactionById(id)
|
||||||
financialService.deleteTransaction(transaction)
|
financialService.deleteTransaction(transaction)
|
||||||
}
|
}
|
||||||
@@ -206,7 +218,8 @@ class SpaceController(
|
|||||||
@RequestParam("sort") sortBy: String = "name",
|
@RequestParam("sort") sortBy: String = "name",
|
||||||
@RequestParam("direction") direction: String = "ASC"
|
@RequestParam("direction") direction: String = "ASC"
|
||||||
): List<Category> {
|
): List<Category> {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return categoryService.getCategories(spaceId, type, sortBy, direction).awaitSingleOrNull().orEmpty()
|
return categoryService.getCategories(spaceId, type, sortBy, direction).awaitSingleOrNull().orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +232,8 @@ class SpaceController(
|
|||||||
suspend fun createCategory(
|
suspend fun createCategory(
|
||||||
@PathVariable spaceId: String, @RequestBody category: Category
|
@PathVariable spaceId: String, @RequestBody category: Category
|
||||||
): Category {
|
): Category {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return financialService.createCategory(space, category).awaitSingle()
|
return financialService.createCategory(space, category).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,33 +243,38 @@ class SpaceController(
|
|||||||
@RequestBody category: Category,
|
@RequestBody category: Category,
|
||||||
@PathVariable spaceId: String
|
@PathVariable spaceId: String
|
||||||
): Category {
|
): Category {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return categoryService.editCategory(space, category)
|
return categoryService.editCategory(space, category)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
||||||
suspend fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String) {
|
suspend fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String) {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
categoryService.deleteCategory(space, categoryId)
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
|
categoryService.deleteCategory(space, categoryId, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/categories/tags")
|
@GetMapping("/{spaceId}/categories/tags")
|
||||||
suspend fun getTags(@PathVariable spaceId: String): List<Tag> {
|
suspend fun getTags(@PathVariable spaceId: String): List<Tag> {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.getTags(space)
|
return spaceService.getTags(space)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/categories/tags")
|
@PostMapping("/{spaceId}/categories/tags")
|
||||||
suspend fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Tag {
|
suspend fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Tag {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.createTag(space, tag)
|
return spaceService.createTag(space, tag)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
||||||
suspend fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String) {
|
suspend fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String) {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return spaceService.deleteTag(space, tagId)
|
return spaceService.deleteTag(space, tagId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +290,8 @@ class SpaceController(
|
|||||||
|
|
||||||
@GetMapping("/{spaceId}/recurrents")
|
@GetMapping("/{spaceId}/recurrents")
|
||||||
suspend fun getRecurrents(@PathVariable spaceId: String): List<Recurrent> {
|
suspend fun getRecurrents(@PathVariable spaceId: String): List<Recurrent> {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return recurrentService.getRecurrents(spaceId)
|
return recurrentService.getRecurrents(spaceId)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -279,13 +299,15 @@ class SpaceController(
|
|||||||
|
|
||||||
@GetMapping("/{spaceId}/recurrents/{id}")
|
@GetMapping("/{spaceId}/recurrents/{id}")
|
||||||
suspend fun getRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Recurrent {
|
suspend fun getRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Recurrent {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return recurrentService.getRecurrentById(space, id).awaitSingle()
|
return recurrentService.getRecurrentById(space, id).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/recurrent")
|
@PostMapping("/{spaceId}/recurrent")
|
||||||
suspend fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Recurrent {
|
suspend fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Recurrent {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return recurrentService.createRecurrent(space, recurrent).awaitSingle()
|
return recurrentService.createRecurrent(space, recurrent).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,14 +317,16 @@ class SpaceController(
|
|||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestBody recurrent: Recurrent
|
@RequestBody recurrent: Recurrent
|
||||||
): Recurrent {
|
): Recurrent {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return recurrentService.editRecurrent(recurrent).awaitSingle()
|
return recurrentService.editRecurrent(recurrent).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/recurrent/{id}")
|
@DeleteMapping("/{spaceId}/recurrent/{id}")
|
||||||
suspend fun deleteRecurrent(@PathVariable spaceId: String, @PathVariable id: String) {
|
suspend fun deleteRecurrent(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
recurrentService.deleteRecurrent(id).awaitSingle()
|
recurrentService.deleteRecurrent(id).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class SubscriptionController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/notifyAll")
|
@PostMapping("/notifyAll")
|
||||||
fun notifyAll(@RequestBody payload: PushMessage): ResponseEntity<Any> {
|
suspend fun notifyAll(@RequestBody payload: PushMessage): ResponseEntity<Any> {
|
||||||
return try {
|
return try {
|
||||||
ResponseEntity.ok(subscriptionService.sendToAll(payload))
|
ResponseEntity.ok(subscriptionService.sendToAll(payload))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package space.luminic.budgerapp.controllers
|
|||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import space.luminic.budgerapp.models.WishList
|
import space.luminic.budgerapp.models.WishList
|
||||||
import space.luminic.budgerapp.models.WishListItem
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
import space.luminic.budgerapp.services.AuthService
|
||||||
import space.luminic.budgerapp.services.SpaceService
|
import space.luminic.budgerapp.services.SpaceService
|
||||||
import space.luminic.budgerapp.services.WishListService
|
import space.luminic.budgerapp.services.WishListService
|
||||||
|
|
||||||
@@ -11,24 +12,28 @@ import space.luminic.budgerapp.services.WishListService
|
|||||||
@RequestMapping("/spaces/{spaceId}/wishlists")
|
@RequestMapping("/spaces/{spaceId}/wishlists")
|
||||||
class WishListController(
|
class WishListController(
|
||||||
private val wishListService: WishListService,
|
private val wishListService: WishListService,
|
||||||
private val spaceService: SpaceService
|
private val spaceService: SpaceService,
|
||||||
|
private val authService: AuthService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
suspend fun findWishList(@PathVariable spaceId: String): List<WishList> {
|
suspend fun findWishList(@PathVariable spaceId: String): List<WishList> {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.findWishLists(spaceId)
|
return wishListService.findWishLists(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{wishListId}")
|
@GetMapping("/{wishListId}")
|
||||||
suspend fun getWishList(@PathVariable spaceId: String, @PathVariable wishListId: String): WishList {
|
suspend fun getWishList(@PathVariable spaceId: String, @PathVariable wishListId: String): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.getList(wishListId)
|
return wishListService.getList(wishListId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
suspend fun createWishList(@PathVariable spaceId: String, @RequestBody wishList: WishList): WishList {
|
suspend fun createWishList(@PathVariable spaceId: String, @RequestBody wishList: WishList): WishList {
|
||||||
val space = spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
val space = spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.createWishList(space, wishList)
|
return wishListService.createWishList(space, wishList)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +43,8 @@ class WishListController(
|
|||||||
@PathVariable wishListId: String,
|
@PathVariable wishListId: String,
|
||||||
@RequestBody wishList: WishList
|
@RequestBody wishList: WishList
|
||||||
): WishList {
|
): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.updateWishListInfo(wishList)
|
return wishListService.updateWishListInfo(wishList)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +55,8 @@ class WishListController(
|
|||||||
@PathVariable itemId: String,
|
@PathVariable itemId: String,
|
||||||
@RequestBody wishListItem: WishListItem
|
@RequestBody wishListItem: WishListItem
|
||||||
): WishList {
|
): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.updateWishListItemInfo(wishListId, wishListItem)
|
return wishListService.updateWishListItemInfo(wishListId, wishListItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +66,8 @@ class WishListController(
|
|||||||
@PathVariable wishListId: String,
|
@PathVariable wishListId: String,
|
||||||
@RequestBody wishListItem: WishListItem
|
@RequestBody wishListItem: WishListItem
|
||||||
): WishList {
|
): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.addItemToWishList(wishListId, wishListItem)
|
return wishListService.addItemToWishList(wishListId, wishListItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +77,15 @@ class WishListController(
|
|||||||
@PathVariable wishListId: String,
|
@PathVariable wishListId: String,
|
||||||
@PathVariable wishListItemId: String
|
@PathVariable wishListItemId: String
|
||||||
): WishList {
|
): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.removeItemFromWishList(wishListId, wishListItemId)
|
return wishListService.removeItemFromWishList(wishListId, wishListItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{wishListId}")
|
@DeleteMapping("/{wishListId}")
|
||||||
suspend fun deleteWishList(@PathVariable spaceId: String, @PathVariable wishListId: String) {
|
suspend fun deleteWishList(@PathVariable spaceId: String, @PathVariable wishListId: String) {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
wishListService.deleteWishList(wishListId)
|
wishListService.deleteWishList(wishListId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +93,9 @@ class WishListController(
|
|||||||
suspend fun cancelReserve(
|
suspend fun cancelReserve(
|
||||||
@PathVariable spaceId: String, @PathVariable wishListId: String,
|
@PathVariable spaceId: String, @PathVariable wishListId: String,
|
||||||
@PathVariable wishlistItemId: String
|
@PathVariable wishlistItemId: String
|
||||||
) : WishList {
|
): WishList {
|
||||||
spaceService.isValidRequest(spaceId)
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return wishListService.cancelReserve(wishListId, wishlistItemId)
|
return wishListService.cancelReserve(wishListId, wishlistItemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
22
src/main/kotlin/space/luminic/budgerapp/models/BotStates.kt
Normal file
22
src/main/kotlin/space/luminic/budgerapp/models/BotStates.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package space.luminic.budgerapp.models
|
||||||
|
|
||||||
|
import org.springframework.data.annotation.Id
|
||||||
|
import org.springframework.data.mongodb.core.mapping.DBRef
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document
|
||||||
|
|
||||||
|
enum class BotStates {
|
||||||
|
WAIT_CATEGORY
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document("bot-user-states")
|
||||||
|
data class BotUserState(
|
||||||
|
@Id val id: String? = null,
|
||||||
|
@DBRef val user: User,
|
||||||
|
var data: MutableList<ChatData> = mutableListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ChatData (
|
||||||
|
val chatId: String,
|
||||||
|
var state: BotStates,
|
||||||
|
var data: MutableMap<String, String> = mutableMapOf(),
|
||||||
|
)
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
package space.luminic.budgerapp.models
|
package space.luminic.budgerapp.models
|
||||||
|
|
||||||
|
|
||||||
open class NotFoundException(message: String) : Exception(message)
|
open class NotFoundException(message: String) : Exception(message)
|
||||||
|
open class TelegramBotException(message: String, val chatId: Long) : Exception(message)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package space.luminic.budgerapp.repos
|
||||||
|
|
||||||
|
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||||
|
import space.luminic.budgerapp.models.BotUserState
|
||||||
|
|
||||||
|
interface BotStatesRepo: ReactiveMongoRepository<BotUserState, String> {
|
||||||
|
}
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
package space.luminic.budgerapp.repos
|
package space.luminic.budgerapp.repos
|
||||||
|
|
||||||
|
|
||||||
|
import org.bson.types.ObjectId
|
||||||
|
import org.springframework.data.mongodb.repository.Query
|
||||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
import space.luminic.budgerapp.models.Subscription
|
import space.luminic.budgerapp.models.Subscription
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
interface SubscriptionRepo : ReactiveMongoRepository<Subscription, String> {
|
interface SubscriptionRepo : ReactiveMongoRepository<Subscription, String> {
|
||||||
|
|
||||||
|
@Query("{ \$and: [ " +
|
||||||
|
"{ 'user': { '\$ref': 'users', '\$id': ?0 } }, " +
|
||||||
|
"{ 'isActive': true } " +
|
||||||
|
"]}")
|
||||||
|
fun findByUserIdAndIsActive(userId: ObjectId): Flux<Subscription>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,10 @@ import kotlinx.coroutines.reactive.awaitFirstOrNull
|
|||||||
import kotlinx.coroutines.reactor.awaitSingle
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.springframework.cache.annotation.Cacheable
|
import org.springframework.cache.annotation.Cacheable
|
||||||
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import space.luminic.budgerapp.configs.AuthException
|
import space.luminic.budgerapp.configs.AuthException
|
||||||
import space.luminic.budgerapp.models.TokenStatus
|
import space.luminic.budgerapp.models.TokenStatus
|
||||||
import space.luminic.budgerapp.models.User
|
import space.luminic.budgerapp.models.User
|
||||||
@@ -28,6 +28,16 @@ class AuthService(
|
|||||||
) {
|
) {
|
||||||
private val passwordEncoder = BCryptPasswordEncoder()
|
private val passwordEncoder = BCryptPasswordEncoder()
|
||||||
|
|
||||||
|
suspend fun getSecurityUser(): User {
|
||||||
|
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
|
?: throw AuthException("Authentication failed")
|
||||||
|
val authentication = securityContextHolder.authentication
|
||||||
|
|
||||||
|
val username = authentication.name
|
||||||
|
// Получаем пользователя по имени
|
||||||
|
return userService.getByUsername(username)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun login(username: String, password: String): String {
|
suspend fun login(username: String, password: String): String {
|
||||||
val user = userRepository.findByUsername(username).awaitFirstOrNull()
|
val user = userRepository.findByUsername(username).awaitFirstOrNull()
|
||||||
?: throw UsernameNotFoundException("Пользователь не найден")
|
?: throw UsernameNotFoundException("Пользователь не найден")
|
||||||
|
|||||||
308
src/main/kotlin/space/luminic/budgerapp/services/BotService.kt
Normal file
308
src/main/kotlin/space/luminic/budgerapp/services/BotService.kt
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.bson.Document
|
||||||
|
import org.bson.types.ObjectId
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||||
|
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.query.Criteria
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.telegram.telegrambots.bots.TelegramLongPollingBot
|
||||||
|
import org.telegram.telegrambots.meta.api.methods.send.SendMessage
|
||||||
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage
|
||||||
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText
|
||||||
|
import org.telegram.telegrambots.meta.api.objects.Update
|
||||||
|
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup
|
||||||
|
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton
|
||||||
|
import org.telegram.telegrambots.meta.exceptions.TelegramApiException
|
||||||
|
import space.luminic.budgerapp.configs.TelegramBotConfig
|
||||||
|
import space.luminic.budgerapp.configs.TelegramBotProperties
|
||||||
|
import space.luminic.budgerapp.models.*
|
||||||
|
import space.luminic.budgerapp.repos.BotStatesRepo
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class BotService(
|
||||||
|
private val telegramBotProperties: TelegramBotProperties,
|
||||||
|
private val botStatesRepo: BotStatesRepo,
|
||||||
|
private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||||
|
private val userService: UserService,
|
||||||
|
private val categoriesService: CategoryService,
|
||||||
|
private val financialService: FinancialService,
|
||||||
|
private val spaceService: SpaceService,
|
||||||
|
) : TelegramLongPollingBot(telegramBotProperties.token) {
|
||||||
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
override fun getBotUsername(): String {
|
||||||
|
return telegramBotProperties.username
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpdateReceived(update: Update) = runBlocking {
|
||||||
|
logger.info("Received message $update")
|
||||||
|
try {
|
||||||
|
if (update.hasCallbackQuery()) {
|
||||||
|
processCallback(update)
|
||||||
|
} else if (update.hasMessage()) {
|
||||||
|
if (update.message.hasText()) {
|
||||||
|
processMessage(update)
|
||||||
|
} else if (update.message.hasPhoto()) {
|
||||||
|
processPhoto(update)
|
||||||
|
} else if (update.message.hasVideo()) {
|
||||||
|
processVideo(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: TelegramBotException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
logger.error(e.message)
|
||||||
|
sendMessage(e.chatId.toString(), "${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun processCallback(update: Update) {
|
||||||
|
val chatId = update.callbackQuery.message.chatId.toString()
|
||||||
|
val tgUserId = update.callbackQuery.from.id
|
||||||
|
val user = userService.getUserByTelegramId(tgUserId) ?: throw TelegramBotException(
|
||||||
|
"User ${update.callbackQuery.from.userName} not found",
|
||||||
|
chatId = update.callbackQuery.message.chatId
|
||||||
|
)
|
||||||
|
val state = getState(user.id!!)
|
||||||
|
if (state != null) {
|
||||||
|
when (state.data.first { it.chatId == chatId }.state) {
|
||||||
|
BotStates.WAIT_CATEGORY ->
|
||||||
|
if (update.callbackQuery.data.startsWith("category_")) {
|
||||||
|
confirmTransaction(
|
||||||
|
chatId,
|
||||||
|
user = userService.getUserByTelegramId(tgUserId)!!,
|
||||||
|
update
|
||||||
|
)
|
||||||
|
} else if (update.callbackQuery.data == "cancel") {
|
||||||
|
finishState(chatId, user)
|
||||||
|
val deleteMsg = DeleteMessage(chatId, update.callbackQuery.message.messageId)
|
||||||
|
execute(deleteMsg)
|
||||||
|
sendMessage(chatId, "Введите сумму и комментарий когда будете готовы.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun processMessage(update: Update) {
|
||||||
|
val user = userService.getUserByTelegramId(update.message.from.id) ?: throw TelegramBotException(
|
||||||
|
"Мы не знакомы",
|
||||||
|
chatId = update.message.chatId,
|
||||||
|
)
|
||||||
|
getState(user.id!!)?.data?.find { it.chatId == update.message.chatId.toString() }?.let {
|
||||||
|
if (it.state == BotStates.WAIT_CATEGORY) {
|
||||||
|
throw TelegramBotException(
|
||||||
|
"Уже есть открытый выбор категории",
|
||||||
|
update.message.chatId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newExpense(
|
||||||
|
update.message.chatId.toString(),
|
||||||
|
user = user,
|
||||||
|
text = update.message.text,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processPhoto(update: Update) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processVideo(update: Update) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendMessage(chatId: String, text: String) {
|
||||||
|
val message = SendMessage(chatId, text)
|
||||||
|
try {
|
||||||
|
execute(message)
|
||||||
|
} catch (e: TelegramApiException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun newExpense(chatId: String, user: User, text: String) {
|
||||||
|
|
||||||
|
val splitText = text.split(" ")
|
||||||
|
if (splitText.size < 2) {
|
||||||
|
try {
|
||||||
|
throw TelegramBotException("Сумма или комментарий не введены", chatId.toLong())
|
||||||
|
|
||||||
|
} catch (e: TelegramApiException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val sum = try {
|
||||||
|
splitText[0].toInt()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
throw TelegramBotException("Кажется первый параметр не цифра", chatId.toLong())
|
||||||
|
}
|
||||||
|
val textWOSum = splitText.drop(1)
|
||||||
|
var comment = ""
|
||||||
|
textWOSum.map { word ->
|
||||||
|
comment += "$word "
|
||||||
|
}
|
||||||
|
|
||||||
|
val categories =
|
||||||
|
categoriesService.getCategories(
|
||||||
|
"67af3c0f652da946a7dd9931",
|
||||||
|
"EXPENSE",
|
||||||
|
sortBy = "name",
|
||||||
|
direction = "ASC"
|
||||||
|
)
|
||||||
|
.awaitSingle()
|
||||||
|
val keyboard = InlineKeyboardMarkup()
|
||||||
|
val buttonLines = mutableListOf<MutableList<InlineKeyboardButton>>()
|
||||||
|
categories.map { category ->
|
||||||
|
val btn = InlineKeyboardButton.builder().text("${category.icon} ${category.name}")
|
||||||
|
.callbackData("category_${category.id}").build()
|
||||||
|
|
||||||
|
if (category.name.length >= 15) {
|
||||||
|
// Если текст длинный, создаём отдельную строку для кнопки
|
||||||
|
buttonLines.add(mutableListOf(btn))
|
||||||
|
} else {
|
||||||
|
var isAdded = false
|
||||||
|
|
||||||
|
// Пытаемся добавить кнопку в существующую строку
|
||||||
|
for (line in buttonLines) {
|
||||||
|
if (line.size < 2 && (line.isEmpty() || line[0].text.length < 14)) {
|
||||||
|
line.add(btn)
|
||||||
|
isAdded = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если не нашли подходящую строку, создаём новую
|
||||||
|
if (!isAdded) {
|
||||||
|
buttonLines.add(mutableListOf(btn))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val backButton = InlineKeyboardButton.builder().text("Отмена").callbackData("cancel").build()
|
||||||
|
|
||||||
|
buttonLines.add(mutableListOf(backButton))
|
||||||
|
keyboard.keyboard = buttonLines
|
||||||
|
|
||||||
|
val message = SendMessage()
|
||||||
|
message.chatId = chatId
|
||||||
|
val msg = "Выберите категорию"
|
||||||
|
message.text = msg
|
||||||
|
message.replyMarkup = keyboard
|
||||||
|
val userState = BotUserState(user = user)
|
||||||
|
val chatData =
|
||||||
|
userState.data.find { it.chatId == chatId } ?: ChatData(chatId, state = BotStates.WAIT_CATEGORY)
|
||||||
|
chatData.data["sum"] = sum.toString()
|
||||||
|
chatData.data["comment"] = comment
|
||||||
|
userState.data.add(chatData)
|
||||||
|
try {
|
||||||
|
execute(message)
|
||||||
|
setState(userState)
|
||||||
|
} catch (e: TelegramApiException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun confirmTransaction(chatId: String, user: User, update: Update) {
|
||||||
|
val state = getState(user.id!!)
|
||||||
|
if (state == null) {
|
||||||
|
sendMessage(chatId, "Не можем найти информацию о сумме и комментарии")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val stateData = state.data.find { it.chatId == chatId }
|
||||||
|
if (stateData == null) {
|
||||||
|
sendMessage(chatId, "Не можем найти информацию о сумме и комментарии")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val category = categoriesService.getCategories(
|
||||||
|
"67af3c0f652da946a7dd9931",
|
||||||
|
"EXPENSE",
|
||||||
|
sortBy = "name",
|
||||||
|
direction = "ASC"
|
||||||
|
)
|
||||||
|
.awaitSingle()
|
||||||
|
.first { it.id == update.callbackQuery.data.split("_")[1] }
|
||||||
|
val space = spaceService.getSpace("67af3c0f652da946a7dd9931")
|
||||||
|
val instantType = financialService.getTransactionTypes().first { it.code == "INSTANT" }
|
||||||
|
val transaction = financialService.createTransaction(
|
||||||
|
space,
|
||||||
|
transaction = Transaction(
|
||||||
|
space = space,
|
||||||
|
type = instantType,
|
||||||
|
user = user,
|
||||||
|
category = category,
|
||||||
|
comment = stateData.data["comment"]!!.trim(),
|
||||||
|
date = LocalDate.now(),
|
||||||
|
amount = stateData.data["sum"]!!.toDouble(),
|
||||||
|
isDone = true,
|
||||||
|
parentId = null,
|
||||||
|
createdAt = LocalDateTime.now()
|
||||||
|
),
|
||||||
|
user = user
|
||||||
|
)
|
||||||
|
val editMsg = EditMessageText()
|
||||||
|
editMsg.chatId = chatId
|
||||||
|
editMsg.messageId = update.callbackQuery.message.messageId
|
||||||
|
editMsg.text = "Успешно создали транзакцию c id ${transaction.id}"
|
||||||
|
try {
|
||||||
|
execute(editMsg)
|
||||||
|
// execute(msg)
|
||||||
|
finishState(chatId, user)
|
||||||
|
} catch (e: TelegramApiException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
logger.error(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun getState(userId: String): BotUserState? {
|
||||||
|
val lookup = lookup("users", "user.\$id", "_id", "userDetails")
|
||||||
|
val unwind = unwind("userDetails")
|
||||||
|
val match = match(Criteria.where("userDetails._id").`is`(ObjectId(userId)))
|
||||||
|
|
||||||
|
val aggregation = newAggregation(lookup, unwind, match)
|
||||||
|
return reactiveMongoTemplate.aggregate(aggregation, "bot-user-states", Document::class.java)
|
||||||
|
.next()
|
||||||
|
.map { doc ->
|
||||||
|
val dataList = doc.getList("data", Document::class.java)
|
||||||
|
BotUserState(
|
||||||
|
id = doc.getObjectId("_id").toString(),
|
||||||
|
user = User(doc.get("userDetails", Document::class.java).getObjectId("_id").toString()),
|
||||||
|
data = dataList.map {
|
||||||
|
val data = it.get("data", Document::class.java)
|
||||||
|
ChatData(
|
||||||
|
chatId = it.getString("chatId"),
|
||||||
|
state = BotStates.valueOf(it.getString("state")),
|
||||||
|
data = (data.toMap().mapValues { it.value.toString() }.toMutableMap())
|
||||||
|
)
|
||||||
|
}.toMutableList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.awaitSingleOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun setState(userState: BotUserState): BotUserState {
|
||||||
|
val stateToSave = userState.user.id?.let { userId ->
|
||||||
|
getState(userId)?.copy(data = userState.data)
|
||||||
|
?: BotUserState(user = userState.user, data = userState.data)
|
||||||
|
} ?: BotUserState(user = userState.user, data = userState.data)
|
||||||
|
|
||||||
|
return botStatesRepo.save(stateToSave).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun finishState(chatId: String, user: User) {
|
||||||
|
val state = getState(user.id!!)
|
||||||
|
state?.data?.removeIf { it.chatId == chatId }
|
||||||
|
state?.let { botStatesRepo.save(state).awaitSingle() }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,10 +17,7 @@ import org.springframework.data.mongodb.core.query.isEqualTo
|
|||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import space.luminic.budgerapp.mappers.CategoryMapper
|
import space.luminic.budgerapp.mappers.CategoryMapper
|
||||||
import space.luminic.budgerapp.models.Category
|
import space.luminic.budgerapp.models.*
|
||||||
import space.luminic.budgerapp.models.CategoryType
|
|
||||||
import space.luminic.budgerapp.models.NotFoundException
|
|
||||||
import space.luminic.budgerapp.models.Space
|
|
||||||
import space.luminic.budgerapp.repos.BudgetRepo
|
import space.luminic.budgerapp.repos.BudgetRepo
|
||||||
import space.luminic.budgerapp.repos.CategoryRepo
|
import space.luminic.budgerapp.repos.CategoryRepo
|
||||||
|
|
||||||
@@ -133,7 +130,7 @@ class CategoryService(
|
|||||||
return categoryRepo.save(category).awaitSingle() // Сохраняем категорию, если тип не изменился
|
return categoryRepo.save(category).awaitSingle() // Сохраняем категорию, если тип не изменился
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteCategory(space: Space, categoryId: String) {
|
suspend fun deleteCategory(space: Space, categoryId: String, author: User) {
|
||||||
findCategory(space, categoryId)
|
findCategory(space, categoryId)
|
||||||
val transactions = financialService.getTransactions(space.id!!, categoryId = categoryId).awaitSingle()
|
val transactions = financialService.getTransactions(space.id!!, categoryId = categoryId).awaitSingle()
|
||||||
if (transactions.isNotEmpty()) {
|
if (transactions.isNotEmpty()) {
|
||||||
@@ -154,7 +151,7 @@ class CategoryService(
|
|||||||
|
|
||||||
transactions.map { transaction ->
|
transactions.map { transaction ->
|
||||||
transaction.category = otherCategory
|
transaction.category = otherCategory
|
||||||
financialService.editTransaction(transaction)
|
financialService.editTransaction(transaction, author)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val budgets = financialService.findProjectedBudgets(
|
val budgets = financialService.findProjectedBudgets(
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.awaitAll
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.reactive.asFlow
|
import kotlinx.coroutines.reactive.asFlow
|
||||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||||
import kotlinx.coroutines.reactive.awaitSingle
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
@@ -35,6 +32,7 @@ import space.luminic.budgerapp.repos.CategoryRepo
|
|||||||
import space.luminic.budgerapp.repos.TransactionRepo
|
import space.luminic.budgerapp.repos.TransactionRepo
|
||||||
import space.luminic.budgerapp.repos.WarnRepo
|
import space.luminic.budgerapp.repos.WarnRepo
|
||||||
import java.time.*
|
import java.time.*
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.temporal.TemporalAdjusters
|
import java.time.temporal.TemporalAdjusters
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -48,7 +46,8 @@ class FinancialService(
|
|||||||
val reactiveMongoTemplate: ReactiveMongoTemplate,
|
val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||||
private val categoryRepo: CategoryRepo,
|
private val categoryRepo: CategoryRepo,
|
||||||
val transactionsMapper: TransactionsMapper,
|
val transactionsMapper: TransactionsMapper,
|
||||||
val budgetMapper: BudgetMapper
|
val budgetMapper: BudgetMapper,
|
||||||
|
private val subscriptionService: SubscriptionService
|
||||||
) {
|
) {
|
||||||
private val logger = LoggerFactory.getLogger(FinancialService::class.java)
|
private val logger = LoggerFactory.getLogger(FinancialService::class.java)
|
||||||
|
|
||||||
@@ -846,25 +845,45 @@ class FinancialService(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
|
||||||
suspend fun createTransaction(space: Space, transaction: Transaction): Transaction {
|
suspend fun createTransaction(space: Space, transaction: Transaction, user: User? = null): Transaction {
|
||||||
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
val author = user
|
||||||
val user = userService.getByUserNameWoPass(securityContextHolder.authentication.name)
|
?: userService.getByUserNameWoPass(
|
||||||
if (space.users.none { it.id.toString() == user.id }) {
|
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
|
||||||
|
)
|
||||||
|
if (space.users.none { it.id.toString() == author.id }) {
|
||||||
throw IllegalArgumentException("User does not have access to this Space")
|
throw IllegalArgumentException("User does not have access to this Space")
|
||||||
}
|
}
|
||||||
// Привязываем space и user к транзакции
|
// Привязываем space и user к транзакции
|
||||||
transaction.user = user
|
transaction.user = author
|
||||||
transaction.space = space
|
transaction.space = space
|
||||||
|
|
||||||
val savedTransaction = transactionsRepo.save(transaction).awaitSingle()
|
val savedTransaction = transactionsRepo.save(transaction).awaitSingle()
|
||||||
|
|
||||||
updateBudgetOnCreate(savedTransaction)
|
updateBudgetOnCreate(savedTransaction)
|
||||||
|
scope.launch {
|
||||||
|
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
|
||||||
|
val transactionType = if (transaction.type.code == "INSTANT") "текущую" else "плановую"
|
||||||
|
|
||||||
|
subscriptionService.sendToSpaceOwner(
|
||||||
|
space.owner!!.id!!, PushMessage(
|
||||||
|
title = "Новая транзакция в пространстве ${space.name}!",
|
||||||
|
body = "Пользователь ${author.username} создал $transactionType транзакцию на сумму ${transaction.amount.toInt()} с комментарием ${transaction.comment} с датой ${
|
||||||
|
dateFormatter.format(
|
||||||
|
transaction.date
|
||||||
|
)
|
||||||
|
}",
|
||||||
|
url = "https://luminic.space/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
return savedTransaction
|
return savedTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@CacheEvict(cacheNames = ["transactions", "budgets"], allEntries = true)
|
@CacheEvict(cacheNames = ["transactions", "budgets"], allEntries = true)
|
||||||
suspend fun editTransaction(transaction: Transaction): Transaction {
|
suspend fun editTransaction(transaction: Transaction, author: User): Transaction {
|
||||||
val oldStateOfTransaction = getTransactionById(transaction.id!!)
|
val oldStateOfTransaction = getTransactionById(transaction.id!!)
|
||||||
val changed = compareSumDateDoneIsChanged(oldStateOfTransaction, transaction)
|
val changed = compareSumDateDoneIsChanged(oldStateOfTransaction, transaction)
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
@@ -878,8 +897,57 @@ class FinancialService(
|
|||||||
transaction
|
transaction
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val space = transaction.space
|
||||||
val savedTransaction = transactionsRepo.save(transaction).awaitSingle()
|
val savedTransaction = transactionsRepo.save(transaction).awaitSingle()
|
||||||
updateBudgetOnEdit(oldStateOfTransaction, savedTransaction, amountDifference)
|
updateBudgetOnEdit(oldStateOfTransaction, savedTransaction, amountDifference)
|
||||||
|
scope.launch {
|
||||||
|
var whatChanged = "nothing"
|
||||||
|
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
|
||||||
|
val transactionType = if (transaction.type.code == "INSTANT") "текущую" else "плановую"
|
||||||
|
|
||||||
|
val sb = StringBuilder()
|
||||||
|
if (oldStateOfTransaction.amount != transaction.amount) {
|
||||||
|
sb.append("${oldStateOfTransaction.amount} → ${transaction.amount}\n")
|
||||||
|
whatChanged = "main"
|
||||||
|
}
|
||||||
|
if (oldStateOfTransaction.comment != transaction.comment) {
|
||||||
|
sb.append("${oldStateOfTransaction.comment} → ${transaction.comment}\n")
|
||||||
|
whatChanged = "main"
|
||||||
|
|
||||||
|
}
|
||||||
|
if (oldStateOfTransaction.date != transaction.date) {
|
||||||
|
sb.append("${dateFormatter.format(oldStateOfTransaction.date)} → ${dateFormatter.format(transaction.date)}\n")
|
||||||
|
whatChanged = "main"
|
||||||
|
}
|
||||||
|
if (!oldStateOfTransaction.isDone && transaction.isDone) {
|
||||||
|
whatChanged = "done_true"
|
||||||
|
}
|
||||||
|
if (oldStateOfTransaction.isDone && !transaction.isDone) {
|
||||||
|
whatChanged = "done_false"
|
||||||
|
}
|
||||||
|
|
||||||
|
val body: String = when (whatChanged) {
|
||||||
|
"main" -> {
|
||||||
|
"Пользователь ${author.username} изменил $transactionType транзакцию:\n$sb"
|
||||||
|
}
|
||||||
|
"done_true" -> {
|
||||||
|
"Пользователь ${author.username} выполнил ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
||||||
|
}
|
||||||
|
"done_false" -> {
|
||||||
|
"Пользователь ${author.username} отменил выполнение ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
||||||
|
}
|
||||||
|
else -> "Изменения не обнаружены, но что то точно изменилось"
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptionService.sendToSpaceOwner(
|
||||||
|
space?.owner!!.id!!, PushMessage(
|
||||||
|
title = "Новое действие в пространстве ${space.name}!",
|
||||||
|
body = body,
|
||||||
|
url = "https://luminic.space/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return savedTransaction
|
return savedTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,14 +37,9 @@ class SpaceService(
|
|||||||
private val tagRepo: TagRepo
|
private val tagRepo: TagRepo
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun isValidRequest(spaceId: String): Space {
|
|
||||||
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
|
||||||
?: throw AuthException("Authentication failed")
|
|
||||||
val authentication = securityContextHolder.authentication
|
|
||||||
|
|
||||||
val username = authentication.name
|
|
||||||
// Получаем пользователя по имени
|
suspend fun isValidRequest(spaceId: String, user: User): Space {
|
||||||
val user = userService.getByUsername(username)
|
|
||||||
val space = getSpace(spaceId)
|
val space = getSpace(spaceId)
|
||||||
|
|
||||||
// Проверяем доступ пользователя к пространству
|
// Проверяем доступ пользователя к пространству
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ package space.luminic.budgerapp.services
|
|||||||
|
|
||||||
import com.interaso.webpush.VapidKeys
|
import com.interaso.webpush.VapidKeys
|
||||||
import com.interaso.webpush.WebPushService
|
import com.interaso.webpush.WebPushService
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.reactive.awaitSingle
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.bson.types.ObjectId
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.dao.DuplicateKeyException
|
import org.springframework.dao.DuplicateKeyException
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import space.luminic.budgerapp.models.PushMessage
|
import space.luminic.budgerapp.models.PushMessage
|
||||||
import space.luminic.budgerapp.models.Subscription
|
import space.luminic.budgerapp.models.Subscription
|
||||||
import space.luminic.budgerapp.models.SubscriptionDTO
|
import space.luminic.budgerapp.models.SubscriptionDTO
|
||||||
@@ -36,38 +39,50 @@ class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
|
|||||||
vapidKeys = VapidKeys.fromUncompressedBytes(VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY)
|
vapidKeys = VapidKeys.fromUncompressedBytes(VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun sendNotification(endpoint: String, p256dh: String, auth: String, payload: PushMessage): Mono<Void> {
|
|
||||||
return Mono.fromRunnable<Void> {
|
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(
|
pushService.send(
|
||||||
payload = Json.encodeToString(payload),
|
payload = Json.encodeToString(payload),
|
||||||
endpoint = endpoint,
|
endpoint = endpoint,
|
||||||
p256dh = p256dh,
|
p256dh = p256dh,
|
||||||
auth = auth
|
auth = auth
|
||||||
)
|
)
|
||||||
|
logger.info("Уведомление успешно отправлено на endpoint: $endpoint")
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Ошибка при отправке уведомления на endpoint $endpoint: ${e.message}")
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
.doOnSuccess {
|
|
||||||
logger.info("Уведомление успешно отправлено на endpoint: $endpoint")
|
|
||||||
}
|
|
||||||
.doOnError { e ->
|
|
||||||
logger.error("Ошибка при отправке уведомления на endpoint $endpoint: ${e.message}")
|
|
||||||
}
|
|
||||||
.onErrorResume { e ->
|
|
||||||
Mono.error(e) // Пробрасываем ошибку дальше, если нужна обработка выше
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun sendToAll(payload: PushMessage): Mono<List<String>> {
|
suspend fun sendToAll(payload: PushMessage) {
|
||||||
return subscriptionRepo.findAll()
|
|
||||||
.flatMap { sub ->
|
subscriptionRepo.findAll().collectList().awaitSingle().forEach { sub ->
|
||||||
|
|
||||||
|
try {
|
||||||
sendNotification(sub.endpoint, sub.p256dh, sub.auth, payload)
|
sendNotification(sub.endpoint, sub.p256dh, sub.auth, payload)
|
||||||
.then(Mono.just("${sub.user?.username} at endpoint ${sub.endpoint}"))
|
} catch (e: Exception) {
|
||||||
.onErrorResume { e ->
|
sub.isActive = false
|
||||||
sub.isActive = false
|
subscriptionRepo.save(sub).awaitSingle()
|
||||||
subscriptionRepo.save(sub).then(Mono.empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.collectList() // Собираем результаты в список
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,12 @@ class UserService(val userRepo: UserRepo) {
|
|||||||
.switchIfEmpty(Mono.error(Exception("User not found"))) // Обрабатываем случай, когда пользователь не найден
|
.switchIfEmpty(Mono.error(Exception("User not found"))) // Обрабатываем случай, когда пользователь не найден
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getUserByTelegramId(telegramId: Long): User? {
|
||||||
|
return userRepo.findByTgId(telegramId.toString()).awaitSingleOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
@Cacheable("users", key = "#username")
|
|
||||||
|
@Cacheable("users", key = "#username")
|
||||||
suspend fun getByUserNameWoPass(username: String): User {
|
suspend fun getByUserNameWoPass(username: String): User {
|
||||||
return userRepo.findByUsernameWOPassword(username).awaitSingleOrNull()
|
return userRepo.findByUsernameWOPassword(username).awaitSingleOrNull()
|
||||||
?: throw NotFoundException("User with username: $username not found")
|
?: throw NotFoundException("User with username: $username not found")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ScheduledTasks(private val subscriptionService: SubscriptionService,
|
|||||||
private val logger = LoggerFactory.getLogger(ScheduledTasks::class.java)
|
private val logger = LoggerFactory.getLogger(ScheduledTasks::class.java)
|
||||||
|
|
||||||
@Scheduled(cron = "0 30 19 * * *")
|
@Scheduled(cron = "0 30 19 * * *")
|
||||||
fun sendNotificationOfMoneyFilling() {
|
suspend fun sendNotificationOfMoneyFilling() {
|
||||||
subscriptionService.sendToAll(
|
subscriptionService.sendToAll(
|
||||||
PushMessage(
|
PushMessage(
|
||||||
title = "Время заполнять траты!🤑",
|
title = "Время заполнять траты!🤑",
|
||||||
@@ -23,13 +23,6 @@ class ScheduledTasks(private val subscriptionService: SubscriptionService,
|
|||||||
badge = "/apple-touch-icon.png",
|
badge = "/apple-touch-icon.png",
|
||||||
url = "https://luminic.space/transactions/create"
|
url = "https://luminic.space/transactions/create"
|
||||||
)
|
)
|
||||||
).doOnNext { responses ->
|
|
||||||
responses.forEach { response ->
|
|
||||||
logger.info("Уведомление отправлено: $response")
|
|
||||||
}
|
|
||||||
}.subscribe(
|
|
||||||
{ logger.info("Все уведомления отправлены.") },
|
|
||||||
{ error -> logger.error("Ошибка при отправке уведомлений: ${error.message}", error) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,4 @@ spring.datasource.username=familybudget_app
|
|||||||
spring.datasource.password=FB1q2w3e4r!
|
spring.datasource.password=FB1q2w3e4r!
|
||||||
|
|
||||||
|
|
||||||
# ??????? JDBC
|
telegram.bot.token = 6972242509:AAGyXuL3T-BNE4XMoo_qvtaYxw_SuiS_dDs
|
||||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
|
||||||
|
|
||||||
# ????????? Hibernate
|
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
|
||||||
spring.jpa.hibernate.ddl-auto=none
|
|
||||||
|
|
||||||
@@ -1,27 +1,8 @@
|
|||||||
spring.application.name=budger-app
|
spring.application.name=budger-app
|
||||||
|
spring.data.mongodb.uri=mongodb://budger-app:BA1q2w3e4r!@luminic.space:27017/budger-app?authSource=admin&minPoolSize=10&maxPoolSize=100
|
||||||
spring.data.mongodb.host=77.222.32.64
|
|
||||||
spring.data.mongodb.port=27017
|
|
||||||
spring.data.mongodb.database=budger-app
|
|
||||||
#spring.data.mongodb.username=budger-app
|
#spring.data.mongodb.username=budger-app
|
||||||
#spring.data.mongodb.password=BA1q2w3e4r!
|
#spring.data.mongodb.password=BA1q2w3e4r!
|
||||||
#spring.data.mongodb.authentication-database=admin
|
#spring.data.mongodb.authentication-database=admin
|
||||||
|
|
||||||
|
|
||||||
management.endpoints.web.exposure.include=*
|
management.endpoints.web.exposure.include=*
|
||||||
management.endpoint.health.show-details=always
|
management.endpoint.health.show-details=always
|
||||||
|
telegram.bot.token=6972242509:AAGyXuL3T-BNE4XMoo_qvtaYxw_SuiS_dDs
|
||||||
|
|
||||||
|
|
||||||
spring.datasource.url=jdbc:postgresql://213.183.51.243/familybudget_app
|
|
||||||
spring.datasource.username=familybudget_app
|
|
||||||
spring.datasource.password=FB1q2w3e4r!
|
|
||||||
|
|
||||||
|
|
||||||
# ??????? JDBC
|
|
||||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
|
||||||
|
|
||||||
# ????????? Hibernate
|
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
|
||||||
spring.jpa.hibernate.ddl-auto=none
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ logging.level.org.springframework.data.mongodb.code = DEBUG
|
|||||||
#management.endpoint.metrics.access=read_only
|
#management.endpoint.metrics.access=read_only
|
||||||
|
|
||||||
|
|
||||||
|
telegram.bot.token = 6662300972:AAFXjk_h0AUCy4bORC12UcdXbYnh2QSVKAY
|
||||||
|
|
||||||
|
|||||||
@@ -34,3 +34,6 @@ management.endpoints.web.exposure.include=*
|
|||||||
# Enable Prometheus metrics export
|
# Enable Prometheus metrics export
|
||||||
management.prometheus.metrics.export.enabled=true
|
management.prometheus.metrics.export.enabled=true
|
||||||
|
|
||||||
|
telegram.bot.username = expenses_diary_bot
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user