200行代码手动搭建一个博客系统

minblog是一款仅200行代码的轻量级博客系统,支持管理员登录及Markdown编辑文章。该系统采用Koa.js框架搭建,利用EJS模板引擎渲染页面,并通过JSON文件存储文章数据。

minblog是一个不足200行代码的博客系统. 支持管理员登陆, markdown编辑文章

项目地址 github.com/tans/minblo…

示例地址 : blog.minapp.xin

服务器实现代码如下:

 
const fs = require("fs");
const path = require("path");
const Koa = require("koa");
const Router = require("koa-router");
const Render = require("koa-ejs");
const bodyParser = require("koa-bodyparser");
const marked = require("marked");

// 使用koa
const app = new Koa();
const router = new Router();
Render(app, {
  root: path.join(__dirname, "view"),
  layout: "layout",
  viewExt: "ejs"
});

// 管理员登陆密码
let token = "minblog";

// 文章保存在posts.json
let posts = JSON.parse(fs.readFileSync("./posts.json").toString()); 

 
//首页
router.get("/", async ctx => {
  ctx.state.posts = posts;
  await ctx.render("list");
});
//文章页
router.get("/posts/:postid", async ctx => {
  ctx.state.post = posts[ctx.params.postid];
  ctx.state.postid = ctx.params.postid;
  ctx.state.html = marked(ctx.state.post.content);
  await ctx.render("detail");
});
//登陆页
router.get("/login", async ctx => {
  await ctx.render("login");
});
//登陆接口
router.post("/login", async ctx => {
  if (ctx.request.body.token == token) {
    ctx.cookies.set("token", token);
    ctx.redirect("/");
  }
  ctx.body = "密码不对";
});
//文章编辑页
router.get("/edit/:postid", async ctx => {
  ctx.state.post = posts[ctx.params.postid] || {};
  ctx.state.postid = ctx.params.postid;
  await ctx.render("edit");
});
//保存文章
router.post("/posts/:postid", async ctx => {
  if (!ctx.state.logined) {
    ctx.body = "未登录";
  }
  if (ctx.request.body.show !== "true") {
    delete ctx.request.body.show;
  }
  if (ctx.params.postid == "new") {
    posts.push(ctx.request.body);
  } else {
    posts[ctx.params.postid] = ctx.request.body;
  }

  fs.writeFileSync("./posts.json", JSON.stringify(posts, null, 2));
  ctx.redirect("/posts/" + ctx.params.postid);
});
//上下文加载登陆状态
app.use(async (ctx, next) => {
  ctx.state.logined = false;
  if (ctx.cookies.get("token") == token) {
    ctx.state.logined = true;
  }
  await next();
});
app.use(bodyParser());
app.use(router.routes()).use(router.allowedMethods());

app.listen(3000);
复制代码

前端页面


//layout.ejs
<!DOCTYPE html>
<html>
<head>
  <title>minblog</title>
  <style type="text/css">
    body{
      color:#555;
      padding:10px;
      padding-top:40px;
      max-width: 720px;
      margin: 0 auto;
      position: relative;
    }
    a{
      color: #001234;
      font-weight: bolder;
      text-decoration: none;
    }
  </style>
</head>
<body>
  <a href="/" style="position: absolute; top:10px;left: 10px;">minblog</a>
  <hr style="border:none; border-top:1px solid #eee" />
  <%- body %>
</body>
</html>

//首页部分
<style type="text/css">
  .articleList{list-style: none; padding: 0 }
  .articleList li{padding-top: 10px; }
  .articleList .hide{text-decoration: line-through;}

</style>
<div style="position:absolute;top:10px; right:10px;">
  <%if(logined){%>
    <a href="edit/new">发表</a>
  <%}else{%>
    <a href="login">登陆</a>
  <%}%>
</div>

<ul  class="articleList">
  <% for (var i = 0; i < posts.length; i++) {
    var postid = posts.length-i-1;
  %>
    <%if(posts[postid].show || logined){%>
      <li class="<%=posts[postid].show?'':'hide'%>" >
        <a href="posts/<%=postid%>"><%=posts[postid].title%></a>
      </li>
    <%}%>
  <% } %>
</ul>


//文章页部分
<h1 style="text-align: center"><%=post.title%></h1>
<div><%-html%></div>
<div style="position:absolute;top:10px; right:10px;">
  <a href="../edit/<%=postid%>">编辑</a>
</div>

//登陆页部分
<h2>管理员登陆</h2>
<form method="post" action="login">
  <input name="token"  placeholder="输入登陆密码" />
  <button>确定</button>
</form>

//编辑页部分
<form method="post" action="../posts/<%=postid%>" >
  <div>
    <div>标题</div>
    <input style="width: 320px;" type="" name="title" value="<%=post.title%>">
  </div>
  <br/>
  <div>
    <div>内容</div>
    <textarea name="content" style="width: 480px;" rows="30"><%=post.content%></textarea>
  </div>
  <div>
    <div>可见</div>
    <select name="show">
      <option value=true <%=post.show?'selected':''%> >显示</option>
      <option value=false <%=!post.show?'selected':''%>>隐藏</option>
    </select>
  </div>
  <br/>
  <br/>
  <button>确定</button>
</form>
复制代码

转载于:https://juejin.im/post/5a76f46a6fb9a063606eb236

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值