...
This commit is contained in:
parent
97236619f0
commit
2175abbc80
@ -56,6 +56,8 @@ dependencies {
|
|||||||
implementation ("org.seleniumhq.selenium:selenium-java:4.10.0")
|
implementation ("org.seleniumhq.selenium:selenium-java:4.10.0")
|
||||||
|
|
||||||
implementation ("org.commonmark:commonmark:0.18.0")
|
implementation ("org.commonmark:commonmark:0.18.0")
|
||||||
|
implementation ("net.coobird:thumbnailator:0.4.14")
|
||||||
|
|
||||||
|
|
||||||
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")
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package kr.lunaticbum.back.lun.configs
|
package kr.lunaticbum.back.lun.configs
|
||||||
|
|
||||||
import io.qdrant.client.QdrantClient
|
|
||||||
import org.springframework.ai.ollama.api.OllamaApi
|
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.http.CacheControl
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -17,12 +17,15 @@ class AppConfig : WebMvcConfigurer {
|
|||||||
@Value("\${resource.location}")
|
@Value("\${resource.location}")
|
||||||
private val resourceLocation: String? = null
|
private val resourceLocation: String? = null
|
||||||
|
|
||||||
|
val cacheControl: CacheControl = CacheControl.maxAge(Duration.ofDays(365))
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun authInterceptor(): BumsInterceptor {
|
fun authInterceptor(): BumsInterceptor {
|
||||||
return BumsInterceptor()
|
return BumsInterceptor()
|
||||||
}
|
}
|
||||||
override fun addResourceHandlers(registry: org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry) {
|
override fun addResourceHandlers(registry: org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry) {
|
||||||
registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation)
|
|
||||||
|
registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation).setCacheControl(cacheControl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addInterceptors(registry: InterceptorRegistry) {
|
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import com.google.gson.Gson
|
|||||||
import jakarta.servlet.http.Cookie
|
import jakarta.servlet.http.Cookie
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
import jakarta.servlet.http.HttpServletResponse
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.ApiKeyWordKey
|
||||||
|
import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncType11
|
||||||
|
import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncTypeKey
|
||||||
import kr.lunaticbum.back.lun.model.UserManager
|
import kr.lunaticbum.back.lun.model.UserManager
|
||||||
import kr.lunaticbum.back.lun.service.JwtService
|
import kr.lunaticbum.back.lun.service.JwtService
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
@ -98,6 +101,8 @@ class BumsInterceptor : HandlerInterceptor {
|
|||||||
(it.getAttribute(WRITE_PERMISSION_KEY) as? Boolean)?.let { permission ->
|
(it.getAttribute(WRITE_PERMISSION_KEY) as? Boolean)?.let { permission ->
|
||||||
if (permission) {
|
if (permission) {
|
||||||
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK")
|
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK")
|
||||||
|
modelAndView?.modelMap?.put(EncTypeKey, EncType11)
|
||||||
|
modelAndView?.modelMap?.put(ApiKeyWordKey,"Def")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import kr.lunaticbum.back.lun.model.*
|
|||||||
import kr.lunaticbum.back.lun.service.JwtService
|
import kr.lunaticbum.back.lun.service.JwtService
|
||||||
import kr.lunaticbum.back.lun.utils.LogService
|
import kr.lunaticbum.back.lun.utils.LogService
|
||||||
import kr.lunaticbum.back.lun.utils.getFileExtension
|
import kr.lunaticbum.back.lun.utils.getFileExtension
|
||||||
|
import net.coobird.thumbnailator.Thumbnails
|
||||||
import org.commonmark.node.Node
|
import org.commonmark.node.Node
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
import org.commonmark.renderer.html.HtmlRenderer
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
@ -25,13 +26,11 @@ import org.springframework.beans.factory.annotation.Autowired
|
|||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.core.io.Resource
|
import org.springframework.core.io.Resource
|
||||||
import org.springframework.core.io.UrlResource
|
import org.springframework.core.io.UrlResource
|
||||||
import org.springframework.data.domain.Pageable
|
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
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.kotlin.core.publisher.toMono
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -219,16 +218,54 @@ class BlogController() {
|
|||||||
val firstImg: Element? = doc.select("img")?.first()
|
val firstImg: Element? = doc.select("img")?.first()
|
||||||
val imgSrc: String = firstImg?.attr("src") ?: ""
|
val imgSrc: String = firstImg?.attr("src") ?: ""
|
||||||
it.image = imgSrc
|
it.image = imgSrc
|
||||||
|
it.thumb = imgSrc.replaceBeforeLast(".", "_thumbnail.")
|
||||||
|
generateThumbnail(imgSrc.split("/").last(), 200)
|
||||||
it.html = doc.text()
|
it.html = doc.text()
|
||||||
}
|
}
|
||||||
it.title = if ((it.title?.length ?: 0) >= 1) it.title else ""
|
it.title = if ((it.title?.length ?: 0) >= 1) it.title else ""
|
||||||
}
|
}
|
||||||
}.chunked(2))
|
})
|
||||||
}catch (ex: Exception){ex.printStackTrace()}
|
}catch (ex: Exception){ex.printStackTrace()}
|
||||||
|
|
||||||
return vm
|
return vm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun generateThumbnail(originalPath: String, targetWidth: Int) {
|
||||||
|
try {
|
||||||
|
val originalFile = File("$uploadPath${File.separator}$originalPath")
|
||||||
|
|
||||||
|
// 썸네일 경로 생성 (예: /upload/uuid.jpg → /upload/uuid_thumbnail.jpg)
|
||||||
|
val thumbnailPath = originalFile.path
|
||||||
|
.replaceBeforeLast(".", "_thumbnail.")
|
||||||
|
|
||||||
|
|
||||||
|
val thumbnailFile = File(thumbnailPath)
|
||||||
|
// 썸네일 이미 존재하면 종료
|
||||||
|
if (thumbnailFile.exists()) {
|
||||||
|
println("썸네일 이미 존재: $thumbnailPath")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 원본 파일 존재 확인
|
||||||
|
if (!originalFile.exists()) {
|
||||||
|
println("원본 파일 없음: $originalPath")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 썸네일 생성 (가로 기준 비율 유지)
|
||||||
|
Thumbnails.of(originalFile)
|
||||||
|
.width(targetWidth)
|
||||||
|
.keepAspectRatio(true)
|
||||||
|
.toFile(thumbnailPath)
|
||||||
|
|
||||||
|
println("썸네일 생성 완료: $thumbnailPath")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
println("썸네일 생성 실패: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("recent")
|
@GetMapping("recent")
|
||||||
fun recent() : ResultMV{
|
fun recent() : ResultMV{
|
||||||
val vm = ResultMV("content/blog/viewer")
|
val vm = ResultMV("content/blog/viewer")
|
||||||
@ -273,78 +310,73 @@ class BlogController() {
|
|||||||
@PostMapping("post/imageUpload")
|
@PostMapping("post/imageUpload")
|
||||||
fun postImage(@RequestPart("file") upload: MultipartFile, res: HttpServletResponse, req: HttpServletRequest): ResponseEntity<FileSaveResult> {
|
fun postImage(@RequestPart("file") upload: MultipartFile, res: HttpServletResponse, req: HttpServletRequest): ResponseEntity<FileSaveResult> {
|
||||||
var lResultCode = 0
|
var lResultCode = 0
|
||||||
var lResultMsg = "Suscces"
|
var lResultMsg = "Success"
|
||||||
var out: OutputStream? = null
|
var out: FileOutputStream? = null
|
||||||
var printWriter: PrintWriter? = null
|
|
||||||
var targetFile: File? = null
|
var targetFile: File? = null
|
||||||
logService.log("imgUploadPath ${upload.originalFilename}")
|
|
||||||
res.characterEncoding = "utf-8"
|
val uuid = UUID.randomUUID()
|
||||||
res.contentType = "text/html;charset=utf-8"
|
|
||||||
var uuid = UUID.randomUUID()
|
|
||||||
val extension: String = getFileExtension(upload.originalFilename) ?: ""
|
val extension: String = getFileExtension(upload.originalFilename) ?: ""
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
logService.log("imgUploadPath ${uuid.toString()}")
|
|
||||||
|
|
||||||
val bytes = upload.bytes
|
val bytes = upload.bytes
|
||||||
|
|
||||||
var f = File(uploadPath)
|
val f = File(uploadPath)
|
||||||
// logService.log("imgUploadPath ${f.parentFile.parentFile.parentFile.parentFile.absoluteFile}")
|
if (!f.exists()) f.mkdirs()
|
||||||
// logService.log("imgUploadPath ${f.parentFile.parentFile.parentFile.absoluteFile}")
|
|
||||||
// logService.log("imgUploadPath ${f.parentFile.parentFile.absoluteFile}")
|
|
||||||
// logService.log("imgUploadPath ${f.parentFile.absoluteFile}")
|
|
||||||
logService.log("imgUploadPath ${f.exists()}")
|
|
||||||
logService.log("imgUploadPath ${f.absolutePath}")
|
|
||||||
if (f.exists() == false) f.mkdirs()
|
|
||||||
// 실제 이미지 저장 경로
|
|
||||||
val imgUploadPath = (uploadPath + File.separator + uuid).toString() + "." + extension
|
|
||||||
logService.log("imgUploadPath $imgUploadPath")
|
|
||||||
targetFile = File(imgUploadPath)
|
|
||||||
if(targetFile.parentFile.exists() == false)targetFile.parentFile.mkdirs()
|
|
||||||
|
|
||||||
// 이미지 저장
|
// 원본 이미지 저장 경로
|
||||||
out = FileOutputStream(imgUploadPath)
|
val originalImagePath = "$uploadPath${File.separator}$uuid.$extension"
|
||||||
|
logService.log("Original image path: $originalImagePath")
|
||||||
|
|
||||||
|
// 썸네일 저장 경로
|
||||||
|
val thumbnailPath = "$uploadPath${File.separator}${uuid}_thumbnail.$extension"
|
||||||
|
logService.log("Thumbnail path: $thumbnailPath")
|
||||||
|
|
||||||
|
targetFile = File(originalImagePath)
|
||||||
|
if (!targetFile.parentFile.exists()) targetFile.parentFile.mkdirs()
|
||||||
|
|
||||||
|
// 원본 이미지 저장
|
||||||
|
out = FileOutputStream(originalImagePath)
|
||||||
out.write(bytes)
|
out.write(bytes)
|
||||||
out.flush()
|
out.flush()
|
||||||
|
|
||||||
// ckEditor 로 전송
|
// 썸네일 생성 및 저장
|
||||||
// printWriter = res.writer
|
Thumbnails.of(originalImagePath)
|
||||||
// val callback = req.getParameter("CKEditorFuncNum")
|
.width(200) // 가로 크기를 설정
|
||||||
// val fileUrl = "/blog/post/image/$uuid.$extension"
|
.keepAspectRatio(true)
|
||||||
|
.toFile(thumbnailPath)
|
||||||
|
|
||||||
// printWriter.println(
|
logService.log("Original image saved: ${File(originalImagePath).exists()}")
|
||||||
// ("<script type='text/javascript'>"
|
logService.log("Thumbnail saved: ${File(thumbnailPath).exists()}")
|
||||||
// + "window.parent.CKEDITOR.tools.callFunction("
|
|
||||||
// + callback + ",'" + fileUrl + "','이미지를 업로드하였습니다.')"
|
// 메타데이터 읽기 (원본 이미지에서)
|
||||||
// + "</script>")
|
val metadata: Metadata? = ImageMetadataReader.readMetadata(File(originalImagePath))
|
||||||
// )
|
|
||||||
//
|
|
||||||
// printWriter.flush()
|
|
||||||
logService.log("imgUploadPath $imgUploadPath")
|
|
||||||
logService.log("imgUploadPath ${File(imgUploadPath).exists()}")
|
|
||||||
val metadata: Metadata? = ImageMetadataReader.readMetadata(File(imgUploadPath))
|
|
||||||
metadata?.let {
|
metadata?.let {
|
||||||
it.directories?.forEach { directory ->
|
it.directories?.forEach { directory ->
|
||||||
logService.log(directory.name)
|
logService.log(directory.name)
|
||||||
logService.log(directory.tags.map { tag -> logService.log("tag.tagName >>> ${tag.tagName} || tag.description ${tag.description}")}.joinToString(" \n"))
|
logService.log(directory.tags.map { tag ->
|
||||||
|
logService.log("tag.tagName >>> ${tag.tagName} || tag.description ${tag.description}")
|
||||||
|
}.joinToString(" \n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
lResultCode = 1
|
||||||
|
lResultMsg = "Error: ${e.message}"
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
out?.close()
|
out?.close()
|
||||||
printWriter?.close()
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(FileSaveResult().apply {
|
|
||||||
|
|
||||||
|
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(FileSaveResult().apply {
|
||||||
this.resultCode = lResultCode
|
this.resultCode = lResultCode
|
||||||
this.resultMsg = lResultMsg
|
this.resultMsg = lResultMsg
|
||||||
this.fileName = "$uuid.$extension"
|
this.fileName = "$uuid.$extension"
|
||||||
|
this.thumbnailName = "${uuid}_thumbnail.$extension"
|
||||||
})
|
})
|
||||||
return responce
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -36,6 +36,7 @@ class Post {
|
|||||||
|
|
||||||
var html : String? = null
|
var html : String? = null
|
||||||
var image : String? = null
|
var image : String? = null
|
||||||
|
var thumb : String? = null
|
||||||
|
|
||||||
var writer : String? = null
|
var writer : String? = null
|
||||||
var writeTime : Long = 0
|
var writeTime : Long = 0
|
||||||
|
|||||||
@ -18,4 +18,5 @@ open class LoginResult : ResponceResult() {
|
|||||||
@Getter
|
@Getter
|
||||||
class FileSaveResult : ResponceResult() {
|
class FileSaveResult : ResponceResult() {
|
||||||
var fileName : String? = null
|
var fileName : String? = null
|
||||||
|
var thumbnailName : String? = null
|
||||||
}
|
}
|
||||||
@ -3385,3 +3385,58 @@ button.small,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Login Popup*/
|
||||||
|
.login_overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_popup {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_form h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_form input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.login_form button {*/
|
||||||
|
/* width: 100%;*/
|
||||||
|
/* padding: 10px;*/
|
||||||
|
/* background-color: #4CAF50;*/
|
||||||
|
/* color: white;*/
|
||||||
|
/* border: none;*/
|
||||||
|
/* cursor: pointer;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
.login_close {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
@ -48,9 +48,9 @@
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: #00000044;
|
background: #00000044;
|
||||||
}
|
}
|
||||||
#title_field {
|
/*#title_field {*/
|
||||||
font-size: 20px;
|
/*font-size: 20px;*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
.pop_layer .pop_container {
|
.pop_layer .pop_container {
|
||||||
padding: 20px 25px;
|
padding: 20px 25px;
|
||||||
|
|||||||
@ -16,8 +16,8 @@ let baseData = {
|
|||||||
'writeTime' : 0,
|
'writeTime' : 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToEditor(path,id,sk) {
|
function goToEditor(id) {
|
||||||
location.href = path + id+"?token="+sk;
|
location.href = getMainPath() + '/blog/editor/' + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const loginForm = document.getElementById('loginFormElement');
|
||||||
|
loginForm.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault(); // 기본 폼 제출 동작 방지
|
||||||
|
submitLoginForm();
|
||||||
|
});
|
||||||
|
});
|
||||||
onload = function() {
|
onload = function() {
|
||||||
history.replaceState({}, null, location.pathname);
|
history.replaceState({}, null, location.pathname);
|
||||||
// var accToken = get_cookie("access")
|
// var accToken = get_cookie("access")
|
||||||
@ -8,6 +14,31 @@ onload = function() {
|
|||||||
document.cookie = "access=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
document.cookie = "access=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
||||||
document.cookie = "refresh=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
document.cookie = "refresh=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
||||||
document.cookie = "CLEAR=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
document.cookie = "CLEAR=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
||||||
|
|
||||||
|
var currentList = [{"page":["posts"],"id":"menu_posts"},{"page":["licenses"],"id":"menu_drop"},{"page":["licenses"],"id":"menu_drop"}]
|
||||||
|
if(location.pathname.length > 1) {
|
||||||
|
// 1. 모든 'current' 클래스를 가진 요소를 선택하고 제거
|
||||||
|
// const currentElements = document.querySelectorAll('.current');
|
||||||
|
// currentElements.forEach(element => {
|
||||||
|
// element.classList.remove('current');
|
||||||
|
// });
|
||||||
|
currentList.forEach(element => {
|
||||||
|
element.page.forEach((page, index) => {
|
||||||
|
console.log(location.pathname);
|
||||||
|
if (location.pathname.includes(page)) {
|
||||||
|
const targetElement = document.getElementById(element.id);
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.classList.add('current');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const targetElement = document.getElementById('menu_home');
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.classList.add('current');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// onbeforeunload = function () {
|
// onbeforeunload = function () {
|
||||||
// var accToken = get_cookie("access")
|
// var accToken = get_cookie("access")
|
||||||
@ -261,3 +292,74 @@ function layer_popup(el){
|
|||||||
function urldecode(t){
|
function urldecode(t){
|
||||||
return decodeURI(t)
|
return decodeURI(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openLoginPopup(formType) {
|
||||||
|
|
||||||
|
document.getElementById('overlay').style.display = 'block';
|
||||||
|
document.getElementById('loginForm').style.display = formType === 'login' ? 'block' : 'none';
|
||||||
|
document.getElementById('signupForm').style.display = formType === 'signup' ? 'block' : 'none';
|
||||||
|
|
||||||
|
if(formType === 'login') {
|
||||||
|
const loginIdInput = document.getElementById('loginId');
|
||||||
|
loginIdInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopup() {
|
||||||
|
document.getElementById('overlay').style.display = 'none';
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// function submitForm(formType) {
|
||||||
|
// alert(formType === 'login' ? '로그인 시도' : '회원가입 시도');
|
||||||
|
// closePopup();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
function submitLoginForm() {
|
||||||
|
// const id = document.getElementById('loginId').value;
|
||||||
|
// const password = document.getElementById('loginPassword').value;
|
||||||
|
let user_id = document.getElementById('loginId')
|
||||||
|
let user_pw = document.getElementById('loginPassword')
|
||||||
|
let data = {
|
||||||
|
'user_id': user_id.value,
|
||||||
|
'user_pw': user_pw.value,
|
||||||
|
}
|
||||||
|
postLogin(getMainPath()+"/user/login.ajax",user_pw.data,JSON.stringify(data),user_pw.data, function (data) {
|
||||||
|
closePopup()
|
||||||
|
if (data.isOk) {
|
||||||
|
document.cookie = "access=" + data.token.split(";")[0]+";"
|
||||||
|
window.sessionStorage.setItem("REFRESH",data.refresh.split(";")[0])
|
||||||
|
document.location.replace(location.href)
|
||||||
|
} else {
|
||||||
|
if (data.resultCode === 7100) {
|
||||||
|
if(confirm(`너 누구임 정보 없는데?!\n${data.resultMsg}[${data.resultCode}]\n가입 할래!?`)){
|
||||||
|
document.location.replace(getMainPath() + "/user/join")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert(`너 누구임?!\n${data.resultMsg}[${data.resultCode}]`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// AJAX 요청
|
||||||
|
// fetch('/login', {
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// },
|
||||||
|
// body: JSON.stringify({ id, password }),
|
||||||
|
// })
|
||||||
|
// .then(response => response.json())
|
||||||
|
// .then(data => {
|
||||||
|
// if (data.success) {
|
||||||
|
// alert('로그인 성공!');
|
||||||
|
// // 로그인 성공 후 처리 (예: 페이지 리다이렉트)
|
||||||
|
// } else {
|
||||||
|
// alert('로그인 실패: ' + data.message);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch(error => {
|
||||||
|
// console.error('Error:', error);
|
||||||
|
// alert('로그인 중 오류가 발생했습니다.');
|
||||||
|
// });
|
||||||
|
}
|
||||||
@ -23,30 +23,19 @@
|
|||||||
baseData.firstPostLon = [[${srcPost.firstPostLon}]];
|
baseData.firstPostLon = [[${srcPost.firstPostLon}]];
|
||||||
baseData.writeTime = [[${srcPost.writeTime}]];
|
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'))
|
||||||
console.log(window.c)
|
// console.log(window.c)
|
||||||
console.log(style.getPropertyValue('--FooterHeight'))
|
// console.log(style.getPropertyValue('--FooterHeight'))
|
||||||
console.log(style.getPropertyValue('--TopHeight'))
|
// console.log(style.getPropertyValue('--TopHeight'))
|
||||||
document.querySelector("#title_field").value = baseData.title
|
|
||||||
var editorHeght = (
|
|
||||||
document.querySelector('#main_layer').getBoundingClientRect().height -
|
|
||||||
(
|
|
||||||
Number(style.getPropertyValue('--ButtonHegit').replace("px","") * 3)
|
|
||||||
+ Number(style.getPropertyValue('--TopHeight').replace("px",""))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
editor = new toastui.Editor({
|
editor = new toastui.Editor({
|
||||||
el: document.querySelector('#editor'),
|
el: document.querySelector('#editor'),
|
||||||
previewStyle: 'tab',
|
previewStyle: 'tab',
|
||||||
height: editorHeght + 'px',
|
height: '900px',
|
||||||
width:'95%',
|
width:'95%',
|
||||||
theme:'dark',
|
|
||||||
usageStatistics : false,
|
usageStatistics : false,
|
||||||
toolbar:null,
|
toolbar:null,
|
||||||
initialValue:baseData.content,
|
initialValue:baseData.content,
|
||||||
theme:"dark",
|
|
||||||
initialEditType:"wysiwyg",
|
initialEditType:"wysiwyg",
|
||||||
hooks: {
|
hooks: {
|
||||||
addImageBlobHook: (blob, callback) => {
|
addImageBlobHook: (blob, callback) => {
|
||||||
@ -84,24 +73,32 @@
|
|||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="content" id="content">
|
<th:block layout:fragment="content" id="content">
|
||||||
<div id="main_layer">
|
|
||||||
|
<section class="wrapper style2" >
|
||||||
|
<th:block th:if="${PERMISSION == 'OK'}">
|
||||||
|
<div class="container" >
|
||||||
|
<header class="major">
|
||||||
|
<h3><label for="title_field"><input id="title_field" th:value="${srcPost.title}"></label></h3>
|
||||||
|
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="wrapper style2">
|
||||||
<th:block th:if="${PERMISSION != 'OK'}">
|
<th:block th:if="${PERMISSION != 'OK'}">
|
||||||
<h1>권한이 없는 뎁쇼?!</h1>
|
<h1>권한이 없는 뎁쇼?!</h1>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block th:if="${PERMISSION == 'OK'}">
|
<th:block th:if="${PERMISSION == 'OK'}">
|
||||||
|
|
||||||
<label for="title_field"></label><input id="title_field" class="write_option">
|
|
||||||
|
|
||||||
<div id="editor" ></div>
|
<div id="editor" ></div>
|
||||||
<div class="write_controllbox">
|
<div class="write_controllbox">
|
||||||
|
|
||||||
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>
|
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>
|
||||||
<div class="write_option btn-example" to="#popLayer2" onclick="openPopup(this)" id="hashtag_field"></div>
|
<div class="write_option btn-example" to="#popLayer2" onclick="openPopup(this)" id="hashtag_field"></div>
|
||||||
<label for="location_field"></label><input class="write_option" readonly id="location_field"/>
|
<label for="location_field"></label><input class="write_option" readonly id="location_field"/>
|
||||||
</div>
|
</div>
|
||||||
<h1><button id="save" class="write_option" style="width: 100%; position: relative" onclick="save()">저장하셈</button></h1>
|
<h1><button id="save" class="write_option" style="width: 100%; position: relative" onclick="save()">저장하셈</button></h1>
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</section>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="popup_layer">
|
<th:block layout:fragment="popup_layer">
|
||||||
<div id="popLayer1" class="pop_layer">
|
<div id="popLayer1" class="pop_layer">
|
||||||
|
|||||||
@ -14,17 +14,6 @@
|
|||||||
let onChange = () => {console.log(editor.getMarkdown())}
|
let onChange = () => {console.log(editor.getMarkdown())}
|
||||||
document.addEventListener("DOMContentLoaded", onLoaded);
|
document.addEventListener("DOMContentLoaded", onLoaded);
|
||||||
function onLoaded() {
|
function onLoaded() {
|
||||||
// 1. 모든 'current' 클래스를 가진 요소를 선택하고 제거
|
|
||||||
const currentElements = document.querySelectorAll('.current');
|
|
||||||
currentElements.forEach(element => {
|
|
||||||
element.classList.remove('current');
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 특정 조건에 맞는 요소에 'current' 클래스 추가 (예: ID가 'targetElement'인 요소)
|
|
||||||
const targetElement = document.getElementById('menu_posts');
|
|
||||||
if (targetElement) {
|
|
||||||
targetElement.classList.add('current');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -65,30 +54,16 @@
|
|||||||
<div class="col-6 col-12-narrower imp-narrower">
|
<div class="col-6 col-12-narrower imp-narrower">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<section class="col-6 col-12-narrower imp-narrower" th:each="post : ${Posts}">
|
||||||
<h2>Two Sidebar</h2>
|
<!-- <span th:text="${cell}"></span>-->
|
||||||
<p>Yup. Two sidebars at the same time.</p>
|
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}">
|
||||||
</header>
|
<a href="#" class="image left"><img th:src="${#strings.length(post.image) > 0} ? ${post.image} : 'images/pic01.jpg'" alt="" /></a>
|
||||||
|
<div class="inner">
|
||||||
<span class="image featured"><img src="images/banner.jpg" alt="" /></span>
|
<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>Phasellus quam turpis, feugiat sit amet ornare in, hendrerit in lectus.
|
</div>
|
||||||
Praesent semper mod quis eget mi. Etiam eu ante risus. Aliquam erat volutpat.
|
</div>
|
||||||
Aliquam luctus et mattis lectus sit amet pulvinar. Nam turpis nisi
|
</section>
|
||||||
consequat etiam lorem ipsum dolor sit amet nullam.</p>
|
|
||||||
|
|
||||||
<h3>And Yet Another Subheading</h3>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas ac quam risus, at tempus
|
|
||||||
justo. Sed dictum rutrum massa eu volutpat. Quisque vitae hendrerit sem. Pellentesque lorem felis,
|
|
||||||
ultricies a bibendum id, bibendum sit amet nisl. Mauris et lorem quam. Maecenas rutrum imperdiet
|
|
||||||
rhoncus dui quis euismod. Maecenas lorem tellus, congue et condimentum ac, ullamcorper non sapien.
|
|
||||||
Donec sagittis massa et leo semper a scelerisque metus faucibus. Morbi congue mattis mi.
|
|
||||||
Phasellus sed nisl vitae risus tristique volutpat. Cras rutrum commodo luctus.</p>
|
|
||||||
|
|
||||||
<p>Phasellus odio risus, faucibus et viverra vitae, eleifend ac purus. Praesent mattis, enim
|
|
||||||
quis hendrerit porttitor, sapien tortor viverra magna, sit amet rhoncus nisl lacus nec arcu.
|
|
||||||
Maecenas tortor mauris, consectetur pellentesque dapibus eget, tincidunt vitae arcu.
|
|
||||||
Vestibulum purus augue, tincidunt sit amet iaculis id, porta eu purus.</p>
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -30,11 +30,21 @@
|
|||||||
initialValue:baseData.content,
|
initialValue:baseData.content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function loadEditor() {
|
||||||
|
goToEditor([[${srcPost.id}]]);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="content" id="content">
|
<th:block layout:fragment="content" id="content">
|
||||||
<section class="wrapper style2">
|
<section class="wrapper style2">
|
||||||
<div class="container">
|
|
||||||
|
<div class="container" th:if="${PERMISSION == 'OK'}" onclick="loadEditor()">
|
||||||
|
<header class="major">
|
||||||
|
<h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2>
|
||||||
|
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
<div class="container" th:if="${PERMISSION != 'OK'}" onclick="openLoginPopup('login')">
|
||||||
<header class="major">
|
<header class="major">
|
||||||
<h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2>
|
<h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2>
|
||||||
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
|
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||||
|
|||||||
@ -73,7 +73,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="col-4 col-12-narrower">
|
<section class="col-4 col-12-narrower">
|
||||||
<div class="box highlight">
|
<div class="box highlight" th:if="${PERMISSION == 'OK'}" onclick=gotoWrite()>
|
||||||
|
<i class="icon solid major fa-pencil-alt"></i>
|
||||||
|
<h3>글쓰기[Writing]</h3>
|
||||||
|
<p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p>
|
||||||
|
</div>
|
||||||
|
<div class="box highlight" th:if="${PERMISSION != 'OK'}" onclick="openLoginPopup('login')">
|
||||||
<i class="icon solid major fa-pencil-alt"></i>
|
<i class="icon solid major fa-pencil-alt"></i>
|
||||||
<h3>글쓰기[Writing]</h3>
|
<h3>글쓰기[Writing]</h3>
|
||||||
<p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p>
|
<p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p>
|
||||||
|
|||||||
@ -14,10 +14,8 @@
|
|||||||
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('#main_layer').getBoundingClientRect().height + 'px'
|
|
||||||
editor = new toastui.Editor({
|
editor = new toastui.Editor({
|
||||||
el: document.querySelector('#editor'),
|
el: document.querySelector('#editor'),
|
||||||
height: '500px',
|
|
||||||
width:'100%',
|
width:'100%',
|
||||||
viewer: true,
|
viewer: true,
|
||||||
usageStatistics : false,
|
usageStatistics : false,
|
||||||
@ -137,15 +135,15 @@
|
|||||||
"> - **Embedded license files**: [jcl-over-slf4j-2.0.16.jar/META-INF/LICENSE.txt](jcl-over-slf4j-2.0.16.jar/META-INF/LICENSE.txt)\n" +
|
"> - **Embedded license files**: [jcl-over-slf4j-2.0.16.jar/META-INF/LICENSE.txt](jcl-over-slf4j-2.0.16.jar/META-INF/LICENSE.txt)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\n"
|
"\n"
|
||||||
,
|
|
||||||
theme:"dark",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="content" id="content">
|
<th:block layout:fragment="content" id="content">
|
||||||
<div id="main_layer" style="background:#30303594;">
|
<section class="wrapper style1">
|
||||||
|
<div class="container">
|
||||||
<div id="editor" ></div>
|
<div id="editor" ></div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</th:block>
|
</th:block>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -9,10 +9,14 @@
|
|||||||
<!-- Nav -->
|
<!-- Nav -->
|
||||||
<nav id="nav">
|
<nav id="nav">
|
||||||
<ul>
|
<ul>
|
||||||
<li id="menu_home" class="current"><a th:href="@{/}">Home</a></li>
|
<li id="menu_home" ><a th:href="@{/}">Home</a></li>
|
||||||
<li id="menu_posts"><a href="blog/posts">Posts</a></li>
|
<li id="menu_posts"><a href="blog/posts">Posts</a></li>
|
||||||
|
|
||||||
|
<li id="menu_sec"><a href="left-sidebar">Left Sidebar</a></li>
|
||||||
|
<li id="menu_thr"><a href="right-sidebar">Right Sidebar</a></li>
|
||||||
|
<li id="menu_four"><a href="two-sidebar">Two Sidebar</a></li>
|
||||||
<li id="menu_drop">
|
<li id="menu_drop">
|
||||||
<a href="#">Dropdown</a>
|
<a href="#">About</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#">Lorem dolor</a></li>
|
<li><a href="#">Lorem dolor</a></li>
|
||||||
<li><a href="#">Magna phasellus</a></li>
|
<li><a href="#">Magna phasellus</a></li>
|
||||||
@ -27,13 +31,9 @@
|
|||||||
<li><a href="#">Veroeros feugiat</a></li>
|
<li><a href="#">Veroeros feugiat</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#">Veroeros feugiat</a></li>
|
<li><a th:href="@{/licenses}">Licenses</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu_sec"><a href="left-sidebar">Left Sidebar</a></li>
|
|
||||||
<li id="menu_thr"><a href="right-sidebar">Right Sidebar</a></li>
|
|
||||||
<li id="menu_four"><a href="two-sidebar">Two Sidebar</a></li>
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
|
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="overlay" class="login_overlay">
|
||||||
|
<div id="popup" class="login_popup">
|
||||||
|
<div id="loginForm" class="login_form">
|
||||||
|
<h2>로그인</h2>
|
||||||
|
<form id="loginFormElement" onsubmit="return false;">
|
||||||
|
<input type="text" th:data="${enc}" id="loginId" placeholder="아이디" required>
|
||||||
|
<input type="password" th:data="${type}" id="loginPassword" placeholder="비밀번호" required>
|
||||||
|
<button type="submit" class="button">로그인</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="signupForm" class="login_form">
|
||||||
|
<h2>회원가입</h2>
|
||||||
|
<input type="text" placeholder="아이디" required>
|
||||||
|
<input type="password" placeholder="비밀번호" required>
|
||||||
|
<input type="email" placeholder="이메일" required>
|
||||||
|
<button onclick="submitForm('signup')">가입하기</button>
|
||||||
|
</div>
|
||||||
|
<span class="login_close" onclick="closePopup()">×</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script th:src="@{/assets/js/jquery.min.js}"></script>
|
<script th:src="@{/assets/js/jquery.min.js}"></script>
|
||||||
<script th:src="@{/assets/js/jquery.dropotron.min.js}"></script>
|
<script th:src="@{/assets/js/jquery.dropotron.min.js}"></script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user