diff --git a/pom.xml b/pom.xml
index 163ad53..f89a153 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,7 +47,18 @@
org.jetbrains.kotlin
kotlin-stdlib
-
+
+ jakarta.inject
+ jakarta.inject-api
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.postgresql
+ postgresql
+
org.springframework.boot
spring-boot-starter-test
@@ -58,6 +69,27 @@
kotlin-test-junit5
test
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ runtime
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ runtime
+ 0.11.5
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
diff --git a/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt
new file mode 100644
index 0000000..68d54d6
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt
@@ -0,0 +1,35 @@
+package com.coded.spring.ordering
+
+import com.coded.spring.ordering.entities.Roles
+import com.coded.spring.ordering.entities.UserEntity
+import com.coded.spring.ordering.repositories.UsersRepository
+
+import org.springframework.boot.CommandLineRunner
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Bean
+import org.springframework.security.crypto.password.PasswordEncoder
+
+@SpringBootApplication
+class InitUserRunner {
+ @Bean
+ fun initUsers(userRepository: UsersRepository, passwordEncoder: PasswordEncoder) = CommandLineRunner {
+ val user = UserEntity(
+ name = "HelloUser",
+ username = "testuser",
+ password = passwordEncoder.encode("password123"),
+ age = 18,
+ role = Roles.USER
+ )
+ if (userRepository.findByUsername(user.username) == null) {
+ println("Creating user ${user.username}")
+ userRepository.save(user)
+ } else {
+ println("User ${user.username} already exists")
+ }
+ }
+}
+
+fun main(args: Array) {
+ runApplication(*args).close()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt
new file mode 100644
index 0000000..7f652db
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt
@@ -0,0 +1,21 @@
+package com.coded.spring.ordering.authentication
+
+import com.coded.spring.ordering.repositories.UsersRepository
+import org.springframework.security.core.userdetails.*
+import org.springframework.stereotype.Service
+
+@Service
+class CustomUserDetailsService(
+ private val usersRepository: UsersRepository
+) : UserDetailsService {
+ override fun loadUserByUsername(username: String): UserDetails {
+ val user = usersRepository.findByUsername(username)
+ ?: throw UsernameNotFoundException("User not found")
+
+ return User.builder()
+ .username(user.username)
+ .password(user.password)
+ .roles(user.role.toString())
+ .build()
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt
new file mode 100644
index 0000000..a4880e1
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt
@@ -0,0 +1,58 @@
+package com.coded.spring.ordering.authentication
+
+
+import com.coded.spring.ordering.authentication.jwt.JwtAuthenticationFilter
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.authentication.AuthenticationManager
+import org.springframework.security.authentication.AuthenticationProvider
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.http.SessionCreationPolicy
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.web.SecurityFilterChain
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
+
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig(
+ private val jwtAuthFilter: JwtAuthenticationFilter,
+ private val userDetailsService: UserDetailsService
+) {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http.csrf { it.disable() }
+ .authorizeHttpRequests {
+ it.requestMatchers("/auth/**").permitAll()
+ .anyRequest().authenticated()
+ }
+ .sessionManagement {
+ it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ }
+ .authenticationProvider(authenticationProvider())
+ .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java)
+
+ return http.build()
+ }
+
+ @Bean
+ fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
+
+ @Bean
+ fun authenticationManager(config: AuthenticationConfiguration): AuthenticationManager =
+ config.authenticationManager
+
+ @Bean
+ fun authenticationProvider(): AuthenticationProvider {
+ val provider = DaoAuthenticationProvider()
+ provider.setUserDetailsService(userDetailsService)
+ provider.setPasswordEncoder(passwordEncoder())
+ return provider
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt
new file mode 100644
index 0000000..0440b75
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt
@@ -0,0 +1,60 @@
+package com.coded.spring.ordering.authentication.jwt
+
+import org.springframework.security.authentication.*
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.security.core.userdetails.UsernameNotFoundException
+import org.springframework.web.bind.annotation.*
+
+
+@RestController
+@RequestMapping("/auth")
+class AuthenticationController(
+ private val authenticationManager: AuthenticationManager,
+ private val userDetailsService: UserDetailsService,
+ private val jwtService: JwtService
+) {
+
+ @GetMapping("/test")
+ fun testing() = "test"
+
+ @PostMapping("/login")
+ fun login(@RequestBody authRequest: AuthenticationRequest): AuthenticationResponse {
+ println("🔐 Received login request: $authRequest")
+
+ val authToken = UsernamePasswordAuthenticationToken(authRequest.username, authRequest.password)
+ println("🔐 Created UsernamePasswordAuthenticationToken")
+
+ val authentication = try {
+ authenticationManager.authenticate(authToken)
+ } catch (ex: Exception) {
+ println("❌ Authentication failed: ${ex.message}")
+ throw UsernameNotFoundException("Invalid credentials")
+ }
+
+ println("✅ Authentication success: ${authentication.isAuthenticated}")
+ println("➡️ Authorities: ${authentication.authorities}")
+
+ if (authentication.isAuthenticated) {
+ val userDetails = userDetailsService.loadUserByUsername(authRequest.username)
+ println("✅ Loaded user details for: ${userDetails.username}")
+
+ val token = jwtService.generateToken(userDetails.username)
+ println("🔑 Generated JWT token: $token")
+
+ return AuthenticationResponse(token)
+ } else {
+ println("❌ Authentication marked as not authenticated")
+ throw UsernameNotFoundException("Invalid user request!")
+ }
+
+ }
+}
+
+data class AuthenticationRequest(
+ val username: String,
+ val password: String
+)
+
+data class AuthenticationResponse(
+ val token: String
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt
new file mode 100644
index 0000000..f180b7c
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt
@@ -0,0 +1,45 @@
+package com.coded.spring.ordering.authentication.jwt
+
+import jakarta.servlet.FilterChain
+import jakarta.servlet.http.*
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
+import org.springframework.stereotype.Component
+import org.springframework.web.filter.OncePerRequestFilter
+
+@Component
+class JwtAuthenticationFilter(
+ private val jwtService: JwtService,
+ private val userDetailsService: UserDetailsService
+) : OncePerRequestFilter() {
+
+ override fun doFilterInternal(
+ request: HttpServletRequest,
+ response: HttpServletResponse,
+ filterChain: FilterChain
+ ) {
+ val authHeader = request.getHeader("Authorization")
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ filterChain.doFilter(request, response)
+ return
+ }
+
+ val token = authHeader.substring(7)
+ val username = jwtService.extractUsername(token)
+
+ if (SecurityContextHolder.getContext().authentication == null) {
+ if (jwtService.isTokenValid(token, username)) {
+ val userDetails = userDetailsService.loadUserByUsername(username)
+ val authToken = UsernamePasswordAuthenticationToken(
+ userDetails, null, userDetails.authorities
+ )
+ authToken.details = WebAuthenticationDetailsSource().buildDetails(request)
+ SecurityContextHolder.getContext().authentication = authToken
+ }
+ }
+
+ filterChain.doFilter(request, response)
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt
new file mode 100644
index 0000000..c877c7c
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt
@@ -0,0 +1,42 @@
+package com.coded.spring.ordering.authentication.jwt
+
+import io.jsonwebtoken.*
+import io.jsonwebtoken.security.Keys
+import org.springframework.stereotype.Component
+import java.util.*
+import javax.crypto.SecretKey
+
+@Component
+class JwtService {
+
+ private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256)
+ private val expirationMs: Long = 1000 * 60 * 60
+
+ fun generateToken(username: String): String {
+ val now = Date()
+ val expiry = Date(now.time + expirationMs)
+
+ return Jwts.builder()
+ .setSubject(username)
+ .setIssuedAt(now)
+ .setExpiration(expiry)
+ .signWith(secretKey)
+ .compact()
+ }
+
+ fun extractUsername(token: String): String =
+ Jwts.parserBuilder()
+ .setSigningKey(secretKey)
+ .build()
+ .parseClaimsJws(token)
+ .body
+ .subject
+
+ fun isTokenValid(token: String, username: String): Boolean {
+ return try {
+ extractUsername(token) == username
+ } catch (e: Exception) {
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt
new file mode 100644
index 0000000..3fa2e3f
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt
@@ -0,0 +1,25 @@
+package com.coded.spring.ordering.controllers
+
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class HelloWorldController(){
+
+ @GetMapping("/hello")
+ fun helloWorld() = "Hello World"
+
+ @GetMapping("/boo")
+ fun testing(): String {
+ return "AAAA"
+ }
+
+ @PostMapping("/hello")
+ fun helloName(@RequestBody req: Name) = "Hello ${req.name}!"
+}
+
+data class Name(
+ val name:String
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt
new file mode 100644
index 0000000..5efe5d1
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt
@@ -0,0 +1,33 @@
+package com.coded.spring.ordering.controllers
+
+import com.coded.spring.ordering.entities.ItemEntity
+import com.coded.spring.ordering.entities.OrderEntity
+import com.coded.spring.ordering.repositories.OrdersRepository
+import com.coded.spring.ordering.services.OrdersService
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RestController
+
+
+@RestController
+
+class OrdersController(val ordersRepository: OrdersRepository, val ordersService: OrdersService) {
+
+ @PostMapping("/orders")
+ fun createNewOrder(@RequestBody req: OrderRequest) {
+ ordersService.createOrder(userId = req.user)
+ }
+
+ @GetMapping("/orders")
+ fun getOrders(): MutableList {
+ return ordersRepository.findAll()
+ }
+
+
+ data class OrderRequest(
+ val user: Long,
+ val restaurant: String,
+ val items: List
+ )
+}
diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt
new file mode 100644
index 0000000..59ea37e
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt
@@ -0,0 +1,14 @@
+package com.coded.spring.ordering.controllers
+
+import com.coded.spring.ordering.services.UsersService
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class UsersController(
+ private val usersService: UsersService
+){
+
+ @GetMapping("/users/v1/list")
+ fun users() = usersService.listUsers()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt
new file mode 100644
index 0000000..db854c8
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt
@@ -0,0 +1,17 @@
+package com.coded.spring.ordering.entities
+
+import jakarta.persistence.*
+
+
+@Entity
+@Table(name="items")
+data class ItemEntity(
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ val id: Long? = null,
+ val name: String,
+ val quantity: Int,
+ val order_id: Long
+){
+ constructor() : this(null, "", 0, 0)
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt
new file mode 100644
index 0000000..12ef48c
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt
@@ -0,0 +1,27 @@
+package com.coded.spring.ordering.entities
+
+import jakarta.persistence.Entity
+import jakarta.persistence.GeneratedValue
+import jakarta.persistence.GenerationType
+import jakarta.persistence.Id
+import jakarta.persistence.ManyToOne
+import jakarta.persistence.OneToMany
+import jakarta.persistence.Table
+
+@Entity
+@Table(name = "orders")
+data class OrderEntity(
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ val id: Long? = null,
+ val restaurant:String? =null,
+
+ @ManyToOne
+ val user: UserEntity,
+
+
+ @OneToMany(mappedBy = "order_id")
+ val items: List? = null
+){
+ constructor() : this(null,null, UserEntity(), listOf())
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt
new file mode 100644
index 0000000..a593071
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt
@@ -0,0 +1,25 @@
+package com.coded.spring.ordering.entities
+
+import jakarta.persistence.*
+
+@Entity
+@Table(name = "users")
+data class UserEntity(
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ var id: Long? = null,
+ var name: String,
+ var age: Int,
+ var username: String,
+ var password: String,
+
+ @Enumerated(EnumType.STRING)
+ val role: Roles = Roles.USER
+
+) {
+ constructor() : this(0, "name", 0, "username", "password", Roles.USER)
+}
+
+enum class Roles {
+ USER, ADMIN
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt
new file mode 100644
index 0000000..ccafe63
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt
@@ -0,0 +1,7 @@
+package com.coded.spring.ordering.repositories
+
+import com.coded.spring.ordering.entities.ItemEntity
+import org.springframework.data.jpa.repository.JpaRepository
+
+
+interface ItemsRepository: JpaRepository
diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt
new file mode 100644
index 0000000..f2a4d81
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt
@@ -0,0 +1,9 @@
+package com.coded.spring.ordering.repositories
+
+import com.coded.spring.ordering.entities.OrderEntity
+import jakarta.inject.Named
+import org.springframework.data.jpa.repository.JpaRepository
+
+@Named
+interface OrdersRepository : JpaRepository
+
diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt
new file mode 100644
index 0000000..8db0783
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt
@@ -0,0 +1,16 @@
+package com.coded.spring.ordering.repositories
+
+import com.coded.spring.ordering.entities.UserEntity
+import jakarta.inject.Named
+import org.springframework.data.jpa.repository.JpaRepository
+
+@Named
+interface UsersRepository : JpaRepository {
+ fun age(age: Int): MutableList
+
+ fun findByUsername(username: String): UserEntity?
+
+}
+
+
+
diff --git a/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt
new file mode 100644
index 0000000..2c80d27
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt
@@ -0,0 +1,29 @@
+package com.coded.spring.ordering.services
+
+import com.coded.spring.ordering.entities.ItemEntity
+import com.coded.spring.ordering.entities.OrderEntity
+import com.coded.spring.ordering.entities.UserEntity
+import com.coded.spring.ordering.repositories.OrdersRepository
+import com.coded.spring.ordering.repositories.UsersRepository
+import jakarta.inject.Named
+
+@Named
+class OrdersService(
+ private val usersRepository: UsersRepository,
+ private val orderRepository: OrdersRepository
+) {
+
+
+ fun createOrder(userId: Long): OrderResponse {
+ val user = usersRepository.findById(userId).get()
+ val newOrder = OrderEntity(user = user)
+ orderRepository.save(newOrder)
+ return OrderResponse(newOrder.user, newOrder.items)
+ }
+
+}
+
+data class OrderResponse(
+ val user: UserEntity,
+ val items: List?
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt b/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt
new file mode 100644
index 0000000..2627eae
--- /dev/null
+++ b/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt
@@ -0,0 +1,22 @@
+package com.coded.spring.ordering.services
+
+import com.coded.spring.ordering.repositories.UsersRepository
+import jakarta.inject.Named
+
+@Named
+class UsersService(
+ private val usersRepository: UsersRepository,
+) {
+
+ fun listUsers(): List = usersRepository.findAll().map {
+ UserResponse(
+ name = it.name,
+ age = it.age
+ )
+ }
+}
+
+data class UserResponse(
+ val name: String,
+ val age: Int
+)
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 3704dc6..e55b719 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,6 @@
spring.application.name=Kotlin.SpringbootV2
+
+spring.datasource.url=jdbc:postgresql://localhost:5433/myHelloDatabase
+spring.datasource.username=postgres
+spring.datasource.password=184481
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
\ No newline at end of file