Skip to content

Commit

Permalink
Adding backend for an App
Browse files Browse the repository at this point in the history
### What's done:
- new backend project
- script for deployment
- small changes to frontend
  • Loading branch information
orchestr7 committed Sep 6, 2024
1 parent 8b443f3 commit 4023aaa
Show file tree
Hide file tree
Showing 21 changed files with 278 additions and 208 deletions.
34 changes: 34 additions & 0 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
plugins {
kotlin("multiplatform")
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.3.3"
id("io.spring.dependency-management") version "1.1.6"
kotlin("plugin.jpa") version "1.9.25"
}

kotlin {
jvm()
sourceSets {
jvmMain.dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
// implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
implementation("com.h2database:h2:2.3.232")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
}

jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}

tasks.withType<Test> {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.posidata.backend

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.ComponentScan

@SpringBootApplication
@ComponentScan(basePackages = ["ru.posidata"])
class BackendApplication

fun main(args: Array<String>) {
runApplication<BackendApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ru.posidata.backend.controller

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.*
import ru.posidata.backend.service.TelegramAuthService


@RestController
@RequestMapping(path = ["/api/v1/"])
class TelegramAuthController(private val telegramAuthService: TelegramAuthService) {
@GetMapping("/user-info")
fun getSelfUserInfo(authentication: Authentication?): String {
return authentication?.principal.toString()
}

@PostMapping("/test")
fun test(@RequestBody request: Map<String, Any>): String {
return "$request"
}

@PostMapping("auth/telegram")
fun telegramAuth(@RequestBody request: Map<String, Any>): ResponseEntity<Any> {
return try {
val authResult = telegramAuthService.authenticate(request)
if (authResult.isAuthenticated) {
ResponseEntity.ok(authResult.token)
} else {
ResponseEntity.status(HttpStatus.FORBIDDEN).body("Login info hash mismatch")
}
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.FORBIDDEN).body("Server error while authenticating")
}
}
}
15 changes: 15 additions & 0 deletions backend/src/jvmMain/kotlin/ru/posidata/backend/entity/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.posidata.backend.entity

import jakarta.persistence.*

@Entity
@Table(name = "users")
data class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
@Column(unique = true)
val telegramId: Long,
val firstName: String,
val lastName: String?,
val username: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.posidata.backend.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import ru.posidata.backend.entity.User

@Repository
interface UserRepository : JpaRepository<User, Long> {
fun findByTelegramId(telegramId: Long): User?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.posidata.backend.service

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.security.MessageDigest
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

@Service
class TelegramAuthService(
@Value("\${telegram.bot.token}") private val telegramToken: String,
private val userService: UserService
) {
fun authenticate(request: Map<String, Any>): AuthResult {
val hash = request["hash"] as String
val sortedRequest = request.toMutableMap().apply { remove("hash") }

val dataCheckString = sortedRequest.entries
.sortedBy { it.key.lowercase(Locale.getDefault()) }
.joinToString("\n") { "${it.key}=${it.value}" }

val secretKey = SecretKeySpec(
MessageDigest.getInstance("SHA-256").digest(telegramToken.toByteArray(Charsets.UTF_8)),
"HmacSHA256"
)
val mac = Mac.getInstance("HmacSHA256")
mac.init(secretKey)

val result = mac.doFinal(dataCheckString.toByteArray(Charsets.UTF_8))
val calculatedHash = result.joinToString("") { "%02x".format(it) }

return if (hash.equals(calculatedHash, ignoreCase = true)) {
val user = userService.findOrCreateUser(request)
println(user)
AuthResult(true, "token")
} else {
AuthResult(false, null)
}
}
}

data class AuthResult(val isAuthenticated: Boolean, val token: String?)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ru.posidata.backend.service

import org.springframework.stereotype.Service
import ru.posidata.backend.entity.User
import ru.posidata.backend.repository.UserRepository

@Service
class UserService(private val userRepository: UserRepository) {
fun findOrCreateUser(telegramData: Map<String, Any>): User {
val telegramId = telegramData["id"] as Long
return userRepository.findByTelegramId(telegramId) ?: createUser(telegramData)
}

private fun createUser(telegramData: Map<String, Any>): User {
val user = User(
telegramId = telegramData["id"] as Long,
firstName = telegramData["first_name"] as String,
lastName = telegramData["last_name"] as? String,
username = telegramData["username"] as? String
)
return userRepository.save(user)
}
}
8 changes: 8 additions & 0 deletions backend/src/jvmMain/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring:
application:
name:
backend

telegram:
bot:
token:
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.posidata.backend

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class BackendApplicationTests {

@Test
fun contextLoads() {
}

}
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
java
kotlin("multiplatform") version("2.0.0") apply(false)
}

group = "ru.posidata"
Expand Down
2 changes: 1 addition & 1 deletion common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform") version("2.0.0")
kotlin("multiplatform")
}

kotlin {
Expand Down
5 changes: 2 additions & 3 deletions frontend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform") version ("2.0.0")
kotlin("multiplatform")
}

kotlin {
Expand Down Expand Up @@ -59,7 +59,6 @@ kotlin {
implementation(npm("react-dom", "^18.0.0"))
implementation(npm("react-modal", "^3.0.0"))
implementation(npm("@popperjs/core", "2.11.8"))
implementation(npm("animate.css", "^4.1.1"))
// ====== font awesome ======
implementation(npm("@fortawesome/fontawesome-svg-core", "6.5.2"))
implementation(npm("@fortawesome/free-solid-svg-icons", "6.5.2"))
Expand All @@ -68,7 +67,7 @@ kotlin {
implementation(npm("@fortawesome/fontawesome-free", "6.5.2"))
implementation(npm("@fortawesome/react-fontawesome", "0.2.2"))
// ====== cookies ======
implementation(npm("js-cookie", "^3.0.5"))
// implementation(npm("js-cookie", "^3.0.5"))
// ====== animation =========
implementation(npm("animate.css", "^4.1.1"))
implementation(npm("react-tsparticles", "1.42.1"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import react.dom.html.ReactHTML.button
import react.dom.html.ReactHTML.span
import web.cssom.*


/**
* Wrapper for css style for a glowing neon text, see .glow.scss
*/
fun ChildrenBuilder.neonLightingText(input: String) {
button {
className = ClassName("glowing-btn")
Expand All @@ -19,4 +21,3 @@ fun ChildrenBuilder.neonLightingText(input: String) {
}
}
}

6 changes: 6 additions & 0 deletions frontend/src/jsMain/kotlin/ru/posidata/views/main/MainView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package ru.posidata.views.main
import js.objects.jso
import react.FC
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h1
import react.dom.html.ReactHTML.script
import react.react
import react.useState
import ru.posidata.views.utils.internals.Answer.NONE
Expand All @@ -26,12 +28,15 @@ val mainView = FC {
val (uniqueRandom, setUniqueRandom) = useState<List<Int>>(listOf())

div {

className = ClassName("full-width-container")
div {
className = ClassName("row justify-content-center align-items-center")
style = jso {
minHeight = "100vh".unsafeCast<MinHeight>()
}


div {
id = "back"
className = ClassName("card col-xl-4 col-lg-5 col-md-7 col-sm-8 col-12")
Expand All @@ -56,6 +61,7 @@ val mainView = FC {
this.setSelection = setSelection
}
}

QUESTION -> questionCard {
this.counter = counter
this.setCounter = setCounter
Expand Down
Loading

0 comments on commit 4023aaa

Please sign in to comment.