This commit is contained in:
lunaticbum 2025-08-05 11:24:23 +09:00
parent cfcbf3c819
commit d6ea6dc154
20 changed files with 306 additions and 89 deletions

View File

@ -42,10 +42,10 @@ class SecurityConfig(
}.authorizeHttpRequests { auth -> }.authorizeHttpRequests { auth ->
auth auth
.requestMatchers( .requestMatchers(
"/", "/home", "/", "/home.bs",
"/bums/where.bs" , "/bums/where.bs" ,
"/user/login.bs", "/user/signup.bs","/user/login.bjx", "/user/login.bs", "/user/signup.bs","/user/login.bjx",
"/blog/viewer/**" , "/blog/viewer/**" , "/blog/posts" , "/blog/rankOfViews.bjx","/blog/recentOfPost.bjx",
"/css/**", "/js/**", "/images/**", "/webjars/**", "/assets/**").permitAll() "/css/**", "/js/**", "/images/**", "/webjars/**", "/assets/**").permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
}.formLogin { form -> }.formLogin { form ->

View File

@ -32,6 +32,7 @@ import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile import org.springframework.web.multipart.MultipartFile
import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import java.io.* import java.io.*
import java.net.URLDecoder import java.net.URLDecoder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -51,7 +52,7 @@ class BlogController() {
private lateinit var locationLogService: LocationLogService private lateinit var locationLogService: LocationLogService
@Autowired @Autowired
private lateinit var postManageg: PostManageg private lateinit var postManager: PostManager
@Autowired @Autowired
lateinit var logService: LogService lateinit var logService: LogService
@ -99,12 +100,12 @@ class BlogController() {
} else { } else {
logService.log("target.writeTime >>> ${target.writeTime}") logService.log("target.writeTime >>> ${target.writeTime}")
target.modifyTime = System.currentTimeMillis() target.modifyTime = System.currentTimeMillis()
postManageg.save(target) postManager.save(target)
target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post() target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post()
target.originId = target.id target.originId = target.id
target.id = null target.id = null
} }
var postMono = postManageg.save(target) var postMono = postManager.save(target)
if (postMono != null) { if (postMono != null) {
lResultMsg = "save post" lResultMsg = "save post"
lResultCode = 0 lResultCode = 0
@ -128,10 +129,51 @@ class BlogController() {
return responce return responce
} }
@GetMapping("rankOfViews.bjx")
fun rankOfViews(httpServletRequest: HttpServletRequest): Mono<ResponseEntity<PostsResult>> {
logService.log(httpServletRequest.requestURI)
val resultCode = 0
val resultMsg = "Success"
return postManager.getTop10Posts()
.collectList() // Flux<Post> -> Mono<List<Post>>
.map { postsList ->
val postsResult = PostsResult().apply {
this.resultCode = resultCode
this.resultMsg = resultMsg
this.posts = postsList // List<Post> 할당 가능
}
ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(postsResult)
}
}
@GetMapping("recentOfPost.bjx")
fun recentOfPost(httpServletRequest: HttpServletRequest): Mono<ResponseEntity<PostsResult>> {
logService.log(httpServletRequest.requestURI)
val resultCode = 0
val resultMsg = "Success"
return postManager.getRecent10Posts()
.collectList() // Flux<Post> -> Mono<List<Post>>
.map { postsList ->
val postsResult = PostsResult().apply {
this.resultCode = resultCode
this.resultMsg = resultMsg
this.posts = postsList // List<Post> 할당 가능
}
ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(postsResult)
}
}
@GetMapping("viewer/{postId}") @GetMapping("viewer/{postId}")
fun viewer(@PathVariable postId : String) : ResultMV{ fun viewer(@PathVariable postId : String) : ResultMV{
val vm = ResultMV("content/blog/viewer") val vm = ResultMV("content/blog/viewer")
postManageg.getPost(postId).block().apply { postManager.getPost(postId).block().apply {
this?.title = URLDecoder.decode(this?.title) this?.title = URLDecoder.decode(this?.title)
this?.content = URLDecoder.decode(this?.content) this?.content = URLDecoder.decode(this?.content)
@ -142,14 +184,14 @@ class BlogController() {
try { try {
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.firstPostLat!!,this?.firstPostLon!!)).await() var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.firstPostLat!!,this?.firstPostLon!!)).await()
this.firstAddress = addrs.first().formattedAddress this.firstAddress = addrs.first().formattedAddress
postManageg.save(this) postManager.save(this)
} catch (e: Exception) {} } catch (e: Exception) {}
} }
if (this?.modifyAddress?.length ?: 0 < 4){ if (this?.modifyAddress?.length ?: 0 < 4){
try { try {
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.modifyLat!!,this?.modifyLon!!)).await() var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.modifyLat!!,this?.modifyLon!!)).await()
this.modifyAddress = addrs.first().formattedAddress this.modifyAddress = addrs.first().formattedAddress
postManageg.save(this) postManager.save(this)
} catch (e: Exception) {} } catch (e: Exception) {}
} }
} }
@ -168,7 +210,7 @@ class BlogController() {
if (principal is UserDetails) { if (principal is UserDetails) {
val username = principal.username val username = principal.username
// 추가 정보 사용 가능 // 추가 정보 사용 가능
postManageg.find20()?.apply { postManager.find20()?.apply {
forEach { forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
val content = URLDecoder.decode(it.content) val content = URLDecoder.decode(it.content)
@ -187,7 +229,7 @@ class BlogController() {
@GetMapping("editor/{postId}") @GetMapping("editor/{postId}")
fun editor(@PathVariable postId : String) : ResultMV{ fun editor(@PathVariable postId : String) : ResultMV{
val vm = ResultMV("content/blog/editor") val vm = ResultMV("content/blog/editor")
postManageg.getPost(postId).block().apply { postManager.getPost(postId).block().apply {
this?.title = URLDecoder.decode(this?.title) this?.title = URLDecoder.decode(this?.title)
this?.content = URLDecoder.decode(this?.content) this?.content = URLDecoder.decode(this?.content)
vm.modelMap.put("srcPost",this) vm.modelMap.put("srcPost",this)
@ -200,7 +242,7 @@ class BlogController() {
fun posts() : ResultMV{ fun posts() : ResultMV{
val vm = ResultMV("content/blog/posts") val vm = ResultMV("content/blog/posts")
try { try {
vm.modelMap.put("Posts", postManageg.find20().apply { vm.modelMap.put("Posts", postManager.find20().apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)

View File

@ -35,7 +35,7 @@ class BumsPrivate {
val m = ResultMV("content/private/where") val m = ResultMV("content/private/where")
locationService.find10().apply { locationService.find10().apply {
m.modelMap.put("locations",this.reversed()) m.modelMap.put("locations",this)
} }
m.setTitle("돼지 여기있다요~!!") m.setTitle("돼지 여기있다요~!!")
return m return m

View File

@ -2,7 +2,7 @@ package kr.lunaticbum.back.lun.controllers
import com.google.gson.Gson import com.google.gson.Gson
import jakarta.servlet.http.HttpServletResponse import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.PostManageg import kr.lunaticbum.back.lun.model.PostManager
import kr.lunaticbum.back.lun.model.ResultMV import kr.lunaticbum.back.lun.model.ResultMV
import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.LogService
import net.coobird.thumbnailator.Thumbnails import net.coobird.thumbnailator.Thumbnails
@ -29,13 +29,13 @@ class Home {
lateinit var logService: LogService lateinit var logService: LogService
@Autowired @Autowired
private lateinit var postManageg: PostManageg private lateinit var postManager: PostManager
@GetMapping("/","/home") @GetMapping("/","/home.bs")
fun home() : ResultMV { fun home() : ResultMV {
val vm = ResultMV("content/home") val vm = ResultMV("content/home")
try { try {
vm.modelMap.put("Posts", postManageg.find4().apply { vm.modelMap.put("Posts", postManager.find4().apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)
@ -105,7 +105,7 @@ class Home {
@GetMapping("/h2") @GetMapping("/h2")
fun home2() : ResultMV { fun home2() : ResultMV {
val vm = ResultMV("content/index_ex") val vm = ResultMV("content/index_ex")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply { vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)
@ -118,7 +118,7 @@ class Home {
@GetMapping("/left-sidebar") @GetMapping("/left-sidebar")
fun lside() : ResultMV { fun lside() : ResultMV {
val vm = ResultMV("content/left-sidebar") val vm = ResultMV("content/left-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply { vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)
@ -130,7 +130,7 @@ class Home {
@GetMapping("/no-sidebar") @GetMapping("/no-sidebar")
fun nside() : ResultMV { fun nside() : ResultMV {
val vm = ResultMV("content/no-sidebar") val vm = ResultMV("content/no-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply { vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)
@ -143,7 +143,7 @@ class Home {
@GetMapping("/right-sidebar") @GetMapping("/right-sidebar")
fun rside() : ResultMV { fun rside() : ResultMV {
val vm = ResultMV("content/right-sidebar") val vm = ResultMV("content/right-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply { vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)
@ -156,7 +156,7 @@ class Home {
@GetMapping("/two-sidebar") @GetMapping("/two-sidebar")
fun bside() : ResultMV { fun bside() : ResultMV {
val vm = ResultMV("content/two-sidebar") val vm = ResultMV("content/two-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply { vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach { this.forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content) it.content = URLDecoder.decode(it.content)

View File

@ -10,14 +10,20 @@ import org.bson.codecs.pojo.annotations.BsonIgnore
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.annotation.Id import org.springframework.data.annotation.Id
import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.mapping.Document import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.Query
import org.springframework.data.mongodb.repository.ReactiveMongoRepository import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.Duration import java.time.Duration
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
class BumsPrivate { class BumsPrivate {
@ -48,6 +54,8 @@ class LocationLog {
var time : Long = 0L var time : Long = 0L
var userId : String? = null var userId : String? = null
var bettween : String? = null
override fun toString(): String { override fun toString(): String {
val buffer = StringBuffer() val buffer = StringBuffer()
buffer.append(mFeatureName).append("|").append("\n") buffer.append(mFeatureName).append("|").append("\n")
@ -72,7 +80,13 @@ class LocationLog {
@Repository @Repository
interface LocationLogRepository : ReactiveMongoRepository<LocationLog, String> { interface LocationLogRepository : ReactiveMongoRepository<LocationLog, String> {
@Query("{ 'time' : { \$gte: ?0 } }")
fun findRecent(since: Long, sort: Sort): Flux<LocationLog>
// @Query("SELECT l FROM LocationLog l WHERE l.timeString >= :since ORDER BY l.timeString DESC")
// fun findRecent(@Param("since") since: String): Flux<LocationLog>
fun findTop30ByOrderByTimeDesc(): Flux<LocationLog>
fun findAllBy() : Mono<LocationLog> fun findAllBy() : Mono<LocationLog>
fun findFirstByOrderByTimeDesc() : Mono<LocationLog> fun findFirstByOrderByTimeDesc() : Mono<LocationLog>
fun findFirstByUserIdOrderByTimeDesc(userId: String) : Mono<LocationLog> fun findFirstByUserIdOrderByTimeDesc(userId: String) : Mono<LocationLog>
@ -91,8 +105,16 @@ class LocationLogService : LocationService {
private lateinit var logRepository: LocationLogRepository private lateinit var logRepository: LocationLogRepository
fun find10() : List<LocationLog> { fun find10() : List<LocationLog> {
return logRepository.findAll().takeLast(10).buffer(10).blockLast(Duration.ofSeconds(30)) ?: listOf() val sinceMills = System.currentTimeMillis() - ((24 * 60 * 60 * 1000) * 7)
println("sinceMills >> $sinceMills")
val sort = Sort.by(Sort.Direction.DESC, "time") // 오름차순 정렬
// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
// val since = LocalDateTime.now().minusHours(24).format(formatter)
// println("since >> $since")
val flux = filterByDistanceReactive(logRepository.findRecent(sinceMills,sort), 10.0)
return flux.collectList().block(Duration.ofSeconds(30)) ?: listOf()
} }
fun getLocationLog() : LocationLog? { fun getLocationLog() : LocationLog? {
return logRepository.findFirstByOrderByTimeDesc().block() return logRepository.findFirstByOrderByTimeDesc().block()
} }
@ -100,7 +122,33 @@ class LocationLogService : LocationService {
fun getLocationLogBy(userId : String) : LocationLog? { fun getLocationLogBy(userId : String) : LocationLog? {
return logRepository.findFirstByOrderByTimeDesc().block() return logRepository.findFirstByOrderByTimeDesc().block()
} }
fun filterByDistanceReactive(flux: Flux<LocationLog>, minDistanceMeter: Double): Flux<LocationLog> {
return flux
.buffer(2, 1)
.filter { pair ->
if (pair.size < 2) true
else haversine(pair[0].mLatitude, pair[0].mLongitude, pair[1].mLatitude, pair[1].mLongitude) >= minDistanceMeter
}
.map { pair ->
val distance = if (pair.size < 2) 0.0 else haversine(pair[0].mLatitude, pair[0].mLongitude, pair[1].mLatitude, pair[1].mLongitude)
val base = pair[0]
println("base >>> ${base.time} ${base.timeString}")
base.bettween = String.format("%.2f m", distance) // 소수점 두자리까지 거리 표시
base
}
}
// Haversine 거리계산 함수 (단위:m)
fun haversine(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val R = 6371000.0 // 지구 반지름(m)
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}
fun save(log: LocationLog) { fun save(log: LocationLog) {
println("saved msg before ${log}") println("saved msg before ${log}")

View File

@ -9,13 +9,18 @@ import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonRepresentation import org.bson.codecs.pojo.annotations.BsonRepresentation
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.mapping.Document import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import org.springframework.data.mongodb.repository.ReactiveMongoRepository import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import java.net.URLDecoder
import java.time.Duration import java.time.Duration
@Data @Data
@ -49,31 +54,90 @@ class Post {
var modifyTime : Long = 0 var modifyTime : Long = 0
var modifyLat : Double = 0.0 var modifyLat : Double = 0.0
var modifyLon : Double = 0.0 var modifyLon : Double = 0.0
var readCount : Long = 0
var voteCount : Long = 0
var unlikeCount : Long = 0
} }
@Document(collection = "Comment")
class Comment {
@BsonId
var id: String? = null
var postId: String? = null // 댓글이 달린 포스트의 id
var parentId: String? = null // 대댓글이면 상위 댓글의 id, 최상위 댓글이면 null
var writer: String? = null
var content: String? = null
var writeTime: Long? = null
var mentions: List<String>? = null // 언급된 유저 아이디(선택)
}
@Repository
interface CommentRepository : ReactiveMongoRepository<Comment, String> {
fun findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId: String): Flux<Comment> // 최상위 댓글
fun findByParentIdOrderByWriteTimeAsc(parentId: String): Flux<Comment>
}
@Service
class CommentService(private val commentRepository: CommentRepository) {
fun addComment(comment: Comment): Mono<Comment> {
// 예시: 부모 댓글 존재 여부/권한 검증 등 비즈니스 로직 처리
return commentRepository.save(comment)
}
fun getCommentsForPost(postId: String): Flux<Comment> {
return commentRepository.findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId)
}
// 기타: 대댓글 불러오기, 신고/삭제, 멘션 알림 등 확장 가능
}
@Repository @Repository
interface PostRepository : ReactiveMongoRepository<Post, String> { interface PostRepository : ReactiveMongoRepository<Post, String> {
fun findAllByModifyTime(time : Long? = 0): Flux<Post> fun findAllByModifyTime(time : Long? = 0): Flux<Post>
fun findAllByPostingTrue(pageable: Pageable): Flux<Post> fun findAllByPostingTrue(pageable: Pageable): Flux<Post>
fun findTop10ByOrderByReadCountDesc(): Flux<Post>
fun findTop10ByOrderByModifyTimeDesc(): Flux<Post>
} }
@Service @Service
class PostManageg { class PostManager(
private val postRepository: PostRepository,
private val reactiveMongoTemplate: ReactiveMongoTemplate
) {
@Autowired @Autowired
private lateinit var logService: LogService private lateinit var logService: LogService
@Autowired
private lateinit var postRepository: PostRepository
@Autowired @Autowired
private lateinit var bCryptPasswordEncoder: PasswordEncoder private lateinit var bCryptPasswordEncoder: PasswordEncoder
fun getPost(id : String) : Mono<Post> = postRepository.findById(id) // fun getPost(id : String) : Mono<Post> = postRepository.findById(id)
fun getPost(id: String): Mono<Post> {
val query = Query.query(Criteria.where("id").`is`(id))
val update = Update().inc("readCount", 1)
return reactiveMongoTemplate.findAndModify(query, update, Post::class.java)
.switchIfEmpty(Mono.error(NoSuchElementException("Post not found with id $id")))
}
fun find20(pageable :Pageable) : List<Post> { fun find20(pageable :Pageable) : List<Post> {
return postRepository.findAllByPostingTrue(pageable).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf() return postRepository.findAllByPostingTrue(pageable).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
} }
fun getTop10Posts(): Flux<Post> {
return postRepository.findTop10ByOrderByReadCountDesc().map { p ->
p.title = URLDecoder.decode(p.title)
p
}
}
fun getRecent10Posts(): Flux<Post> {
return postRepository.findTop10ByOrderByModifyTimeDesc().map { p ->
p.title = URLDecoder.decode(p.title)
p
}
}
fun find4() : List<Post> { fun find4() : List<Post> {

View File

@ -8,6 +8,11 @@ open class ResponceResult : BaseResult() {
} }
@Getter
open class PostsResult : BaseResult() {
var posts: List<Post>? = null
}
@Getter @Getter
open class LoginResult : ResponceResult() { open class LoginResult : ResponceResult() {

View File

@ -91,3 +91,8 @@ resource.location=.
server.forward-headers-strategy=framework server.forward-headers-strategy=framework
#>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59 #>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

View File

@ -39,7 +39,7 @@
$( $(
'<div id="titleBar">' + '<div id="titleBar">' +
'<a href="#navPanel" class="toggle"></a>' + '<a href="#navPanel" class="toggle"></a>' +
'<span class="title">' + $('#logo').html() + '</span>' + '<span class="title" onclick="javascript:gotoHome()">' + $('#logo').html() + '</span>' +
'</div>' '</div>'
) )
.appendTo($body); .appendTo($body);

View File

@ -13,13 +13,13 @@ html {
background: black; background: black;
} }
#where{ /*#where{*/
table-layout: fixed; /* table-layout: fixed;*/
} /*}*/
.where_item { /*.where_item {*/
display: table-cell; /* display: table-cell;*/
} /*}*/
body { body {
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;

View File

@ -1,18 +1,18 @@
.layer { /*.layer {*/
height: 100%; /* height: 100%;*/
place-content: space-between; /* place-content: space-between;*/
place-items: stretch; /* place-items: stretch;*/
display: grid; /* display: grid;*/
gap: 10px; /* gap: 10px;*/
grid-auto-rows: minmax(200px, auto); /* grid-auto-rows: minmax(200px, auto);*/
grid-template-columns: repeat(auto-fill, minmax(200px, auto)); /* grid-template-columns: repeat(auto-fill, minmax(200px, auto));*/
width: 100%; /* width: 100%;*/
} /*}*/
.where_item { /*!*.where_item {*!*/
justify-content: space-between; /*!* justify-content: space-between;*!*/
flex-wrap: wrap; /*!* flex-wrap: wrap;*!*/
flex-direction: row; /*!* flex-direction: row;*!*/
width: 100%; /*!* width: 100%;*!*/
border-radius: 10px; /*!* border-radius: 10px;*!*/
background: #F0F0F524; /*!* background: #F0F0F524;*!*/
} /*!*}*!*/

View File

@ -4,6 +4,68 @@ document.addEventListener('DOMContentLoaded', function() {
e.preventDefault(); // 기본 폼 제출 동작 방지 e.preventDefault(); // 기본 폼 제출 동작 방지
submitLoginForm(); submitLoginForm();
}); });
if (document.querySelector(".rank_of_view")) {
fetch('blog/rankOfViews.bjx')
.then(response => response.json())
.then(data => {
const ul = document.querySelector('.rank_of_view');
ul.innerHTML = ''; // 기존 리스트 지움
ul.style.listStyle = 'none'; // 불릿 제거
ul.style.paddingLeft = '0'; // 들여쓰기 제거 (선택사항)
// data가 ['제목1', '제목2', ...] 형식이라고 가정
data.posts.forEach(item => {
const date = new Date(item.writeTime);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const formatted = `${year}/${month}/${day}`;
const li = document.createElement('li');
const a = document.createElement('a');
a.id = item.id;
a.href = `${getMainPath()}/blog/viewer/${item.id}`;
a.textContent = `${item.title}[${year}/${month}/${day}]`
li.appendChild(a);
ul.appendChild(li);
});
})
.catch(error => {
console.error('받아오기 실패:', error);
}
);
}
if (document.querySelector(".recent_posts")) {
fetch('blog/recentOfPost.bjx')
.then(response => response.json())
.then(data => {
const ul = document.querySelector('.recent_posts');
ul.innerHTML = ''; // 기존 리스트 지움
ul.style.listStyle = 'none'; // 불릿 제거
ul.style.paddingLeft = '0'; // 들여쓰기 제거 (선택사항)
// data가 ['제목1', '제목2', ...] 형식이라고 가정
data.posts.forEach(item => {
const date = new Date(item.writeTime);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const formatted = `${year}/${month}/${day}`;
const li = document.createElement('li');
const a = document.createElement('a');
a.id = item.id;
a.href = `${getMainPath()}/blog/viewer/${item.id}`;
a.textContent = `${item.title}[${year}/${month}/${day}]`
li.appendChild(a);
ul.appendChild(li);
});
})
.catch(error => {
console.error('받아오기 실패:', error);
}
);
}
}); });
onload = function() { onload = function() {
history.replaceState({}, null, location.pathname); history.replaceState({}, null, location.pathname);
@ -213,6 +275,11 @@ function logout() {
form.submit(); form.submit();
} }
function gotoHome() {
console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/home.bs"
}
function gotoLogin() { function gotoLogin() {
console.log(`location.port >> ${location.port}`) console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/login.bs" location.href = getMainPath()+"/login.bs"
@ -222,6 +289,10 @@ function gotoJoin() {
document.location.replace(getMainPath() + "/user/join.bs") document.location.replace(getMainPath() + "/user/join.bs")
} }
function goToViewer(item) {
location.href = getMainPath() + "/blog/viewer/" + item.id
}
function goToView(path,id) { function goToView(path,id) {
location.href = path + id; location.href = path + id;
} }

View File

@ -57,8 +57,8 @@
<article> <article>
<section class="col-6 col-12-narrower imp-narrower" th:each="post : ${Posts}"> <section class="col-6 col-12-narrower imp-narrower" th:each="post : ${Posts}">
<!-- <span th:text="${cell}"></span>--> <!-- <span th:text="${cell}"></span>-->
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}"> <div class="box post" onclick="goToViewer(this)" th:id="${post.id}">
<a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a> <a href="javascript:goToViewer(this)" th:id="${post.id}" th:data="${post.id}" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
<div class="inner"> <div class="inner">
<h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3> <h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3>
<p th:text="${#strings.abbreviate(post.html, 80)}" class="ellipsis"></p> <p th:text="${#strings.abbreviate(post.html, 80)}" class="ellipsis"></p>

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script> <script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
// document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script> </script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">
@ -51,7 +47,7 @@
<div class="row" th:each="row : ${Posts}"> <div class="row" th:each="row : ${Posts}">
<section class="col-6 col-12-narrower" th:each="post : ${row}"> <section class="col-6 col-12-narrower" th:each="post : ${row}">
<!-- <span th:text="${cell}"></span>--> <!-- <span th:text="${cell}"></span>-->
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}"> <div class="box post" onclick="goToViewer(this)" th:id="${post.id}">
<a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a> <a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
<div class="inner"> <div class="inner">
<h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3> <h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3>

View File

@ -12,10 +12,6 @@
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded); document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script> </script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">

View File

@ -12,10 +12,7 @@
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded); document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script> </script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">

View File

@ -11,12 +11,13 @@
<div id="main_layer"> <div id="main_layer">
<div class="layer"> <div class="layer">
<th:block id="where" th:each="location : ${locations}"> <th:block id="where" th:each="location : ${locations}">
<div class="where_item"> <div class="where_item" style="font-family: monospace; white-space: nowrap;">
<label th:text="${location.timeString}"/><br/> <span th:text="${location.timeString}"></span> |
<label th:text="${location.mAddressLines}"/><br/> <span th:text="${location.mAddressLines}"></span> |
<label th:text="${location.mCountryName}"/><br/> <span th:text="${location.bettween}"></span> |
<label th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"/><br/> <span th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"></span> |
<label th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"/><br/> <span th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"></span> |
<span th:text="${location.mCountryName}"></span>
</div> </div>
</th:block> </th:block>
</div> </div>

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script> <script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script> </script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content">--> <th:block layout:fragment="content" id="content">-->

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script> <script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script> </script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">

View File

@ -5,8 +5,8 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<section class="col-3 col-6-narrower col-12-mobilep"> <section class="col-3 col-6-narrower col-12-mobilep">
<h3>Links to Stuff</h3> <h3>Rank of Views</h3>
<ul class="links"> <ul class="rank_of_view" >
<li><a href="#">Mattis et quis rutrum</a></li> <li><a href="#">Mattis et quis rutrum</a></li>
<li><a href="#">Suspendisse amet varius</a></li> <li><a href="#">Suspendisse amet varius</a></li>
<li><a href="#">Sed et dapibus quis</a></li> <li><a href="#">Sed et dapibus quis</a></li>
@ -17,8 +17,8 @@
</ul> </ul>
</section> </section>
<section class="col-3 col-6-narrower col-12-mobilep"> <section class="col-3 col-6-narrower col-12-mobilep">
<h3>More Links to Stuff</h3> <h3>Recent of Posts</h3>
<ul class="links"> <ul class="recent_posts">
<li><a href="#">Duis neque nisi dapibus</a></li> <li><a href="#">Duis neque nisi dapibus</a></li>
<li><a href="#">Sed et dapibus quis</a></li> <li><a href="#">Sed et dapibus quis</a></li>
<li><a href="#">Rutrum accumsan sed</a></li> <li><a href="#">Rutrum accumsan sed</a></li>