All checks were successful
Build And Test / build-and-push (push) Successful in 4m22s
168 lines
5.6 KiB
HTML
168 lines
5.6 KiB
HTML
{% extends "components/base.html" %}
|
|
{% block title %}Create Post{% endblock %}
|
|
|
|
{% block main_area %}
|
|
<h1 class="pt-3">Create New Post</h1>
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<form method="POST" id="post-form">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
{{ form.title.label_tag }}
|
|
{{ form.title }}
|
|
</div>
|
|
<div class="mb-3">
|
|
{{ form.summary.label_tag }}
|
|
{{ form.summary }}
|
|
</div>
|
|
<div class="mb-3">
|
|
{{ form.tags.label_tag }}
|
|
{{ form.tags }}
|
|
</div>
|
|
|
|
<!-- 마크다운 에디터 -->
|
|
<h2>Contents</h2>
|
|
<textarea id="markdown-editor" name="contents" class="form-control" rows="10"></textarea>
|
|
|
|
<!-- 버튼 -->
|
|
<div class="d-flex justify-content-end mt-4">
|
|
<button type="submit" class="btn btn-primary me-2">Create Post</button>
|
|
<a href="{% url 'blog:post_list' %}" class="btn btn-secondary">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div id="preview" class="border p-3 bg-light h-100 overflow-auto"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 마크다운 파서 및 하이라이트 -->
|
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
|
|
|
<script>
|
|
const md = window.markdownit({
|
|
highlight: function (str, lang) {
|
|
if (lang && hljs.getLanguage(lang)) {
|
|
try {
|
|
return hljs
|
|
.highlight(str, {language: lang})
|
|
.value;
|
|
} catch (__) {}
|
|
}
|
|
return '';
|
|
}
|
|
});
|
|
|
|
const textarea = document.getElementById("markdown-editor");
|
|
const preview = document.getElementById("preview");
|
|
|
|
let presignedUrlMap = new Map(); // object_name => presigned_url 저장
|
|
|
|
// 미리보기 업데이트 (textarea 내용은 그대로, 렌더링할 때만 presigned 치환)
|
|
function updatePreview() {
|
|
const markdownContent = textarea.value;
|
|
const lines = markdownContent.split("\n");
|
|
const previewLines = [...lines];
|
|
|
|
for (let i = 0; i < previewLines.length; i++) {
|
|
const match = previewLines[i].match(/!\[Image\]\((.+)\)/);
|
|
if (match) {
|
|
const objectName = match[1];
|
|
if (presignedUrlMap.has(objectName)) {
|
|
const presignedUrl = presignedUrlMap.get(objectName);
|
|
previewLines[i] = ``;
|
|
}
|
|
}
|
|
}
|
|
|
|
preview.innerHTML = md.render(previewLines.join("\n"));
|
|
|
|
document
|
|
.querySelectorAll("#preview pre code")
|
|
.forEach((block) => {
|
|
hljs.highlightElement(block);
|
|
});
|
|
}
|
|
|
|
// 이미지 붙여넣기 처리
|
|
textarea.addEventListener("paste", async function (event) {
|
|
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
|
|
|
for (const item of items) {
|
|
if (item.type.startsWith("image/")) {
|
|
const file = item.getAsFile();
|
|
if (!file)
|
|
return;
|
|
|
|
const formData = new FormData();
|
|
formData.append("image", file);
|
|
|
|
try {
|
|
const response = await fetch("/obs_minio/upload/", {
|
|
method: "POST",
|
|
body: formData
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
const fullImageUrl = data.url;
|
|
const objectName = fullImageUrl
|
|
.split("/")
|
|
.slice(-2)
|
|
.join("/");
|
|
|
|
// textarea에 object_name 삽입
|
|
const markdownImage = `\n`;
|
|
const cursorPos = textarea.selectionStart;
|
|
const textBefore = textarea
|
|
.value
|
|
.substring(0, cursorPos);
|
|
const textAfter = textarea
|
|
.value
|
|
.substring(cursorPos);
|
|
textarea.value = textBefore + markdownImage + textAfter;
|
|
|
|
textarea.focus();
|
|
|
|
// presigned URL 받아서 map에 저장
|
|
const presignedResponse = await fetch("/obs_minio/get_presigned_url/", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify({object_name: objectName})
|
|
});
|
|
|
|
if (presignedResponse.ok) {
|
|
const presignedData = await presignedResponse.json();
|
|
presignedUrlMap.set(objectName, presignedData.presigned_url);
|
|
}
|
|
|
|
updatePreview();
|
|
} else {
|
|
alert("Image upload failed. Please try again.");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error uploading image:", error);
|
|
alert("An error occurred during image upload.");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 입력할 때는 부드럽게 presigned 적용해서 렌더링
|
|
textarea.addEventListener("input", function () {
|
|
updatePreview();
|
|
});
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
updatePreview();
|
|
});
|
|
</script>
|
|
</div>
|
|
{% endblock %}
|