This commit is contained in:
lunaticbum 2024-10-24 18:04:29 +09:00
parent 21df7cd3c3
commit 5979281bab
13 changed files with 194 additions and 36 deletions

View File

@ -52,7 +52,7 @@ dependencies {
implementation ("com.drewnoakes:metadata-extractor:2.19.0") implementation ("com.drewnoakes:metadata-extractor:2.19.0")
implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-security")
compileOnly("org.projectlombok:lombok") compileOnly("org.projectlombok:lombok")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client") // runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")

View File

@ -18,7 +18,6 @@ import org.springframework.core.io.Resource
import org.springframework.core.io.UrlResource import org.springframework.core.io.UrlResource
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
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
@ -32,7 +31,9 @@ import java.util.*
@RestController @RestController
@RequestMapping("/blog") @RequestMapping("/blog")
class BlogController() { class BlogController() {
companion object {
val TEMPTOKEN = "TEMP_TOKEN_VIBUM"
}
@Autowired @Autowired
lateinit var globalEvv : GlobalEnvironment lateinit var globalEvv : GlobalEnvironment
@ -48,7 +49,7 @@ class BlogController() {
@GetMapping("write/{token}","write") @GetMapping("write/{token}","write")
fun writ(@PathVariable token : String? ) : ModelAndView{ fun writ(@PathVariable token : String? ) : ModelAndView{
val vm = ModelAndView("content/blog/write") val vm = ModelAndView("content/blog/write")
if (token.equals("TEMP_TOKEN_VIBUM")) { if (token.equals(TEMPTOKEN)) {
vm.modelMap.put(WRITE_PERMISSION_KEY,"OK") vm.modelMap.put(WRITE_PERMISSION_KEY,"OK")
vm.modelMap.put(EncTypeKey, EncType11) vm.modelMap.put(EncTypeKey, EncType11)
vm.modelMap.put(ApiKeyWordKey,"WRITE") vm.modelMap.put(ApiKeyWordKey,"WRITE")
@ -88,16 +89,20 @@ class BlogController() {
var fullData = arrayListOf<String>() var fullData = arrayListOf<String>()
for (idx in 0..max) { if (idx % 2 == 0) { if (nb.size > 0) { fullData.add(nb.removeLast()) } } else { if (na.size > 0) { fullData.add(na.removeLast()) } } } for (idx in 0..max) { if (idx % 2 == 0) { if (nb.size > 0) { fullData.add(nb.removeLast()) } } else { if (na.size > 0) { fullData.add(na.removeLast()) } } }
logService.log(fullData.joinToString("")) logService.log(fullData.joinToString(""))
val target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post() var target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post()
if (target.writeTime < 1L) { if (target.writeTime < 1L) {
target.id = null
target.writeTime = System.currentTimeMillis() target.writeTime = System.currentTimeMillis()
} else { } else {
logService.log("target.writeTime >>> ${target.writeTime}")
target.modifyTime = System.currentTimeMillis()
postManageg.save(target)
target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post()
target.originId = target.id target.originId = target.id
target.id = null target.id = null
target.modifyTime = System.currentTimeMillis()
} }
var user = postManageg.save(target) var postMono = postManageg.save(target)
if (user != null) { if (postMono != null) {
lResultMsg = "save post" lResultMsg = "save post"
lResultCode = 0 lResultCode = 0
} else { } else {
@ -134,20 +139,36 @@ class BlogController() {
} }
@GetMapping("modify") @GetMapping("modify")
fun modify() : ModelAndView{ fun modify(@RequestParam("token") token : String?) : ModelAndView{
logService.log("incoming modify") logService.log("incoming modify")
val vm = ModelAndView("content/blog/modify") val vm = ModelAndView("content/blog/modify")
postManageg.find20()?.apply { if (TEMPTOKEN.equals(token)) {
forEach { it.title = URLDecoder.decode(it.title)} postManageg.find20()?.apply {
vm.modelMap.put("posts",this) forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
}
vm.modelMap.put("chunkedPosts", this.chunked(3))
}
vm.modelMap.put(WRITE_PERMISSION_KEY,"OK")
vm.modelMap.put("path","editor/")
vm.modelMap.put("SK",token)
} else {
vm.modelMap.put(WRITE_PERMISSION_KEY,"NO")
} }
vm.modelMap.put("rowKey","chunkedPosts_")
return vm return vm
} }
@GetMapping("editor/{postId}") @GetMapping("editor/{postId}")
fun editor(@PathVariable postId : String) : ModelAndView{ fun editor(@PathVariable postId : String, @RequestParam("token") token : String?) : ModelAndView{
val vm = ModelAndView("content/blog/editor") val vm = ModelAndView("content/blog/editor")
vm.modelMap.put("srcPost",postManageg.getPost(postId).block().apply { })
if (TEMPTOKEN.equals(token)) {
vm.modelMap.put(WRITE_PERMISSION_KEY,"OK")
} else {
vm.modelMap.put(WRITE_PERMISSION_KEY,"NO")
}
return vm return vm
} }

View File

@ -1,6 +1,7 @@
package kr.lunaticbum.back.lun.controllers package kr.lunaticbum.back.lun.controllers
import com.google.gson.Gson import com.google.gson.Gson
import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.PostManageg import kr.lunaticbum.back.lun.model.PostManageg
import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.LogService
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
@ -8,6 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.ModelAndView import org.springframework.web.servlet.ModelAndView
import java.net.http.HttpClient.Redirect
@RestController @RestController
@RequestMapping() @RequestMapping()
@ -29,7 +31,10 @@ class Home {
}) })
return vm return vm
} }
@GetMapping("/login")
fun login(response: HttpServletResponse) {
response.sendRedirect("/user/login")
}
@GetMapping("/licenses") @GetMapping("/licenses")
fun licenses() : ModelAndView { fun licenses() : ModelAndView {

View File

@ -61,7 +61,7 @@ class UserController {
return vm return vm
} }
@GetMapping("login") @GetMapping("login","login")
fun userLogin(httpServletRequest: HttpServletRequest): ModelAndView { fun userLogin(httpServletRequest: HttpServletRequest): ModelAndView {
logService.log("onJoin") logService.log("onJoin")
val vm = ModelAndView("content/user/login") val vm = ModelAndView("content/user/login")

View File

@ -17,6 +17,7 @@ import org.springframework.security.core.userdetails.UserDetailsService
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.Mono import reactor.core.publisher.Mono
import java.time.Duration import java.time.Duration
@ -50,7 +51,7 @@ class Post {
@Repository @Repository
interface PostRepository : ReactiveMongoRepository<Post, String> { interface PostRepository : ReactiveMongoRepository<Post, String> {
fun findAllByModifyTime(time : Long? = 0): Flux<Post>
} }
@ -64,9 +65,10 @@ class PostManageg {
@Autowired @Autowired
private lateinit var bCryptPasswordEncoder: PasswordEncoder private lateinit var bCryptPasswordEncoder: PasswordEncoder
fun getPost(id : String) : Mono<Post> = postRepository.findById(id)
fun find20() : List<Post> { fun find20() : List<Post> {
return postRepository.findAll().takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf() return postRepository.findAllByModifyTime(0).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
} }
fun save(post: Post): Mono<Post> { fun save(post: Post): Mono<Post> {

View File

@ -9,6 +9,12 @@ open class ResponceResult {
var resultMsg: String? = null var resultMsg: String? = null
} }
@Getter
open class LoginResult : ResponceResult() {
var token: String? = null
}
@Getter @Getter
class FileSaveResult : ResponceResult() { class FileSaveResult : ResponceResult() {
var fileName : String? = null var fileName : String? = null

View File

@ -0,0 +1,22 @@
package kr.lunaticbum.back.lun.model
import lombok.AllArgsConstructor
import lombok.Data
import lombok.NoArgsConstructor
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.Indexed
import org.springframework.data.mongodb.core.mapping.Document
import java.util.*
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "TokenData")
class TokenData {
@Indexed(expireAfterSeconds = 60 * 5)
var expireAt: Date? = null
@Id
var tokenKey : String? = null
var refreshToken : String? = null
}

View File

@ -2,6 +2,9 @@ package kr.lunaticbum.back.lun.model
import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.LogService
import lombok.* import lombok.*
import org.bson.BsonType
import org.bson.codecs.pojo.annotations.BsonId
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.annotation.CreatedDate import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id import org.springframework.data.annotation.Id
@ -22,6 +25,11 @@ import java.time.Duration
@AllArgsConstructor @AllArgsConstructor
@Document(collection = "User") @Document(collection = "User")
class User { class User {
@BsonId
@BsonRepresentation(BsonType.OBJECT_ID)
var userId: String? = null
@Id @Id
var user_id: String? = null var user_id: String? = null
var user_pw: String? = null var user_pw: String? = null

View File

@ -123,3 +123,38 @@ a.btn_layerClose:hover {
background-color: #1f326a; background-color: #1f326a;
color: #fff; color: #fff;
} }
.post_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(300px, auto));
width: 100%;
}
.post_item {
justify-content: space-between;
flex-wrap: wrap;
flex-direction: row;
width: 100%;
border-radius: 10px;
background: #F0F0F524;
}
#postId {
display: none;
}
#writeDate {
text-align: right;
}
.post_attr {
display: block;
padding: 5px;
}
#content{
overflow: hidden;
overflow-y: hidden;
overflow-x: hidden;
}

View File

@ -3,13 +3,23 @@ var currentLat = 0.0
var currentLon = 0.0 var currentLon = 0.0
let baseData = { let baseData = {
'id' : "",
'title': "", 'title': "",
'content': "", 'content': "",
'firstPostLat': "", 'firstPostLat': 0.0,
'firstPostLon': "", 'firstPostLon': 0.0,
'category' : "none", 'category' : "none",
'hashTags' : "#none", 'hashTags' : "#none",
'modifyLat' : 0.0,
'modifyLon' : 0.0,
'originId' : "",
'writeTime' : 0,
} }
function goToEditor(path,id,sk) {
location.href = path + id+"?token="+sk;
}
function onclickWrite(type, keyword, html) { function onclickWrite(type, keyword, html) {
let title_field = document.getElementById('title_field') let title_field = document.getElementById('title_field')
var hasValues = true var hasValues = true
@ -38,7 +48,17 @@ function getLocation() {
function showPosition(position) { function showPosition(position) {
currentLat = position.coords.latitude currentLat = position.coords.latitude
currentLon = position.coords.longitude currentLon = position.coords.longitude
baseData.firstPostLat = encodeURIComponent(currentLat) if(baseData.firstPostLat !== 0.0) {
baseData.firstPostLon = encodeURIComponent(currentLon) baseData.modifyLat = encodeURIComponent(currentLat)
} else {
baseData.firstPostLat = encodeURIComponent(currentLat)
}
if(baseData.firstPostLon !== 0.0 ) {
baseData.modifyLon = encodeURIComponent(currentLon)
} else {
baseData.firstPostLon = encodeURIComponent(currentLon)
}
document.getElementById('location_field').value = "Lat: " + position.coords.latitude + ", Lon: " + position.coords.longitude; document.getElementById('location_field').value = "Lat: " + position.coords.latitude + ", Lon: " + position.coords.longitude;
} }

View File

@ -11,12 +11,17 @@
<link th:href="@{/css/toast-ui.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui.css}" rel="stylesheet" />
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<!-- <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor-dark.css" />--> <!-- <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor-dark.css" />-->
<script th:inline="javascript"> <script th:inline="javascript" >
var currentTitle = [[${defaultTitle}]]
var editor var editor
let onChange = () => {console.log(editor.getMarkdown())}
document.addEventListener("DOMContentLoaded", onLoaded); document.addEventListener("DOMContentLoaded", onLoaded);
function onLoaded() { function onLoaded() {
baseData.id = [[${srcPost.id}]];
baseData.title = [[${srcPost.title}]];
baseData.content = [[${srcPost.content}]];
baseData.firstPostLat = [[${srcPost.firstPostLat}]];
baseData.firstPostLon = [[${srcPost.firstPostLon}]];
baseData.writeTime = [[${srcPost.writeTime}]];
getLocation() getLocation()
var style = getComputedStyle(document.body) var style = getComputedStyle(document.body)
console.log(style.getPropertyValue('--ContentVerticalMargin')) console.log(style.getPropertyValue('--ContentVerticalMargin'))

View File

@ -4,16 +4,52 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script>
<link th:href="@{/css/blog.css}" rel="stylesheet" />
<script type="text/javascript" th:src="@{/js/toast-ui-view.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
let editor
document.addEventListener("DOMContentLoaded", onLoaded);
function onLoaded() {
var els = document.getElementsByClassName('content')
for (i=0;i<els.length;i++) {
var item = $(els[i])
console.log(item[0])
console.log(item.attr("data"))
new toastui.Editor({
el: item[0],
width : (item[0].getBoundingClientRect().width * 0.8) + 'px',
height : (item[0].getBoundingClientRect().width * 0.8) + 'px',
viewer: true,
usageStatistics : false,
initialValue: item.attr("data"),
theme:"dark",
});
}
}
</script>
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">
<table id="main_layer"> <div id="main_layer">
<tr id="where" th:each="location : ${locations}"> <th:block th:if="${PERMISSION != 'OK'}">
<tr id="posts" th:each="post : ${posts}"> <h1>권한이 없는 뎁쇼?!</h1>
<td class="post_item"><span th:text="${#dates.format(post.writeTime, 'dd/MMM/yyyy HH:mm')}"></span></td> </th:block>
<td class="post_item"><span th:text="${post.title}"></span></td> <th:block th:if="${PERMISSION == 'OK'}">
<td class="post_item"><span th:text="${post.id}"></span></td> <div class="post_layer">
</tr> <th:block class="posts_layer" id="posts" th:each="posts ,postsStat: ${chunkedPosts}">
</table> <th:block class="posts_layer" th:class="${#strings.append(rowKey,postsStat.index)}" th:each="post, postStast : ${posts}">
<div class="post_item" th:onclick="goToEditor([[${path}]],[[${post.id}]],[[${SK}]])" >
<span id="postTitle" class="post_attr" th:text="${post.title}"></span>
<div id="content" class="post_attr content" th:attr="data=${post.content}"></div>
<span id="writeDate" class="post_attr" th:text="${#dates.format(post.writeTime, 'dd/MMM/yyyy HH:mm')}"></span>
<span id="postId" class="post_attr" th:text="${post.id}"></span>
</div>
</th:block>
</th:block>
</div>
</th:block>
</div>
</th:block> </th:block>
</html> </html>

View File

@ -5,9 +5,7 @@
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
<script type="text/javascript" th:src="@{/js/toast-ui-view.js}"></script> <script type="text/javascript" th:src="@{/js/toast-ui-view.js}"></script>
<link th:href="@{/css/toast-ui.css}" rel="stylesheet" />
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<!-- <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor-dark.css" />--> <!-- <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor-dark.css" />-->
<script th:inline="javascript"> <script th:inline="javascript">
@ -15,7 +13,7 @@
let onChange = () => {console.log(editor.getMarkdown())} let onChange = () => {console.log(editor.getMarkdown())}
document.addEventListener("DOMContentLoaded", onLoaded); document.addEventListener("DOMContentLoaded", onLoaded);
function onLoaded() { function onLoaded() {
var h = document.querySelector('#content').getBoundingClientRect().height + 'px' var h = document.querySelector('#main_layer').getBoundingClientRect().height + 'px'
editor = new toastui.Editor({ editor = new toastui.Editor({
el: document.querySelector('#editor'), el: document.querySelector('#editor'),
height: '500px', height: '500px',