Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
eccbf56
Set highload-config branch to lab4
butvinm Jan 19, 2026
3c258fd
Set highload-config branch to lab4
butvinm Jan 19, 2026
e7bd681
Add _plan.md and _signal.json to gitigore
butvinm Jan 19, 2026
a0bfe31
Add _plan.md and _signal.json to gitigore
butvinm Jan 19, 2026
18ecf04
Add _workflow.md and _signal.json to gitigore
butvinm Jan 19, 2026
dbda658
Add _workflow.log to gitignore
butvinm Jan 19, 2026
747cb76
Add run_workflow.py to .gitignore
butvinm Jan 19, 2026
769dba0
Migrate tarot-service from JPA to R2DBC
butvinm Jan 19, 2026
9d6e668
Add _state.json to .gitignore
butvinm Jan 19, 2026
e89dfbc
Migrate user-service from Spring MVC + JPA to WebFlux + R2DBC
butvinm Jan 19, 2026
cc88718
Update CLAUDE.md to reflect reactive migration
butvinm Jan 19, 2026
23427c1
Update highload-config submodule with debug logging
butvinm Jan 20, 2026
128ab44
Fix Feign client issues in reactive WebFlux context
butvinm Jan 20, 2026
6a377fc
Phase 1: Refactor tarot-service to Clean Architecture
butvinm Jan 20, 2026
fe77ec2
Phase 2: Refactor user-service to Clean Architecture
butvinm Jan 20, 2026
7ab7031
Phase 3: Refactor divination-service to Clean Architecture
butvinm Jan 20, 2026
c84a8be
Phase 4: Update documentation for Clean Architecture
butvinm Jan 20, 2026
eeddfd4
Update README: fix outdated info and remove verbose sections
butvinm Jan 20, 2026
578246d
Phase 1: Add Kafka cluster to Docker Compose
butvinm Jan 20, 2026
7cabf5c
Phase 2: Add Kafka configuration to config server
butvinm Jan 20, 2026
7092b1a
Phase 3: Add event DTOs to shared-dto module
butvinm Jan 20, 2026
2667ac7
Phase 4: Add Spring Kafka to user-service
butvinm Jan 20, 2026
3a4df87
Phase 5: Integrate event publishing into user-service
butvinm Jan 20, 2026
ec45ca5
Phase 6: Add Spring Kafka to divination-service
butvinm Jan 20, 2026
ad63e5a
Phase 7: Integrate event publishing into divination-service
butvinm Jan 20, 2026
273127a
Phase 8: Wire services to Kafka in docker-compose
butvinm Jan 20, 2026
fa25c40
Phase 9: Update documentation for Kafka
butvinm Jan 20, 2026
ab57d66
Fix createdAt not populated after R2DBC save
butvinm Jan 20, 2026
e439be2
Add Kafka documentation to README
butvinm Jan 20, 2026
a07375b
Phase 1: Create notification-service project scaffold
butvinm Jan 21, 2026
bf401e3
Phase 2: Add domain and application layers
butvinm Jan 21, 2026
dca5efd
Phase 3: Add notification table migration
butvinm Jan 21, 2026
f0d8dac
Phase 4: Add R2DBC persistence layer
butvinm Jan 21, 2026
75cd236
Phase 5: Add internal spread owner endpoint
butvinm Jan 21, 2026
cf26716
Phase 6: Add Feign spread provider
butvinm Jan 21, 2026
6297709
Phase 7: Add Kafka event consumer
butvinm Jan 21, 2026
7130ba8
Phase 8: Add WebSocket infrastructure
butvinm Jan 21, 2026
7105809
Phase 9: Add REST controller and DTOs
butvinm Jan 21, 2026
620a921
Phase 10: Add security configuration
butvinm Jan 21, 2026
72651c3
Phase 11: Add integration tests
butvinm Jan 21, 2026
e405eae
Phase 12: Add gateway routes and docker-compose
butvinm Jan 21, 2026
b5af0c8
Phase 14: Update documentation
butvinm Jan 21, 2026
036666b
Add WebSocket test page for notification service
butvinm Jan 21, 2026
dad0c26
Phase 1: Add MinIO to Docker Compose
butvinm Jan 21, 2026
a65cdaa
Phase 2: Create files-service skeleton
butvinm Jan 21, 2026
1b3f040
Phase 3: Implement files-service domain and persistence layer
butvinm Jan 21, 2026
fc2b338
Phase 4: Implement MinIO storage infrastructure
butvinm Jan 21, 2026
de200c6
Phase 5: Implement files-service application service and security
butvinm Jan 21, 2026
5f23270
Phase 6: Add files-service API controllers
butvinm Jan 21, 2026
b576a10
Phase 7: Add Feign client for files-service
butvinm Jan 21, 2026
21130a7
Phase 8: Add attachment table to divination-service
butvinm Jan 21, 2026
9e10734
Phase 9: Add attachment domain model and repository
butvinm Jan 21, 2026
f57e3f4
Phase 10: Add FileProvider and update DTOs
butvinm Jan 21, 2026
2d8165e
Phase 11: Integrate attachments into DivinationService
butvinm Jan 21, 2026
147bac3
Phase 12: Add tests for files-service
butvinm Jan 21, 2026
5958e5e
Phase 13: Add tests for divination-service attachments
butvinm Jan 21, 2026
b1ec592
Phase 14: Add Kafka event publishing for files
butvinm Jan 21, 2026
efee537
Phase 15: Add gateway routes for files-service
butvinm Jan 21, 2026
fc1d597
Phase 16: Add E2E tests for file attachments
butvinm Jan 21, 2026
3023dc3
Phase 18: Update documentation
butvinm Jan 21, 2026
2b3bc00
Fix presigned URLs to use external endpoint for client access
butvinm Jan 21, 2026
d3ca1ab
Phase 1: Add Kafka consumer infrastructure to divination-service
butvinm Jan 21, 2026
eaeb40f
Phase 2: Add UserEventConsumer to divination-service
butvinm Jan 21, 2026
4897feb
Phase 3: Add unit tests for UserEventConsumer
butvinm Jan 21, 2026
cd4d0dd
Phase 4: Remove synchronous call from user-service
butvinm Jan 21, 2026
0b4df13
Phase 5: Update user-service tests
butvinm Jan 21, 2026
9ab293e
Phase 6: Remove internal endpoint from divination-service
butvinm Jan 21, 2026
a8f4292
Phase 7: Remove deleteUserData from shared Feign client
butvinm Jan 21, 2026
296c593
Phase 8: Update E2E tests for async behavior
butvinm Jan 21, 2026
128c272
Phase 9: Update documentation
butvinm Jan 21, 2026
5e769e7
Remove progress.md
butvinm Jan 21, 2026
d74ec1b
Set highload-config branch to main
butvinm Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ out/
### Python ###
.venv

