This commit is contained in:
lunaticbum 2025-08-11 15:55:11 +09:00
parent d0abec2f4b
commit 3a0a29ec02
15 changed files with 339 additions and 160 deletions

View File

@ -10,6 +10,7 @@ import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncTypeKey
import kr.lunaticbum.back.lun.model.UserManager
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.lang.Nullable
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
@ -47,12 +48,11 @@ class BumsInterceptor : HandlerInterceptor {
handler: Any,
@Nullable modelAndView: ModelAndView?
) {
// if(remeberMe && authResult != null) {
// rememberMeServices.loginSuccess(httpServletRequest, responce, authResult)
// }
modelAndView?.modelMap?.put(EncTypeKey, EncType11)
modelAndView?.modelMap?.put(ApiKeyWordKey,"Def")
if (modelAndView != null) {
modelAndView.modelMap.put(EncTypeKey, EncType11)
modelAndView.modelMap.put(ApiKeyWordKey, "Def")

View File

@ -3,6 +3,7 @@ package kr.lunaticbum.back.lun.configs
import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.MongoPersistentTokenRepository
import kr.lunaticbum.back.lun.model.UserManager
import kr.lunaticbum.back.lun.utils.LogService
import org.springframework.beans.factory.annotation.Autowired
@ -23,14 +24,20 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository
import org.springframework.web.ErrorResponse
import javax.sql.DataSource
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val userManager: UserManager,
private val bCryptPasswordEncoder: BCryptPasswordEncoder
private val bCryptPasswordEncoder: BCryptPasswordEncoder,
private val tokenRepository: MongoPersistentTokenRepository
) {
@Autowired
lateinit var logService: LogService
@ -42,6 +49,22 @@ class SecurityConfig(
}
}
// RememberMeServices를 Bean으로 생성하고 필드에 할당하거나, 생성자 주입을 할 수 있음
@Bean
fun rememberMeServices(): RememberMeServices {
val key = "your-remember-me-key"
return PersistentTokenBasedRememberMeServices(key, userManager,
tokenRepository as PersistentTokenRepository?
).apply {
setParameter("remember-me") // 기본 파라미터명
setTokenValiditySeconds(86400) // 토큰 유효시간 설정
// 필요시 setAlwaysRemember(true) 등 추가 설정 가능
println("CALLED rememberMeServices")
}
}
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf { csrf ->
@ -68,8 +91,9 @@ class SecurityConfig(
.defaultSuccessUrl("/", true)
.permitAll()
}.rememberMe { rememberMe ->
rememberMe
.key("BsTs*!12@") // 보통 안전한 키 지정
rememberMe.rememberMeServices(rememberMeServices())
.key("remember-BsTs*!12@") // 보통 안전한 키 지정
.tokenRepository(tokenRepository)
.tokenValiditySeconds(60 * 60 * 24 * 7) // 7일간 유효
.userDetailsService(userManager) // 사용자 정보 서비스 지정
}.logout { logout ->
@ -86,43 +110,6 @@ class SecurityConfig(
.passwordEncoder(bCryptPasswordEncoder)
return authenticationManagerBuilder.build() // .and() 없이 직접 build() 호출
}
// @Bean
// fun filterChain(http: HttpSecurity): SecurityFilterChain {
//
// http.csrf {
// it.ignoringRequestMatchers("/user/joinUser.bjx").disable()
// }
// http.cors { it.disable() }
// http.headers {
// it.frameOptions { frameOptionsConfig ->
// frameOptionsConfig.disable()
// }
// }
//
// http.authorizeHttpRequests {
// logService.log(it.toString())
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers("/blog/viewer/**").permitAll()
// it.anyRequest().permitAll()
// }
// http.sessionManagement {
// it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// }
// .exceptionHandling { it ->
// it.authenticationEntryPoint(unauthorizedEntryPoint)
// .accessDeniedHandler(accessDeniedHandler)
// }
//// .formLogin { formLogin ->
//// formLogin
//// .loginPage("/user/join")
//////// .usernameParameter("username")
//////// .passwordParameter("password")
//// .loginProcessingUrl("/user/joinUser.api")
//// .defaultSuccessUrl("/", true)
//// }
//
// return http.build()
// }
private val unauthorizedEntryPoint =
AuthenticationEntryPoint { request: HttpServletRequest?, response: HttpServletResponse, authException: AuthenticationException? ->

View File

@ -176,7 +176,13 @@ class BlogController() {
val vm = ResultMV("content/blog/viewer")
postManager.getPost(postId).block().apply {
this?.title = URLDecoder.decode(this?.title)
this?.content = URLDecoder.decode(this?.content)
println("this?.content >>> ${this?.content}")
if (this?.content is String){
this?.content = URLDecoder.decode(this?.content)
} else {
this?.content = Gson().toJson(this?.content)
}

View File

@ -1,7 +1,11 @@
package kr.lunaticbum.back.lun.controllers
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import jakarta.servlet.http.HttpServletResponse
import kotlinx.coroutines.reactor.awaitSingle
import kr.lunaticbum.back.lun.model.PostManager
import kr.lunaticbum.back.lun.model.ResultMV
import kr.lunaticbum.back.lun.utils.LogService
@ -31,24 +35,68 @@ class Home {
@Autowired
private lateinit var postManager: PostManager
data class PostView(
val id: Long,
val title: String,
val thumb: String?,
val writeTime: Long,
val textOnly: String,
val firstImage: String?
)
data class DeltaOp(val insert: Any)
data class Delta(val ops: List<DeltaOp>)
fun extractFromDelta(deltaJson: String): Pair<String, String?> {
val delta: Delta = Gson().fromJson(deltaJson, Delta::class.java)
var textOnly = StringBuilder()
var firstImage: String? = null
delta.ops.forEach { op ->
if (op.insert is String) {
textOnly.append(op.insert)
} else if (op.insert is Map<*, *>) {
val obj = op.insert as Map<*, *>
if (obj["image"] != null && firstImage == null) {
firstImage = obj["image"].toString()
}
}
}
return textOnly.toString() to firstImage
}
@GetMapping("/","/home.bs")
fun home() : ResultMV {
suspend fun home() : ResultMV {
val vm = ResultMV("content/home")
try {
vm.modelMap.put("Posts", postManager.find4().apply {
vm.modelMap.put("Posts", postManager.find8().apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
val parser: Parser = Parser.builder().build()
val document: Node = parser.parse(it.content)
val renderer = HtmlRenderer.builder().build()
Jsoup.parse(renderer.render(document))?.let { doc ->
val firstImg: Element? = doc.select("img")?.first()
val imgSrc: String = firstImg?.attr("src") ?: ""
it.image = imgSrc
it.thumb = imgSrc.replace(imgSrc.split("/").last(), imgSrc.split("/").last().replace(".","_thumbnail."))
generateThumbnail(imgSrc.split("/").last(), 200)
it.html = doc.text()
println("content >>> ${it.content}")
try {
JsonParser.parseString(it.content)
it.content?.let { content ->
var delta = extractFromDelta(content)
val firstImg = delta.second
it.image = firstImg ?: "images/pic01.jpg"
it.thumb = firstImg ?: "images/pic01.jpg"
it.html = delta.first
}
} catch (e: Exception) {
Jsoup.parse(renderer.render(document))?.let { doc ->
val firstImg: Element? = doc.select("img")?.first()
val imgSrc: String = firstImg?.attr("src") ?: ""
it.image = imgSrc
it.thumb = imgSrc.replace(imgSrc.split("/").last(), imgSrc.split("/").last().replace(".","_thumbnail."))
generateThumbnail(imgSrc.split("/").last(), 200)
it.html = doc.text()
}
}
it.title = if ((it.title?.length ?: 0) >= 1) it.title else ""
}

View File

@ -77,7 +77,11 @@ class Telegram {
jsonString.extractModelData { exception, originDataString ->
if (exception == null) {
Gson().fromJson<ReportModel>(originDataString, ReportModel::class.java)?.let { msg ->
sendMsg("${msg.name}님이 전송\n${msg.message}\n회신가능 메일${msg.email}")
WebClient.create().get()
.uri("https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=${msg.name}님이 전송\n${msg.message}\n회신가능 메일${msg.email}")
.retrieve()
.bodyToMono(String::class.java).block() ?: "FAIL"
}
}
}

View File

@ -32,7 +32,9 @@ import javax.naming.AuthenticationException
@RestController
@RequestMapping("/user")
class UserController {
class UserController(
private val rememberMeServices: RememberMeServices
) {
@Autowired
@ -84,17 +86,17 @@ class UserController {
@ResponseBody
@PostMapping("login.bjx")
fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> {
fun login(httpServletRequest: HttpServletRequest,
httpRes: HttpServletResponse,
@RequestBody jsonString: String) : ResponseEntity<LoginResult> {
var loginResult = LoginResult().apply {
this.isOk = false
this.resultCode = 9999
this.resultMsg = "JUST INITIALIZED"
}
try {
logService.log(httpServletRequest.requestURI)
logService.log(jsonString)
var lResultCode = 0
var lResultMsg = "Suscces"
var u : UserDetails? = null
var user : User? = null
var tokenData : TokenData? = null
var remeberMe = false
var authResult : Authentication? = null
jsonString.extractModelData { exception, originDataString ->
if (exception == null) {
@ -107,6 +109,7 @@ class UserController {
// 인증 성공 시 SecurityContextHolder에 인증 정보 저장
SecurityContextHolder.getContext().authentication = authResult
// 인증 정보가 담긴 SecurityContext를 세션에 저장
val session = httpServletRequest.getSession(true)
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext())
@ -114,11 +117,11 @@ class UserController {
val principal = authResult?.principal
if (principal is UserDetails) {
val username = principal.username
val session = httpServletRequest.getSession(true)
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext())
remeberMe = target.remeberMe ?: false
println("target.remeberMe >>> ${target.rememberMe}")
loginResult.rememberMe = target.rememberMe
if (target.rememberMe == true) {
rememberMeServices.loginSuccess(httpServletRequest, httpRes, authResult)
}
} else {
lResultMsg = "is wrong infomation id or passord"
lResultCode = 7100
@ -134,11 +137,10 @@ class UserController {
}
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).headers {
}.body(LoginResult().apply {
}.body(loginResult.apply {
this.isOk = lResultCode == 0
this.resultCode = lResultCode
this.resultMsg = lResultMsg
this.token = if (remeberMe) "OK" else ""
}).apply {
}
@ -151,8 +153,6 @@ class UserController {
this.isOk = false
this.resultCode = -999
this.resultMsg = e.message ?: "unknown exception"
this.token = ""
this.refresh = ""
}).apply {

View File

@ -4,6 +4,7 @@ import kr.lunaticbum.back.lun.utils.LogService
import lombok.AllArgsConstructor
import lombok.Data
import lombok.NoArgsConstructor
import okio.Timeout
import org.bson.BsonType
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonRepresentation
@ -15,6 +16,7 @@ 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.Aggregation
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository
@ -101,6 +103,14 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
fun countByOrderByModifyTimeDesc(): Mono<Long>
fun findTop5ByOrderByReadCountDesc(): Flux<Post>
fun findTop5ByOrderByModifyTimeDesc(): Flux<Post>
@Aggregation(pipeline = [
"{ \$sort: { modifyTime: -1 } }",
"{ \$group: { _id: \"\$originId\", post: { \$first: \"\$\$ROOT\" } } }",
"{ \$sort: { \"post.modifyTime\": -1 } }",
"{ \$limit: 8 }",
"{ \$replaceRoot: { newRoot: \"\$post\" } }"
])
fun findLatestUniqueOrigin(): Flux<Post>
}
@ -159,16 +169,9 @@ class PostManager(
fun find4() : List<Post> {
val originalList = postRepository.findAllByModifyTime(0)
.takeLast(4)
.buffer(4)
.blockLast(Duration.ofSeconds(30)) ?: listOf()
return originalList + List((4 - originalList.size).coerceAtLeast(0)) {
Post() // 기본값 생성 (필드 초기화 필요)
}
// return postRepository.findAllByModifyTime(0).takeLast(4).buffer(4).blockLast(Duration.ofSeconds(30)) ?: listOf()
fun find8() : List<Post> {
return postRepository.findLatestUniqueOrigin().collectList() // Mono<List<Post>>로 변환 // Flux<Post> → Mono<List<Post>>
.block(Duration.ofSeconds(30)) ?: emptyList()
}
fun find20() : List<Post> {

View File

@ -16,8 +16,7 @@ open class PostsResult : BaseResult() {
@Getter
open class LoginResult : ResponceResult() {
var token: String? = null
var refresh: String? = null
var rememberMe: Boolean? = null
}
@Getter

View File

@ -7,7 +7,11 @@ import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.Indexed
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository
import org.springframework.stereotype.Component
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.time.LocalDateTime
import java.util.*
@ -38,4 +42,68 @@ class TokenData {
interface TokenDataRepository : ReactiveMongoRepository<TokenData, String> {
fun findBytokenKey(tokenKey: String): Mono<TokenData>
fun deleteBytokenKey(tokenKey: String)
}
}
interface PersistentTokenRepository {
fun createNewToken(token: PersistentRememberMeToken)
fun updateToken(series: String, tokenValue: String, lastUsed: Date)
fun getTokenForSeries(seriesId: String): PersistentRememberMeToken?
fun removeUserTokens(username: String)
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "persistent_logins")
data class PersistentLogin(
@Id val series: String,
val username: String,
val tokenValue: String,
val lastUsed: Date
)
interface PersistentLoginRepository : ReactiveMongoRepository<PersistentLogin, String> {
fun findByUsername(username: String): Flux<PersistentLogin>
}
@Component
class MongoPersistentTokenRepository (
private val repository: PersistentLoginRepository
) : PersistentTokenRepository {
override fun createNewToken(token: PersistentRememberMeToken) {
val persistentLogin = PersistentLogin(
series = token.series,
username = token.username,
tokenValue = token.tokenValue,
lastUsed = token.date
)
repository.save(persistentLogin).block() // 블로킹 여부는 환경에 따라 조절
println("CALLED rememberMeServices")
}
override fun updateToken(series: String, tokenValue: String, lastUsed: Date) {
val login = repository.findById(series).block()
if (login != null) {
val updated = login.copy(tokenValue = tokenValue, lastUsed = lastUsed)
repository.save(updated).block()
println("CALLED rememberMeServices")
}
}
override fun getTokenForSeries(seriesId: String): PersistentRememberMeToken? {
val login = repository.findById(seriesId).block()
return login?.let {
println("CALLED rememberMeServices")
PersistentRememberMeToken(it.username, it.series, it.tokenValue, it.lastUsed)
}
}
override fun removeUserTokens(username: String) {
val tokens = repository.findByUsername(username).collectList().block()
tokens?.let {
println("CALLED rememberMeServices")
repository.deleteAll(it).block()
}
}
}

View File

@ -43,7 +43,7 @@ class User {
var isAccept : String? = null
var isAdmin : String? = null
var remeberMe : Boolean? = false
var rememberMe : Boolean? = false
fun checkValid() : Boolean {
if (

View File

@ -3439,4 +3439,5 @@ button.small,
right: 10px;
font-size: 20px;
cursor: pointer;
}
}

View File

@ -14,90 +14,150 @@ function initEditor(useEditor) {
baseData.firstPostLat = serverData.firstPostLat;
baseData.firstPostLon = serverData.firstPostLon;
baseData.writeTime = serverData.writeTime;
baseData.originId = serverData.originId;
getLocation();
setEditorHeight();
window.addEventListener('resize', setEditorHeight);
var Font = Quill.import('formats/font');
Font.whitelist = ['sans-serif', 'serif', 'monospace', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display'];
Quill.register(Font, true);
Quill.register({ 'modules/table-better': QuillTableBetter }, true);
quill = new Quill(editorContainer, {
theme: 'snow',
modules: useEditor ? {
toolbar: {
container: [
[{ font: Font.whitelist }],
[{ 'size': ['small', false, 'large', 'huge'] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
[{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }],
['link', 'image', 'video'],
['table-better'],
[{ 'direction': 'rtl' }],
[{ 'align': [] }],
['clean']
],
handlers: {
image: function () {
selectLocalImage();
},
video: function () {
selectLocalVideo()
}
}
},
'table-better': {
language: 'en_US',
toolbarTable: true
},
keyboard: {
bindings: QuillTableBetter.keyboardBindings
}
} : {toolbar : false, toolbarTable: false},
readOnly: !useEditor
});
loadContent(serverData.content);
if (!useEditor) {
editorContainer.classList.add('readonly-mode');
} else {
editorContainer.classList.remove('readonly-mode');
}
console.log("quill", quill);
}
function isDelta(content) {
try {
// Delta는 JSON이면서 'ops'라는 키를 포함
if (typeof content === "string") {
content = JSON.parse(content);
var Font = Quill.import('formats/font');
Font.whitelist = ['sans-serif', 'serif', 'monospace', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display'];
Quill.register(Font, true);
Quill.register({ 'modules/table-better': QuillTableBetter }, true);
quill = new Quill(editorContainer, {
theme: 'snow',
modules: useEditor ? {
toolbar: {
container: [
[{ font: Font.whitelist }],
[{ 'size': ['small', false, 'large', 'huge'] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
[{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }],
['link', 'image', 'video'],
['table-better'],
[{ 'direction': 'rtl' }],
[{ 'align': [] }],
['clean']
],
handlers: {
image: function () {
selectLocalImage();
},
video: function () {
selectLocalVideo()
}
}
},
'table-better': {
language: 'en_US',
toolbarTable: true
},
keyboard: {
bindings: QuillTableBetter.keyboardBindings
}
} : {toolbar : false, toolbarTable: false},
readOnly: !useEditor
});
loadContent(serverData.content);
if (!useEditor) {
editorContainer.classList.add('readonly-mode');
} else {
editorContainer.classList.remove('readonly-mode');
}
console.log("quill", quill);
}catch (e) { }
try {
document.querySelector("#title_field").textContent = baseData.title
}catch (e) { }
try {
document.getElementById('location_field').textContent = "Lat: " + baseData.firstPostLat + ", Lon: " + baseData.firstPostLon;
var requestOptions = {
method: 'GET',
};
fetch("https://api.geoapify.com/v1/geocode/reverse?lat="+baseData.firstPostLat+"&lon="+baseData.firstPostLon+"&apiKey=2b37a75bb0754086b5a1c4a7c3173ee8", requestOptions)
.then(response => response.json())
.then(function(result) {
try {
document.getElementById('location_field').textContent = result.features[0].properties.formatted
} catch (e) {
document.getElementById('location_field').innerHTML = "Lat: " + baseData.firstPostLat + "<br> Lon: " + baseData.firstPostLon;
}
})
.catch(error => console.log('error', error));
}catch (e) { }
}
function parseDelta(content) {
try {
if (typeof content === "string") {
const obj = JSON.parse(content);
if (obj && typeof obj === "object" && Array.isArray(obj.ops)) {
return obj; // 유효한 Delta 객체
}
} else if (content && typeof content === "object" && Array.isArray(content.ops)) {
return content; // 이미 Delta 객체 형태
}
return typeof content === "object" && content.ops !== undefined;
} catch (e) {
return false; // JSON 파싱 실패하면 마크업(HTML)으로 간주
// JSON 파싱 실패 → HTML로 간주
}
return null; // Delta 아님
}
function loadEditor() {
location.href = getMainPath() + '/blog/editor/' + serverData.id;
}
function loadContent(content) {
if (isDelta(content)) {
quill.setContents(content); // Delta 데이터라면 setContents
console.log("content >>> ", content);
const delta = parseDelta(content);
if (delta) {
quill.setContents(delta);
} else {
quill.clipboard.dangerouslyPasteHTML(content); // HTML 데이터라면 dangerouslyPasteHTML
quill.clipboard.dangerouslyPasteHTML(content);
}
}
function save() {
onclickWrite(serverData.enc ,serverData.keyword,quill.getContents())
onclickWrite(serverData.enc ,serverData.keyword,JSON.stringify(quill.getContents()))
}
function parseDeltaContent(deltaString) {
let textContent = "";
let mediaLinks = [];
try {
const delta = JSON.parse(deltaString); // Delta 문자열 → 객체
if (delta && Array.isArray(delta.ops)) {
delta.ops.forEach(op => {
if (op.insert) {
if (typeof op.insert === "string") {
textContent += op.insert;
} else if (typeof op.insert === "object") {
if (op.insert.image) {
mediaLinks.push(op.insert.image);
}
if (op.insert.video) {
mediaLinks.push(op.insert.video);
}
}
}
});
}
} catch (e) {
console.error("Delta JSON parse error:", e);
}
return { text: textContent.trim(), media: mediaLinks };
}
function selectLocalImage() {
// 이미지 URL 입력 받기
const url = prompt("이미지 URL을 입력하거나 빈칸으로 두시면 파일 업로드를 합니다.");
@ -237,8 +297,8 @@ function onclickWrite(type, keyword, html) {
if (hasValues) {
baseData.title = encodeURIComponent(title_field.value)
baseData.content = encodeURIComponent(html)
baseData.firstPostLat = encodeURIComponent(currentLat)
baseData.firstPostLon = encodeURIComponent(currentLon)
baseData.modifyLat = encodeURIComponent(currentLat)
baseData.modifyLon = encodeURIComponent(currentLon)
}
let uploadUrl = getMainPath() + "/blog/post.bjx";
if(confirm(JSON.stringify(baseData) + "\n해당 내용으로\n유저 등록 하실??")) {

View File

@ -419,16 +419,16 @@ function submitLoginForm() {
let user_id = document.getElementById('loginId')
let user_pw = document.getElementById('loginPassword')
let rememberMe = document.getElementById('rememberMe')
console.log(rememberMe.value)
let data = {
'user_id': user_id.value,
'user_pw': user_pw.value,
'rememberMe' : rememberMe.value,
'rememberMe' : rememberMe.value === "on",
}
postLogin(getMainPath()+"/user/login.bjx",user_pw.data,JSON.stringify(data),user_pw.data, function (data) {
closePopup()
alert("data >> " + data)
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) {

View File

@ -27,10 +27,11 @@
<th:block layout:fragment="content" id="content">
<section class="wrapper style2" >
<th:block th:if="${PERMISSION == 'OK'}">
<th:block sec:authorize="isAuthenticated()">
<div class="container" >
<header class="major">
<h3><label for="title_field"><input id="title_field" th:value="${srcPost.title}"></label></h3>
<h3><label for="title_field"><input id="title_field" th:value="${srcPost.title}" style="width:100%; max-width:100%; box-sizing:border-box;
border:none; outline:none; text-align:center;"></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>
@ -42,6 +43,7 @@
<h1>권한이 없는 뎁쇼?!</h1>
</th:block>
<th:block sec:authorize="isAuthenticated()">
<div id="editor" ></div>
<div class="write_controllbox">
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>

View File

@ -18,6 +18,7 @@
<script th:inline="javascript">
var serverData = {
id: [[${srcPost != null and srcPost.id != null} ? ${srcPost.id} : 0]],
originId: [[${srcPost != null and srcPost.originId != null} ? ${srcPost.originId} : 0]],
title: /*[[${srcPost != null and srcPost.title != null} ? ${srcPost.title} : '']]*/,
content: /*[[${srcPost != null and srcPost.content != null} ? ${srcPost.content} : '']]*/,
firstPostLat: [[${srcPost != null and srcPost.firstPostLat != null} ? ${srcPost.firstPostLat} : 0]],