Fresh를 사용한 블로그 엔진: 단일 게시물 페이지 구축
55193 단어 javascriptreactfreshwebdev
목차
Sokhavuth TIN ・ 8월 9일 ・ 2분 읽기
GitHub: https://github.com/Sokhavuth/deno-fresh
데노 배치: https://khmerweb-fresh.deno.dev
// routes/post/[id].jsx
/** @jsx h */
import { h } from "preact";
import VPost from '../../components/front/post.jsx';
import CPost from "../../controllers/front/post.js";
export const handler = {
async GET(req, ctx) {
return await CPost.getPost(req, ctx);
},
}
export default function Template(props){
return (
<VPost data={props.data} />
)
}
// controllers/front/post.js
import { setting } from "setting";
import postdb from "../../models/post.ts"
class Post{
async getPost(req, ctx){
const config = setting();
config.item = await postdb.getPost(ctx.params.id);
return await ctx.render({"setting": config});
}
}
export default new Post();
// models/post.ts
import { mydb } from "setting"
interface PostSchema {
_id: ObjectId;
id: string;
title: string;
content: string;
categories: string[];
thumb: string;
date: string;
videos: string;
userid: string;
}
class Post{
async count(query={}){
const posts = mydb.collection<PostSchema>("posts")
return await posts.countDocuments(query)
}
async insertPost(req, user_id: string){
const id = crypto.randomUUID()
const formData = await req.formData()
let categories: string[]
if(formData.get("categories").includes(',')){
categories = formData.get("categories").split(',')
}else{
categories = [formData.get("categories")]
}
const new_post = {
id: id,
title: formData.get("title"),
content: formData.get("content"),
categories: categories,
thumb: formData.get("thumb"),
date: formData.get("datetime"),
videos: formData.get("videos"),
userid: user_id,
}
const posts = mydb.collection<PostSchema>("posts")
await posts.insertOne(new_post)
}
async getPosts(amount: number, query={}){
const posts = mydb.collection<PostSchema>("posts")
return await posts.find(query).sort({date:-1,_id:-1}).limit(amount).toArray()
}
async getPost(post_id: string){
const posts = mydb.collection<PostSchema>("posts")
return await posts.findOne({id: post_id})
}
async updatePost(req, post_id: string){
const formData = await req.formData()
let categories: string[]
if(formData.get("categories").includes(',')){
categories = formData.get("categories").split(',')
}else{
categories = [formData.get("categories")]
}
const edited_post = {$set:{
title: formData.get("title"),
content: formData.get("content"),
categories: categories,
thumb: formData.get("thumb"),
date: formData.get("datetime"),
videos: formData.get("videos"),
}}
const posts = mydb.collection<PostSchema>("posts")
await posts.updateOne({id: post_id}, edited_post)
}
async deletePost(post_id: string){
const posts = mydb.collection<PostSchema>("posts")
await posts.deleteOne({id: post_id})
}
async paginatePosts(amount: number, page: number){
const posts = mydb.collection<PostSchema>("posts")
return await posts.find().skip(amount*page).sort({date:-1,_id:-1}).limit(amount).toArray();
}
}
export default new Post()
// components/front/post.jsx
/** @jsx h */
import { h } from "preact";
import Base from "../base.jsx";
function PostJsx(props){
const item = props.data.setting.item;
return(
<section class="Home">
<link href="/styles/front/home.css" rel="stylesheet" />
<script src="/scripts/menu.js"></script>
<header>
<div class="inner region">
<div class="title"><a href="/">{ props.data.setting.site_title }</a></div>
<form action="search" method="post">
<select class="category" name="frontSearch">
<option>Posts</option>
<option>Books</option>
</select>
<input type="text" name="q" required placeholder="Search" />
<input type="submit" value="Submit" />
</form>
<div class="login">
<a href="/login">Login</a> | <a href="#">Register</a>
</div>
</div>
</header>
<div class="menu">
<div class="inner region">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="/styles/front/menu.css" />
<div class="topnav" id="myTopnav" dangerouslySetInnerHTML={{__html: `
<a href="/" class="active">Home</a>
<a href="#news">News</a>
<a href="#contact">Contact</a>
<a href="#about">About</a>
<a href="javascript:void(0);" class="icon" onclick="mobileMenu()">
<i class="fa fa-bars"></i>
</a>
`}}/>
</div>
</div>
<link rel="stylesheet" href="/styles/front/main.css" />
<link rel="stylesheet" href="/styles/front/post.css" />
<script src="/scripts/setPlayer.js"></script>
<div class="main region">
<div class="content">
<div class="post">
<h2 class="title">{item.title}</h2>
<div class="categories">{item.categories.toString()}</div>
<div class="date">{(new Date(item.date)).toLocaleDateString("it-IT")}</div>
{ ((item.videos !== '')&&(item.videos !== '[]')) &&
<section>
<div class="video">
<div class="screen"></div>
<div class="playlist"></div>
</div>
<script dangerouslySetInnerHTML={{__html: `
const videos = JSON.parse('${ item.videos }')
videos.reverse()
let clicked = 0
for(let index in videos){
let ending = ''
if(index == videos.length-1){
ending = videos[index].status
}
let title = videos[index].title
let result = '<div id="part'+index+'" class="part" onClick="setScreen(videos['+index+'],'+index+',true)">videoTitle part '+(++index)+' '+ending+'</div>'
let html = result.replace("videoTitle", "${item.title}")
$('.playlist').append(html)
}
setScreen(videos[0],0,false)
`}}/>
</section>
}
<article dangerouslySetInnerHTML={{__html: `
${item.content}
`}}/>
</div>
<div id="disqus_thread"></div>
<script dangerouslySetInnerHTML={{__html: `
var disqus_config = function () {
this.page.url = "https://khmerweb-fresh.deno.dev/post/${item.id}"
this.page.identifier = "${item.id}";
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://multimedia-15.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
`}}/>
</div>
<div class="sidebar">Sidebar</div>
</div>
</section>
)
}
export default function Post(props){
props.data.page = PostJsx;
return(
<Base data={props.data} />
)
}
// static/scripts/setPlayer.js
function setScreen(entry,id,click){
if(entry['type'] == 'OK'){
var url = `//ok.ru/videoembed/${entry['id']}`
}else if(entry['type'] == 'YouTube'){
var url = `https://www.youtube.com/embed/${entry['id']}`
}else if(entry['type'] == 'YouTubePlaylist'){
var url = `https://www.youtube.com/embed/videoseries?list=${entry['id']}`
}else if(entry['type'] === "Facebook"){
var url = `https://www.facebook.com/watch/?v=${entry['id']}`
}else if(entry['type'] === "GoogleDrive"){
var url = `https://docs.google.com/file/d/${entry['id']}/preview`
}else if(entry['type'] === "Vimeo"){
var url = `https://player.vimeo.com/video/${entry['id']}`
}else if(entry['type'] === "Dailymotion"){
var url = `https://www.dailymotion.com/embed/video/${entry['id']}`
}
if(entry['type'] !== 'Facebook'){
var iframe = `<div>
<iframe id="video-player" src="${url}" frameborder="0" allowfullscreen></iframe>
</div>`;
}else{
var iframe = `<div class="fb-video" data-href="${url}" data-width="auto" data-show-captions="true"></div>`;
}
if(click){
$('.post .video .playlist #part'+clicked)
.css({'background':'var(--background)','color':'black'})
}
$('.post .video .playlist #part'+id)
.css({'background':'var(--background-dark)','color':'white'})
$('.post .video .screen').html(iframe)
if((entry['type'] === "Facebook")&&(fb_api)){
FB.XFBML.parse()
}
clicked = id
}
/* static/styles/front/post.css */
.Home .main .content .post .title{
padding-bottom: 15px;
}
.Home .main .content .post .date{
padding-bottom: 30px;
}
.Home .main .content .post .video{
background: var(--background);
}
.Home .main .content .post .video .screen div{
position: relative;
padding-top: 56.25%;
}
.Home .main .content .post .video .screen div iframe{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.Home .main .content .post .video .playlist{
max-height: 405px;
overflow: auto;
margin-bottom: 20px;
}
.Home .main .content .post .video .playlist .part{
text-align: left;
padding: 10px;
color: black;
display: block;
height: auto;
border: 1px solid black;
border-top: none;
font-size: 16px;
}
.Home .main .content .post .video .playlist .part:last-child{
margin-bottom: 0;
}
.Home .main .content .post .video .playlist .part:hover{
cursor: pointer;
opacity: .7;
}
.Home .main .content .post article img{
width: 100%;
}
Reference
이 문제에 관하여(Fresh를 사용한 블로그 엔진: 단일 게시물 페이지 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/sokhavuth/blog-engine-with-fresh-building-single-post-page-5cfh텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)