.env
.env
_workflow.md
_workflow.log
_state.json
run_workflow.py
297 changes: 274 additions & 23 deletions CLAUDE.md

Large diffs are not rendered by default.

27 changes: 0 additions & 27 deletions PROGRESS.md

This file was deleted.

259 changes: 101 additions & 158 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion config-server/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ spring:
server:
git:
uri: https://github.com/butvinm-itmo/highload-config.git
default-label: revert-to-lab3
default-label: main
clone-on-start: true
timeout: 10

Expand Down
1 change: 1 addition & 0 deletions divination-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-config")
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client")
implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j")
implementation("org.springframework.kafka:spring-kafka")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-database-postgresql")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.github.butvinmitmo.divinationservice.api.controller

import com.github.butvinmitmo.divinationservice.application.service.DivinationService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
import java.util.UUID

/**
* Internal API controller for service-to-service communication.
* These endpoints are not exposed through the gateway and are only accessible
* via Eureka service discovery for internal operations.
*/
@RestController
@RequestMapping("/internal")
class InternalController(
private val divinationService: DivinationService,
) {
/**
* Gets the author (owner) ID of a spread.
* Called by notification-service to determine who should receive notifications.
*
* @param spreadId The ID of the spread
* @return 200 OK with author UUID, or 404 if spread not found
*/
@GetMapping("/spreads/{spreadId}/owner")
fun getSpreadOwner(
@PathVariable spreadId: UUID,
): Mono<ResponseEntity<UUID>> =
divinationService
.getSpreadAuthorId(spreadId)
.map { ResponseEntity.ok(it) }
.defaultIfEmpty(ResponseEntity.notFound().build())
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.butvinmitmo.divinationservice.controller
package com.github.butvinmitmo.divinationservice.api.controller

import com.github.butvinmitmo.divinationservice.service.DivinationService
import com.github.butvinmitmo.divinationservice.application.service.DivinationService
import com.github.butvinmitmo.shared.dto.CreateInterpretationRequest
import com.github.butvinmitmo.shared.dto.CreateInterpretationResponse
import com.github.butvinmitmo.shared.dto.InterpretationDto
Expand Down Expand Up @@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
import java.util.UUID

@RestController
Expand Down Expand Up @@ -83,7 +84,7 @@ class InterpretationController(
@Min(1)
@Max(50)
size: Int,
): reactor.core.publisher.Mono<ResponseEntity<List<InterpretationDto>>> =
): Mono<ResponseEntity<List<InterpretationDto>>> =
divinationService
.getInterpretations(spreadId, page, size)
.map { response ->
Expand Down Expand Up @@ -123,7 +124,7 @@ class InterpretationController(
@Parameter(description = "Interpretation ID", required = true)
@PathVariable
id: UUID,
): reactor.core.publisher.Mono<ResponseEntity<InterpretationDto>> =
): Mono<ResponseEntity<InterpretationDto>> =
divinationService
.getInterpretation(spreadId, id)
.map { interpretation -> ResponseEntity.ok(interpretation) }
Expand Down Expand Up @@ -199,10 +200,12 @@ class InterpretationController(
@PathVariable
spreadId: UUID,
@Valid @RequestBody request: CreateInterpretationRequest,
): reactor.core.publisher.Mono<ResponseEntity<CreateInterpretationResponse>> =
): Mono<ResponseEntity<CreateInterpretationResponse>> =
divinationService
.addInterpretation(spreadId, request)
.map { response -> ResponseEntity.status(HttpStatus.CREATED).body(response) }
.addInterpretation(spreadId, request.text, request.uploadId)
.map { result ->
ResponseEntity.status(HttpStatus.CREATED).body(CreateInterpretationResponse(id = result.id))
}

