GitHub: https://github.com/Sokhavuth/deno-fresh
Deno Deploy: https://khmerweb-fresh.deno.dev/login
// routes/admin/post.jsx
/** @jsx h */
import { h } from "preact";
import VPost from '../../components/admin/post.jsx';
import CPost from "../../controllers/admin/post.js";
export const handler = {
async GET(req, ctx){
return await CPost.getPage(req, ctx);
},
async POST(req, ctx){
return await CPost.createPost(req, ctx);
},
}
export default function Template(props){
return (
<VPost data={ props.data } />
)
}
// controllers/admin/post.js
import { getCookies, deleteCookie } from "cookies";
import { setting, secret_key, myredis } from 'setting';
import { verify } from "jwt";
import postdb from "../../models/post.ts";
class Post{
async getPage(req, ctx){
const cookies = getCookies(req.headers);
if((cookies)&&(cookies.session_id)){
const jwt = await myredis.get(cookies.session_id);
try{
const payload = await verify(jwt, secret_key, "HS512");
if(payload.user){
const config = setting();
config.page_title = "Post page";
config.username = payload.user.title;
config.count = await postdb.count();
config.items = await postdb.getPosts(config.post_amount);
return await ctx.render({"setting": config});
}
}catch(error){
console.log(error);
const config = setting();
config.page_title = "Login page";
const resp = new Response();
deleteCookie(resp.headers, "session_id");
return new Response(undefined, { headers: {location: `/login`}, status: 302 });
}
}
return new Response(undefined, { headers: {location: `/login`}, status: 302 });
}
async createPost(req, ctx){
const cookies = getCookies(req.headers);
if((cookies)&&(cookies.session_id)){
const jwt = await myredis.get(cookies.session_id);
try{
const payload = await verify(jwt, secret_key, "HS512");
if(payload.user.role in {'Admin':1,'Editor':1,'Author':1}){
await postdb.insertPost(req, payload.user.id);
}
return new Response(undefined, { headers: {location: `/admin/post`}, status: 302 });
}catch(error){
console.log(error);
const resp = new Response(undefined, { headers: {location: `/login`}, status: 302 });
deleteCookie(resp.headers, "session_id");
return resp;
}
}
return new Response(undefined, { headers: {location: `/login`}, status: 302 });
}
}
export default new Post();
// components/admin/index.jsx
/** @jsx h */
import { h } from "preact";
import Base from "../base.jsx"
function IndexJsx(props){
const Page = props.data.pageInner;
const items = props.data.setting.items;
const listItems = items.map((item) =>
<li>
<a class="thumb" href={`/post/${item.id}`}>
<img src={item.thumb} />
{((item.videos !== "" )&&(item.videos !== "[]")) &&
<img class="play-icon" src={`/images/play.png`} />
}
</a>
<div class="title">
<a href={`/post/${item.id}`}>{item.title}</a>
<div>{(new Date(item.date)).toLocaleDateString('it-IT')}</div>
</div>
<div class="edit">
<a href={`/admin/post/edit/${item.id}`}><img src={`/images/edit.png`} /></a>
<a href={`/admin/post/delete/${item.id}`}><img src={`/images/delete.png`} /></a>
</div>
</li>
)
return(
<section class="Index" >
<link rel="stylesheet" href="/styles/admin/index.css" />
<header>
<div class="inner region">
<div class="title">{props.data.setting.page_title}</div>
<form action="/admin/search" method="post">
<select name="admin_search">
<option>posts</option>
<option>books</option>
</select>
<input type="text" name="admin_q" required placeholder="Search" />
<input type="submit" value="Search" />
</form>
<div class="logout"><span>{props.data.setting.username}</span> | <a href="/">Home</a> | <a href="/logout">Logout</a></div>
</div>
</header>
<div class="main region">
<div class="sidebar">
<div class="inner">
<a href="/admin/post"><img src="/images/movie.png" /></a>
<a href="/admin/post">Post</a>
<a href="/admin/book"><img src="/images/books.png" /></a>
<a href="/admin/book">Book</a>
<a href="/admin/category"><img src="/images/category.png" /></a>
<a href="/admin/category">Category</a>
<a href="/admin/upload"><img src="/images/upload.png" /></a>
<a href="/admin/upload">Upload</a>
<a href="/admin/user"><img src="/images/users.png" /></a>
<a href="/admin/user">User</a>
<a href="/admin/setting"><img src="/images/setting.png" /></a>
<a href="/admin/setting">Setting</a>
</div>
</div>
<div class="content">
<Page data={props.data} />
</div>
</div>
<div class="footer region">
<div class="info">Total number of items: {props.data.setting.count}</div>
<ul class="list">
{ listItems }
</ul>
<div class="pagination"><img src="/images/load-more.png" /></div>
<div class="credit">© <a href="https://khmerweb.vercel.app/">Khmer Web 2022</a></div>
</div>
</section>
)
}
export default function Index(props){
props.data.page = IndexJsx
return(
<Base data={props.data} />
)
}
/* static/styles/admin/index.css */
.Index header{
background: var(--background-dark);
border-bottom: 7px solid var(--background-light);
margin-bottom: 20px;
}
.Index header .inner{
padding: 5px 0;
}
.Index header .inner .title{
font: 30px/1.5 StardosStencil, Limonf3;
color: orange;
}
.Index header .inner{
display: grid;
grid-template-columns: 25% auto 25%;
align-items: center;
}
.Index header .inner form{
display: grid;
grid-template-columns: 20% auto 20%;
}
.Index header .inner form input,
.Index header .inner form select{
font: var(--body-font);
padding: 2px 5px;
}
.Index header .inner .logout{
text-align: right;
color: white;
}
.Index header .inner .logout a{
color: white;
}
.Index .main{
display: grid;
grid-template-columns: calc(25% - 10px) 75%;
grid-gap: 10px;
}
.Index .main .sidebar{
background: #17a372;
padding: 20px;
}
.Index .main .sidebar .inner{
display: grid;
grid-template-columns: 25% auto;
grid-gap: 20px 10px;
align-items: center;
}
.Index .main .sidebar .inner img{
width: 100%;
float: left;
}
.Index .main .sidebar .inner a{
color: white;
font: 18px/1.5 Oswald, Bayon;
}
.Index .footer .info{
margin-top: 10px;
background: #17a372;
text-align: center;
padding: 10px;
color: white;
}
.Index .footer ul{
list-style-type: none;
display: grid;
grid-template-columns: calc(50% - 5px) calc(50% - 5px);
grid-gap: 10px;
padding: 10px 0;
}
.Index .footer ul li{
display: grid;
grid-template-columns: 25% 60% 15%;
background: #17a372;
align-items: center;
}
.Index .footer ul li .thumb{
display: block;
position: relative;
padding-top: 56.25%;
}
.Index .footer ul li .thumb img{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
float: left;
}
.Index .footer ul li .thumb .play-icon{
position: absolute;
width: 25%;
height: auto;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.Index .footer ul li .title{
padding: 5px 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.Index .footer ul li .title a{
color: white;
font: 17px/1.5 StardosStencil, Limonf3;
}
.Index .footer ul li .edit img{
width: 35px;
margin-right: 5px;
float: left;
visibility: hidden;
}
.Index .footer ul li:hover .edit img{
visibility: visible;
}
.Index .footer .pagination{
text-align: center;
background: #17a372;
padding: 5px 0 0;
}
.Index .footer .pagination img:hover{
cursor: pointer;
opacity: .7;
}
.Index .footer .credit{
text-align: center;
padding: 30px 0;
}
Top comments (0)