// controllers/admin/post.ts

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 = "ទំព័រ​ការផ្សាយ";
                    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 = "ទំព័រ​ចុះ​ឈ្មោះ";
                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();

 

// 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()
    }
}

export default new Post()

 

// components/admin/index.tsx

/** @jsx h */
import { h } from "preact"
import { PageProps } from "$fresh/server.ts";
import Base from "../base.tsx"


function IndexTsx(props: PageProps){
    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>ការផ្សាយ</option>
                  <option>សៀវភៅ</option>
                </select>
                <input type="text" name="admin_q" required placeholder="Search" />
                <input type="submit" value="ស្វែងរក" />
              </form>
              <div class="logout"><span>{props.data.setting.username}</span> | <a href="/">ទំព័រ​មុខ</a> | <a href="/logout">ចេញ​ក្រៅ</a></div>
          </div>
        </header>

        <div class="main region">
          <div class="sidebar">
            <div class="inner">
              <a href="/admin"><img src="/images/movie.png" /></a>
              <a href="/admin">ការផ្សាយ</a>

              <a href="/admin/book"><img src="/images/books.png" /></a>
              <a href="/admin/book">សៀវភៅ</a>

              <a href="/admin/category"><img src="/images/category.png" /></a>
              <a href="/admin/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">អ្នក​ប្រើប្រាស់</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">សរុប​ទាំងអស់​មាន​ចំនួនៈ {props.data.setting.count}</div>
          <ul class="list">
              { listItems }
          </ul>
          <div class="pagination"><img src="/images/load-more.png" /></div>
          <div class="credit">&copy; <a href="https://khmerweb.vercel.app/">Khmer Web 2022</a></div>
        </div>
        </section>
    )
}

export default function Index(props: PageProps){
    props.data.page = IndexTsx
    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 Oswald, 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;
}

 

GitHub: https://github.com/Sokhavuth/khmerweb-fresh

Deno Deploy: https://khmerweb.deno.dev/login