@PutMapping("/{id}")
@Operation(
Expand Down Expand Up @@ -265,9 +268,9 @@ class InterpretationController(
@PathVariable
id: UUID,
@Valid @RequestBody request: UpdateInterpretationRequest,
): reactor.core.publisher.Mono<ResponseEntity<InterpretationDto>> =
): Mono<ResponseEntity<InterpretationDto>> =
divinationService
.updateInterpretation(spreadId, id, request)
.updateInterpretation(spreadId, id, request.text)
.map { updatedInterpretation -> ResponseEntity.ok(updatedInterpretation) }

@DeleteMapping("/{id}")
Expand Down Expand Up @@ -314,11 +317,8 @@ class InterpretationController(
@Parameter(description = "Interpretation ID to delete", required = true)
@PathVariable
id: UUID,
): reactor.core.publisher.Mono<ResponseEntity<Void>> =
): Mono<ResponseEntity<Void>> =
divinationService
.deleteInterpretation(spreadId, id)
.then(
reactor.core.publisher.Mono
.just(ResponseEntity.noContent().build()),
)
.then(Mono.just(ResponseEntity.noContent().build()))
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.butvinmitmo.divinationservice.controller
package com.github.butvinmitmo.divinationservice.api.controller

import com.github.butvinmitmo.divinationservice.service.DivinationService
import com.github.butvinmitmo.divinationservice.application.service.DivinationService
import com.github.butvinmitmo.shared.dto.CreateSpreadRequest
import com.github.butvinmitmo.shared.dto.CreateSpreadResponse
import com.github.butvinmitmo.shared.dto.SpreadDto
Expand Down Expand Up @@ -88,8 +88,8 @@ class SpreadController(
@Valid @RequestBody request: CreateSpreadRequest,
): Mono<ResponseEntity<CreateSpreadResponse>> =
divinationService
.createSpread(request)
.map { response -> ResponseEntity.status(HttpStatus.CREATED).body(response) }
.createSpread(request.question, request.layoutTypeId)
.map { result -> ResponseEntity.status(HttpStatus.CREATED).body(CreateSpreadResponse(id = result.id)) }

@GetMapping
@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.provider

import com.github.butvinmitmo.shared.dto.CardDto
import com.github.butvinmitmo.shared.dto.LayoutTypeDto
import reactor.core.publisher.Mono
import java.util.UUID

