準備
以下のコマンドで必要なパッケージをインストールします。
npm install markdown-it
1から作る場合は以下のパッケージをインストールします。
npm install express-session
npm install express-validator
npm install sqlite3
npm install knex
npm install bookshelf
npm install markdown-it
モデル作成
以下のコマンドでモデルを作成します。
npx sequelize-cli model:generate --name Markdata --attributes userId:integer,title:string,content:text
モデルファイルを修正、追記します。
models/markdata.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const Markdata = sequelize.define('Markdata', {
userId: {
type: DataTypes.INTEGER,
validate: {
notEmpty: {
msg: "利用者は必須です。"
}
}
},
title: {
type: DataTypes.STRING,
validate: {
notEmpty: {
msg: "タイトルは必須です。"
}
}
},
content: {
// 長さの制限無し
type: DataTypes.TEXT,
validate: {
notEmpty: {
msg: "コンテンツは必須です。"
}
}
}
}, {});
Markdata.associate = function(models) {
Markdata.belongsTo(models.User);
};
return Markdata;
};
models/users.js
User.associate = function(models) {
// 主モデル
User.hasMany(models.Board);
// 以下を追記
User.hasMany(models.Markdata);
};
以下のコマンドでマイグレーションを行います。
npx sequelize-cli db:migrate
ルーティング作成
以下のファイルを作成します。
routes/marks.js
const express = require('express');
const router = express.Router();
const db = require('../models/index');
const { Op } = require("sequelize");
const MarkdownIt = require('markdown-it');
const markdown = new MarkdownIt();
const pnum = 10;
// ログインのチェック
function check(req, res) {
if (req.session.login == null) {
// ログイン後に戻る値
req.session.back = '/md';
res.redirect('/users/login');
return true;
} else {
return false;
}
}
// トップページ
router.get('/', (req, res, next) => {
if (check(req, res)){ return };
db.Markdata.findAll({
where:{userId: req.session.login.id},
limit:pnum,
order: [
['createdAt', 'DESC']
]
}).then(mds => {
var data = {
title: 'Markdown Search',
login: req.session.login,
message: '※最近の投稿データ',
form: {find:''},
content: mds
};
res.render('md/index', data);
});
});
// 検索フォームの送信処理
router.post('/', (req, res, next) => {
if (check(req, res)){ return };
db.Markdata.findAll({
where:{
userId: req.session.login.id,
content: {[Op.like]:'%' + req.body.find + '%'},
},
order: [
['createdAt', 'DESC']
]
}).then( mds => {
var data = {
title: 'Markdown Search',
login: req.session.login,
message: '※"' + req.body.find + '" で検索された最近の投稿データ',
form: req.body,
content:mds
};
res.render('md/index', data);
});
});
// 新規作成ページの表示
router.get('/add', (req, res, next) => {
if (check(req, res)){ return };
res.render('md/add', { title: 'Markdown/Add' });
});
// 新規フォームの送信処理
router.post('/add', (req, res, next) => {
if (check(req, res)){ return };
db.sequelize.sync()
.then(() => db.Markdata.create({
userId: req.session.login.id,
title: req.body.title,
content: req.body.content,
})
.then(model => {
res.redirect('/md');
})
);
});
// '/mark'へアクセスした際のリダイレクト
router.get('/mark', (req, res, next) => {
res.redirect('/md');
return;
});
// 指定IDのMarkdata表示
router.get('/mark/:id', (req,res, next) => {
if (check(req, res)){ return };
db.Markdata.findOne({
where: {
id: req.params.id,
userId: req.session.login.id
},
})
.then((model) => {
makepage(req, res, model, true);
});
});
// Markdataの更新処理
router.post('/mark/:id', (req, res, next) => {
if (check(req, res)){ return };
db.Markdata.findByPk(req.params.id)
.then(md => {
md.content = req.body.source;
md.save().then((model) => {
makepage(req, res, model, false);
});
})
});
// 指定IDのMarkdaraの表示ページ作成
function makepage(req, res, model, flg) {
var footer;
if (flg){
var d1 = new Date(model.createdAt);
var dstr1 = d1.getFullYear() + '-' + (d1.getMonth() + 1) + '-' + d1.getDate();
var d2 = new Date(model.updatedAt);
var dstr2 = d2.getFullYear() + '-' + (d2.getMonth() + 1) + '-' + d2.getDate();
footer = '(created: ' + dstr1 + ', updated: ' + dstr2 + ')';
} else {
footer = 'UPdating date and time information...'
}
var data = {
title: 'Markdown',
id: req.params.id,
head: model.title,
footer: footer,
content: markdown.render(model.content),
source: model.content
};
res.render('md/mark', data);
}
module.exports = router;
app.jsに登録します。
app.js
var marksRouter = require('./routes/marks');
app.use('/md', marksRouter);
テンプレート作成
以下のファイルを作成します。
views/md/index.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html">
<title><%= title %></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body class="container">
<header>
<h1 class="display-4">
<%= title %>
</h1>
</header>
<div role="main">
<p class="h5 my-4">Hi,
<span><%= login.name %></span>!<br>
Welcome to <%= title %>.
</p>
<form action="/md" method="POST">
<div class="form-group">
<label for="find">FIND</label>
<input type="text" name="find" id="find" value="<%= form.find %>" class="form-control">
</div>
<input type="submit" value="検索" class="btn btn-primary">
</form>
<p class="my-4 h5"><%= message %></p>
<table class="table">
<% for (var i in content) { %>
<% var ob = content[i]; %>
<tr>
<td>
<a href="/md/mark/<%= ob.id %>" class="text-dark">
<%= ob.title %>
</a>
</td>
</tr>
<% } %>
</table>
<p> </p>
<p><a href="/md/add">※データを登録</a></p>
</div>
</body>
</html>
views/md/add.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html">
<title><%= title %></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body class="container">
<header>
<h1 class="display-4 text-primary">
<%= title %>
</h1>
</header>
<div role="main">
<form action="/md/add" method="post">
<div class="form-group">
<label>TITLE</label>
<input type="text" name="title" id="title" class="form-control">
</div>
<div class="form-group">
<label>CONTENT</label>
<textarea name="content" id="content" rows="10" class="form-control"></textarea>
</div>
<input type="submit" value="送信" class="btn btn-primary">
</form>
<p class="mt-4"><a href="/md"><< Top へ戻る</a></p>
</div>
</body>
</html>
views/md/mark.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html">
<title><%= title %></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body class="container">
<header>
<h1 class="display-4">
<%= title %>
</h1>
</header>
<div role="main">
<p class="h5"><%= head %></p>
<form action="/md/mark/<%=id %>" method="post">
<div class="form-group">
<label for="source">SOURCE</label>
<textarea name="source" id="source" rows="5" class="form-control"><%= source %></textarea>
</div>
<input type="submit" value="更新" class="btn btn-primary">
</form>
<div class="card mt-4">
<div class="card-header text-center h5">
Preview
</div>
<div class="card-body">
<%- content %>
</div>
<div class="card-footer text-muted text-right">
<%= footer %>
</div>
</div>
<p class="mt-4"><a href="/md"><< Top へ戻る</a></p>
</div>
</body>
</html>