..
This commit is contained in:
parent
cfcbf3c819
commit
d6ea6dc154
@ -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 ->
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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}")
|
||||||
|
|||||||
@ -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> {
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;*!*/
|
||||||
}
|
/*!*}*!*/
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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">-->
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user