interface CardProvider {
fun getLayoutTypeById(
requesterId: UUID,
requesterRole: String,
layoutTypeId: UUID,
): Mono<LayoutTypeDto>

fun getRandomCards(
requesterId: UUID,
requesterRole: String,
count: Int,
): Mono<List<CardDto>>

fun getAllCards(): Mono<List<CardDto>>

fun getSystemLayoutType(layoutTypeId: UUID): Mono<LayoutTypeDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.provider

import reactor.core.publisher.Mono
import java.util.UUID

interface CurrentUserProvider {
fun getCurrentUserId(): Mono<UUID>

fun getCurrentRole(): Mono<String>

fun canModify(authorId: UUID): Mono<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.provider

import com.github.butvinmitmo.shared.dto.FileUploadMetadataDto
import reactor.core.publisher.Mono
import java.util.UUID

/**
* Provider interface for file operations via files-service.
* Used to verify uploads, get metadata, and generate download URLs.
*/
interface FileProvider {
/**
* Verifies that an upload exists, belongs to the user, and marks it as completed.
*
* @param uploadId The ID of the upload to verify
* @param userId The ID of the user who should own the upload
* @return File metadata if verification succeeds
*/
fun verifyAndCompleteUpload(
uploadId: UUID,
userId: UUID,
): Mono<FileUploadMetadataDto>

/**
* Gets metadata for a completed file upload.
*
* @param uploadId The ID of the upload
* @return File metadata
*/
fun getUploadMetadata(uploadId: UUID): Mono<FileUploadMetadataDto>

/**
* Gets a presigned download URL for a file.
*
* @param uploadId The ID of the upload
* @return Presigned download URL
*/
fun getDownloadUrl(uploadId: UUID): Mono<String>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.provider

import com.github.butvinmitmo.shared.dto.UserDto
import reactor.core.publisher.Mono
import java.util.UUID

interface UserProvider {
fun getUserById(
requesterId: UUID,
requesterRole: String,
userId: UUID,
): Mono<UserDto>

fun getSystemUser(userId: UUID): Mono<UserDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.publisher

import com.github.butvinmitmo.divinationservice.domain.model.Interpretation
import reactor.core.publisher.Mono

interface InterpretationEventPublisher {
fun publishCreated(interpretation: Interpretation): Mono<Void>

fun publishUpdated(interpretation: Interpretation): Mono<Void>

fun publishDeleted(interpretation: Interpretation): Mono<Void>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.publisher

import com.github.butvinmitmo.divinationservice.domain.model.Spread
import reactor.core.publisher.Mono

interface SpreadEventPublisher {
fun publishCreated(spread: Spread): Mono<Void>

fun publishDeleted(spread: Spread): Mono<Void>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.repository

import com.github.butvinmitmo.divinationservice.domain.model.InterpretationAttachment
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.util.UUID

interface InterpretationAttachmentRepository {
fun save(attachment: InterpretationAttachment): Mono<InterpretationAttachment>

fun findByInterpretationId(interpretationId: UUID): Mono<InterpretationAttachment>

fun findByInterpretationIds(interpretationIds: List<UUID>): Flux<InterpretationAttachment>

fun deleteByInterpretationId(interpretationId: UUID): Mono<Void>

fun existsByInterpretationId(interpretationId: UUID): Mono<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.repository

import com.github.butvinmitmo.divinationservice.domain.model.Interpretation
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.util.UUID

interface InterpretationRepository {
fun save(interpretation: Interpretation): Mono<Interpretation>

fun findById(id: UUID): Mono<Interpretation>

fun findBySpreadIdOrderByCreatedAtDesc(
spreadId: UUID,
offset: Long,
limit: Int,
): Flux<Interpretation>

fun existsByAuthorAndSpread(
authorId: UUID,
spreadId: UUID,
): Mono<Boolean>

fun countBySpreadId(spreadId: UUID): Mono<Long>

fun countBySpreadIds(spreadIds: List<UUID>): Flux<Pair<UUID, Long>>

fun deleteById(id: UUID): Mono<Void>

fun deleteByAuthorId(authorId: UUID): Mono<Void>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.butvinmitmo.divinationservice.application.interfaces.repository

import com.github.butvinmitmo.divinationservice.domain.model.SpreadCard
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.util.UUID

interface SpreadCardRepository {
fun save(spreadCard: SpreadCard): Mono<SpreadCard>

fun findBySpreadId(spreadId: UUID): Flux<SpreadCard>
}
Loading
Loading