From 36e4970354eddf29461ceb27975e08c72c1ffd17 Mon Sep 17 00:00:00 2001 From: oosuhada <185910926+oosuhada@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:15:34 +0900 Subject: [PATCH 1/2] fix: restore lines background with configurable color --- .../gitanimals/render/app/AnimationFacade.kt | 12 +++-- .../render/controller/AnimationController.kt | 2 + .../org/gitanimals/render/domain/User.kt | 37 ++++++++++++++-- .../gitanimals/render/domain/UserService.kt | 9 +++- .../org/gitanimals/render/domain/UserTest.kt | 44 +++++++++++++++++++ 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt b/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt index 59b7db6c..7736617e 100644 --- a/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt +++ b/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt @@ -47,19 +47,25 @@ class AnimationFacade( } } - fun getLineAnimation(username: String, personaId: Long, mode: Mode): String { + fun getLineAnimation( + username: String, + personaId: Long, + mode: Mode, + backgroundColor: String = "transparent", + ): String { return when (userService.existsByName(username)) { true -> { setUserAuthInfoIfNotSet(username) - val svgAnimation = userService.getLineAnimationByUsername(username, personaId, mode) + val svgAnimation = + userService.getLineAnimationByUsername(username, personaId, mode, backgroundColor) eventPublisher.publishEvent(Visited(username, MDC.get(TRACE_ID))) svgAnimation } false -> { val user = createOrUpdateUser(username) - userService.getLineAnimationByUsername(user.getName(), personaId, mode) + userService.getLineAnimationByUsername(user.getName(), personaId, mode, backgroundColor) } } } diff --git a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt index 84f499cd..5e9d205f 100644 --- a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt +++ b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt @@ -32,6 +32,7 @@ class AnimationController( @PathVariable("username") username: String, @RequestParam(name = "pet-id", defaultValue = "0") personaId: String, @RequestParam(name = "contribution-view", defaultValue = "true") contributionView: Boolean, + @RequestParam(name = "background-color", defaultValue = "white") backgroundColor: String, response: HttpServletResponse, ): String { response.cacheControl(3600) @@ -45,6 +46,7 @@ class AnimationController( username = username.deleteBrackets(), personaId = personaId.trimNotDigitCharacters().toLong(), mode = mode, + backgroundColor = backgroundColor, ) } diff --git a/src/main/kotlin/org/gitanimals/render/domain/User.kt b/src/main/kotlin/org/gitanimals/render/domain/User.kt index 15f1db4d..fc0fa465 100644 --- a/src/main/kotlin/org/gitanimals/render/domain/User.kt +++ b/src/main/kotlin/org/gitanimals/render/domain/User.kt @@ -273,8 +273,12 @@ class User( visit += 1 } - fun createLineAnimation(personaId: Long, mode: Mode): String { - val builder = StringBuilder().openLine() + fun createLineAnimation( + personaId: Long, + mode: Mode, + backgroundColor: String = "transparent", + ): String { + val builder = StringBuilder().openLine(backgroundColor.normalizeLineBackgroundColor()) val persona = personas.find { it.id >= personaId } ?: throw IllegalArgumentException("Cannot find persona by id \"$personaId\"") @@ -283,8 +287,33 @@ class User( return builder.closeSvg() } - private fun StringBuilder.openLine(): StringBuilder { - return this.append("") + private fun StringBuilder.openLine(backgroundColor: String): StringBuilder { + this.append("") + + if (backgroundColor != "transparent") { + this.append("") + } + + return this + } + + private fun String.normalizeLineBackgroundColor(): String { + val backgroundColor = trim() + + if (backgroundColor.equals("transparent", ignoreCase = true)) { + return "transparent" + } + + if (backgroundColor.equals("white", ignoreCase = true)) { + return "white" + } + + val hexColor = backgroundColor.removePrefix("#") + require(hexColor.length in setOf(3, 4, 6, 8) && hexColor.all { it.isDigit() || it.lowercaseChar() in 'a'..'f' }) { + "background-color must be 'white', 'transparent', or a 3/4/6/8 digit hex color" + } + + return "#$hexColor" } diff --git a/src/main/kotlin/org/gitanimals/render/domain/UserService.kt b/src/main/kotlin/org/gitanimals/render/domain/UserService.kt index 46d1bd03..7b54c4c0 100644 --- a/src/main/kotlin/org/gitanimals/render/domain/UserService.kt +++ b/src/main/kotlin/org/gitanimals/render/domain/UserService.kt @@ -25,8 +25,13 @@ class UserService( return getUserByName(username).createFarmAnimation() } - fun getLineAnimationByUsername(username: String, personaId: Long, mode: Mode): String { - return getUserByName(username).createLineAnimation(personaId, mode) + fun getLineAnimationByUsername( + username: String, + personaId: Long, + mode: Mode, + backgroundColor: String = "transparent", + ): String { + return getUserByName(username).createLineAnimation(personaId, mode, backgroundColor) } @Retryable(retryFor = [ObjectOptimisticLockingFailureException::class], maxAttempts = 10) diff --git a/src/test/kotlin/org/gitanimals/render/domain/UserTest.kt b/src/test/kotlin/org/gitanimals/render/domain/UserTest.kt index 2ff729b0..3acd8951 100644 --- a/src/test/kotlin/org/gitanimals/render/domain/UserTest.kt +++ b/src/test/kotlin/org/gitanimals/render/domain/UserTest.kt @@ -129,6 +129,50 @@ internal class UserTest( } } + describe("createLineAnimation 메소드는") { + context("personaId와 line mode를 받으면") { + val personaId = 1L + val user = user().apply { + personas.add(persona(id = personaId, user = this)) + } + + it("배경색을 지정하지 않으면 기존처럼 투명 배경의 line SVG를 생성한다") { + val animation = user.createLineAnimation(personaId, Mode.LINE) + + animation.contains("" + ) shouldBe true + } + + it("hex 배경색을 지정하면 해당 색상을 포함한다") { + val animation = user.createLineAnimation(personaId, Mode.LINE, "0d1117") + + animation.startsWith( + "" + ) shouldBe true + } + + it("transparent 배경을 지정하면 배경 rect를 추가하지 않는다") { + val animation = user.createLineAnimation(personaId, Mode.LINE, "transparent") + + animation.contains(" { + user.createLineAnimation(personaId, Mode.LINE, "url(#malicious)") + } + } + } + } + describe("mergePersona 메소드는") { context("increasePersonaId와 deletePersonaId를 받아서,") { val user = User.newUser("devxb", mapOf()) From 28d325bf194738d4ab853cb1bbf279f2095d2ddf Mon Sep 17 00:00:00 2001 From: oosuhada <185910926+oosuhada@users.noreply.github.com> Date: Tue, 1 Sep 2026 17:52:57 +0900 Subject: [PATCH 2/2] fix: preserve transparent lines background default --- .../org/gitanimals/render/controller/AnimationController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt index 5e9d205f..70d5373c 100644 --- a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt +++ b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt @@ -32,7 +32,7 @@ class AnimationController( @PathVariable("username") username: String, @RequestParam(name = "pet-id", defaultValue = "0") personaId: String, @RequestParam(name = "contribution-view", defaultValue = "true") contributionView: Boolean, - @RequestParam(name = "background-color", defaultValue = "white") backgroundColor: String, + @RequestParam(name = "background-color", defaultValue = "transparent") backgroundColor: String, response: HttpServletResponse, ): String { response.cacheControl(3600)