..
This commit is contained in:
parent
cfcbf3c819
commit
d6ea6dc154
@ -42,10 +42,10 @@ class SecurityConfig(
|
||||
}.authorizeHttpRequests { auth ->
|
||||
auth
|
||||
.requestMatchers(
|
||||
"/", "/home",
|
||||
"/", "/home.bs",
|
||||
"/bums/where.bs" ,
|
||||
"/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()
|
||||
.anyRequest().authenticated()
|
||||
}.formLogin { form ->
|
||||
|
||||
@ -32,6 +32,7 @@ import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import org.springframework.web.reactive.function.client.WebClient
|
||||
import reactor.core.publisher.Mono
|
||||
import java.io.*
|
||||
import java.net.URLDecoder
|
||||
import java.text.SimpleDateFormat
|
||||
@ -51,7 +52,7 @@ class BlogController() {
|
||||
private lateinit var locationLogService: LocationLogService
|
||||
|
||||
@Autowired
|
||||
private lateinit var postManageg: PostManageg
|
||||
private lateinit var postManager: PostManager
|
||||
|
||||
@Autowired
|
||||
lateinit var logService: LogService
|
||||
@ -99,12 +100,12 @@ class BlogController() {
|
||||
} else {
|
||||
logService.log("target.writeTime >>> ${target.writeTime}")
|
||||
target.modifyTime = System.currentTimeMillis()
|
||||
postManageg.save(target)
|
||||
postManager.save(target)
|
||||
target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post()
|
||||
target.originId = target.id
|
||||
target.id = null
|
||||
}
|
||||
var postMono = postManageg.save(target)
|
||||
var postMono = postManager.save(target)
|
||||
if (postMono != null) {
|
||||
lResultMsg = "save post"
|
||||
lResultCode = 0
|
||||
@ -128,10 +129,51 @@ class BlogController() {
|
||||
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}")
|
||||
fun viewer(@PathVariable postId : String) : ResultMV{
|
||||
val vm = ResultMV("content/blog/viewer")
|
||||
postManageg.getPost(postId).block().apply {
|
||||
postManager.getPost(postId).block().apply {
|
||||
this?.title = URLDecoder.decode(this?.title)
|
||||
this?.content = URLDecoder.decode(this?.content)
|
||||
|
||||
@ -142,14 +184,14 @@ class BlogController() {
|
||||
try {
|
||||
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.firstPostLat!!,this?.firstPostLon!!)).await()
|
||||
this.firstAddress = addrs.first().formattedAddress
|
||||
postManageg.save(this)
|
||||
postManager.save(this)
|
||||
} catch (e: Exception) {}
|
||||
}
|
||||
if (this?.modifyAddress?.length ?: 0 < 4){
|
||||
try {
|
||||
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.modifyLat!!,this?.modifyLon!!)).await()
|
||||
this.modifyAddress = addrs.first().formattedAddress
|
||||
postManageg.save(this)
|
||||
postManager.save(this)
|
||||
} catch (e: Exception) {}
|
||||
}
|
||||
}
|
||||
@ -168,7 +210,7 @@ class BlogController() {
|
||||
if (principal is UserDetails) {
|
||||
val username = principal.username
|
||||
// 추가 정보 사용 가능
|
||||
postManageg.find20()?.apply {
|
||||
postManager.find20()?.apply {
|
||||
forEach {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
val content = URLDecoder.decode(it.content)
|
||||
@ -187,7 +229,7 @@ class BlogController() {
|
||||
@GetMapping("editor/{postId}")
|
||||
fun editor(@PathVariable postId : String) : ResultMV{
|
||||
val vm = ResultMV("content/blog/editor")
|
||||
postManageg.getPost(postId).block().apply {
|
||||
postManager.getPost(postId).block().apply {
|
||||
this?.title = URLDecoder.decode(this?.title)
|
||||
this?.content = URLDecoder.decode(this?.content)
|
||||
vm.modelMap.put("srcPost",this)
|
||||
@ -200,7 +242,7 @@ class BlogController() {
|
||||
fun posts() : ResultMV{
|
||||
val vm = ResultMV("content/blog/posts")
|
||||
try {
|
||||
vm.modelMap.put("Posts", postManageg.find20().apply {
|
||||
vm.modelMap.put("Posts", postManager.find20().apply {
|
||||
this.forEach {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
|
||||
@ -35,7 +35,7 @@ class BumsPrivate {
|
||||
val m = ResultMV("content/private/where")
|
||||
|
||||
locationService.find10().apply {
|
||||
m.modelMap.put("locations",this.reversed())
|
||||
m.modelMap.put("locations",this)
|
||||
}
|
||||
m.setTitle("돼지 여기있다요~!!")
|
||||
return m
|
||||
|
||||
@ -2,7 +2,7 @@ package kr.lunaticbum.back.lun.controllers
|
||||
|
||||
import com.google.gson.Gson
|
||||
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.utils.LogService
|
||||
import net.coobird.thumbnailator.Thumbnails
|
||||
@ -29,13 +29,13 @@ class Home {
|
||||
lateinit var logService: LogService
|
||||
|
||||
@Autowired
|
||||
private lateinit var postManageg: PostManageg
|
||||
private lateinit var postManager: PostManager
|
||||
|
||||
@GetMapping("/","/home")
|
||||
@GetMapping("/","/home.bs")
|
||||
fun home() : ResultMV {
|
||||
val vm = ResultMV("content/home")
|
||||
try {
|
||||
vm.modelMap.put("Posts", postManageg.find4().apply {
|
||||
vm.modelMap.put("Posts", postManager.find4().apply {
|
||||
this.forEach {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
@ -105,7 +105,7 @@ class Home {
|
||||
@GetMapping("/h2")
|
||||
fun home2() : ResultMV {
|
||||
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 {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
@ -118,7 +118,7 @@ class Home {
|
||||
@GetMapping("/left-sidebar")
|
||||
fun lside() : ResultMV {
|
||||
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 {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
@ -130,7 +130,7 @@ class Home {
|
||||
@GetMapping("/no-sidebar")
|
||||
fun nside() : ResultMV {
|
||||
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 {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
@ -143,7 +143,7 @@ class Home {
|
||||
@GetMapping("/right-sidebar")
|
||||
fun rside() : ResultMV {
|
||||
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 {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
@ -156,7 +156,7 @@ class Home {
|
||||
@GetMapping("/two-sidebar")
|
||||
fun bside() : ResultMV {
|
||||
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 {
|
||||
it.title = URLDecoder.decode(it.title)
|
||||
it.content = URLDecoder.decode(it.content)
|
||||
|
||||
@ -10,14 +10,20 @@ import org.bson.codecs.pojo.annotations.BsonIgnore
|
||||
import org.jsoup.Jsoup
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
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.repository.Query
|
||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||
import org.springframework.data.repository.query.Param
|
||||
import org.springframework.stereotype.Repository
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.reactive.function.client.WebClient
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
|
||||
class BumsPrivate {
|
||||
@ -48,6 +54,8 @@ class LocationLog {
|
||||
var time : Long = 0L
|
||||
var userId : String? = null
|
||||
|
||||
var bettween : String? = null
|
||||
|
||||
override fun toString(): String {
|
||||
val buffer = StringBuffer()
|
||||
buffer.append(mFeatureName).append("|").append("\n")
|
||||
@ -72,7 +80,13 @@ class LocationLog {
|
||||
|
||||
@Repository
|
||||
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 findFirstByOrderByTimeDesc() : Mono<LocationLog>
|
||||
fun findFirstByUserIdOrderByTimeDesc(userId: String) : Mono<LocationLog>
|
||||
@ -91,8 +105,16 @@ class LocationLogService : LocationService {
|
||||
private lateinit var logRepository: LocationLogRepository
|
||||
|
||||
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? {
|
||||
return logRepository.findFirstByOrderByTimeDesc().block()
|
||||
}
|
||||
@ -100,7 +122,33 @@ class LocationLogService : LocationService {
|
||||
fun getLocationLogBy(userId : String) : LocationLog? {
|
||||
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) {
|
||||
println("saved msg before ${log}")
|
||||
|
||||
@ -9,13 +9,18 @@ import org.bson.codecs.pojo.annotations.BsonId
|
||||
import org.bson.codecs.pojo.annotations.BsonRepresentation
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
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.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.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.stereotype.Repository
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import java.net.URLDecoder
|
||||
import java.time.Duration
|
||||
|
||||
@Data
|
||||
@ -49,31 +54,90 @@ class Post {
|
||||
var modifyTime : Long = 0
|
||||
var modifyLat : 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
|
||||
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 findTop10ByOrderByReadCountDesc(): Flux<Post>
|
||||
fun findTop10ByOrderByModifyTimeDesc(): Flux<Post>
|
||||
}
|
||||
|
||||
|
||||
@Service
|
||||
class PostManageg {
|
||||
class PostManager(
|
||||
private val postRepository: PostRepository,
|
||||
private val reactiveMongoTemplate: ReactiveMongoTemplate
|
||||
) {
|
||||
@Autowired
|
||||
private lateinit var logService: LogService
|
||||
|
||||
@Autowired
|
||||
private lateinit var postRepository: PostRepository
|
||||
|
||||
@Autowired
|
||||
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> {
|
||||
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> {
|
||||
|
||||
@ -8,6 +8,11 @@ open class ResponceResult : BaseResult() {
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
open class PostsResult : BaseResult() {
|
||||
var posts: List<Post>? = null
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
open class LoginResult : ResponceResult() {
|
||||
|
||||
@ -91,3 +91,8 @@ resource.location=.
|
||||
server.forward-headers-strategy=framework
|
||||
#>>>>>>> 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
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
$(
|
||||
'<div id="titleBar">' +
|
||||
'<a href="#navPanel" class="toggle"></a>' +
|
||||
'<span class="title">' + $('#logo').html() + '</span>' +
|
||||
'<span class="title" onclick="javascript:gotoHome()">' + $('#logo').html() + '</span>' +
|
||||
'</div>'
|
||||
)
|
||||
.appendTo($body);
|
||||
|
||||
@ -13,13 +13,13 @@ html {
|
||||
background: black;
|
||||
}
|
||||
|
||||
#where{
|
||||
table-layout: fixed;
|
||||
}
|
||||
/*#where{*/
|
||||
/* table-layout: fixed;*/
|
||||
/*}*/
|
||||
|
||||
.where_item {
|
||||
display: table-cell;
|
||||
}
|
||||
/*.where_item {*/
|
||||
/* display: table-cell;*/
|
||||
/*}*/
|
||||
body {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
.layer {
|
||||
height: 100%;
|
||||
place-content: space-between;
|
||||
place-items: stretch;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
grid-auto-rows: minmax(200px, auto);
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, auto));
|
||||
width: 100%;
|
||||
}
|
||||
.where_item {
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
background: #F0F0F524;
|
||||
}
|
||||
/*.layer {*/
|
||||
/* height: 100%;*/
|
||||
/* place-content: space-between;*/
|
||||
/* place-items: stretch;*/
|
||||
/* display: grid;*/
|
||||
/* gap: 10px;*/
|
||||
/* grid-auto-rows: minmax(200px, auto);*/
|
||||
/* grid-template-columns: repeat(auto-fill, minmax(200px, auto));*/
|
||||
/* width: 100%;*/
|
||||
/*}*/
|
||||
/*!*.where_item {*!*/
|
||||
/*!* justify-content: space-between;*!*/
|
||||
/*!* flex-wrap: wrap;*!*/
|
||||
/*!* flex-direction: row;*!*/
|
||||
/*!* width: 100%;*!*/
|
||||
/*!* border-radius: 10px;*!*/
|
||||
/*!* background: #F0F0F524;*!*/
|
||||
/*!*}*!*/
|
||||
@ -4,6 +4,68 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
e.preventDefault(); // 기본 폼 제출 동작 방지
|
||||
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() {
|
||||
history.replaceState({}, null, location.pathname);
|
||||
@ -213,6 +275,11 @@ function logout() {
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function gotoHome() {
|
||||
console.log(`location.port >> ${location.port}`)
|
||||
location.href = getMainPath()+"/home.bs"
|
||||
}
|
||||
|
||||
function gotoLogin() {
|
||||
console.log(`location.port >> ${location.port}`)
|
||||
location.href = getMainPath()+"/login.bs"
|
||||
@ -222,6 +289,10 @@ function gotoJoin() {
|
||||
document.location.replace(getMainPath() + "/user/join.bs")
|
||||
}
|
||||
|
||||
function goToViewer(item) {
|
||||
location.href = getMainPath() + "/blog/viewer/" + item.id
|
||||
}
|
||||
|
||||
function goToView(path,id) {
|
||||
location.href = path + id;
|
||||
}
|
||||
|
||||
@ -57,8 +57,8 @@
|
||||
<article>
|
||||
<section class="col-6 col-12-narrower imp-narrower" th:each="post : ${Posts}">
|
||||
<!-- <span th:text="${cell}"></span>-->
|
||||
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}">
|
||||
<a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
|
||||
<div class="box post" onclick="goToViewer(this)" th:id="${post.id}">
|
||||
<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">
|
||||
<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>
|
||||
|
||||
@ -11,11 +11,7 @@
|
||||
<script type="text/javascript" th:src="@{/js/test.js}"></script>
|
||||
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
|
||||
<script th:inline="javascript">
|
||||
// document.addEventListener("DOMContentLoaded", onLoaded);
|
||||
function goToViewer(item) {
|
||||
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
|
||||
location.href = uploadUrl
|
||||
}
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
<th:block layout:fragment="content" id="content">
|
||||
@ -51,7 +47,7 @@
|
||||
<div class="row" th:each="row : ${Posts}">
|
||||
<section class="col-6 col-12-narrower" th:each="post : ${row}">
|
||||
<!-- <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>
|
||||
<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>
|
||||
|
||||
@ -12,10 +12,6 @@
|
||||
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener("DOMContentLoaded", onLoaded);
|
||||
function goToViewer(item) {
|
||||
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
|
||||
location.href = uploadUrl
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
<th:block layout:fragment="content" id="content">
|
||||
|
||||
@ -12,10 +12,7 @@
|
||||
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener("DOMContentLoaded", onLoaded);
|
||||
function goToViewer(item) {
|
||||
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
|
||||
location.href = uploadUrl
|
||||
}
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
<th:block layout:fragment="content" id="content">
|
||||
|
||||
@ -11,12 +11,13 @@
|
||||
<div id="main_layer">
|
||||
<div class="layer">
|
||||
<th:block id="where" th:each="location : ${locations}">
|
||||
<div class="where_item">
|
||||
<label th:text="${location.timeString}"/><br/>
|
||||
<label th:text="${location.mAddressLines}"/><br/>
|
||||
<label th:text="${location.mCountryName}"/><br/>
|
||||
<label th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"/><br/>
|
||||
<label th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"/><br/>
|
||||
<div class="where_item" style="font-family: monospace; white-space: nowrap;">
|
||||
<span th:text="${location.timeString}"></span> |
|
||||
<span th:text="${location.mAddressLines}"></span> |
|
||||
<span th:text="${location.bettween}"></span> |
|
||||
<span th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"></span> |
|
||||
<span th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"></span> |
|
||||
<span th:text="${location.mCountryName}"></span>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
@ -11,11 +11,7 @@
|
||||
<script type="text/javascript" th:src="@{/js/test.js}"></script>
|
||||
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener("DOMContentLoaded", onLoaded);
|
||||
function goToViewer(item) {
|
||||
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
|
||||
location.href = uploadUrl
|
||||
}
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
<th:block layout:fragment="content" id="content">-->
|
||||
|
||||
@ -11,11 +11,7 @@
|
||||
<script type="text/javascript" th:src="@{/js/test.js}"></script>
|
||||
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener("DOMContentLoaded", onLoaded);
|
||||
function goToViewer(item) {
|
||||
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
|
||||
location.href = uploadUrl
|
||||
}
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
<th:block layout:fragment="content" id="content">
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<section class="col-3 col-6-narrower col-12-mobilep">
|
||||
<h3>Links to Stuff</h3>
|
||||
<ul class="links">
|
||||
<h3>Rank of Views</h3>
|
||||
<ul class="rank_of_view" >
|
||||
<li><a href="#">Mattis et quis rutrum</a></li>
|
||||
<li><a href="#">Suspendisse amet varius</a></li>
|
||||
<li><a href="#">Sed et dapibus quis</a></li>
|
||||
@ -17,8 +17,8 @@
|
||||
</ul>
|
||||
</section>
|
||||
<section class="col-3 col-6-narrower col-12-mobilep">
|
||||
<h3>More Links to Stuff</h3>
|
||||
<ul class="links">
|
||||
<h3>Recent of Posts</h3>
|
||||
<ul class="recent_posts">
|
||||
<li><a href="#">Duis neque nisi dapibus</a></li>
|
||||
<li><a href="#">Sed et dapibus quis</a></li>
|
||||
<li><a href="#">Rutrum accumsan sed</a></li>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user