1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【node.js + express + mongodb】記事管理アプリを作ってみた

Last updated at Posted at 2022-03-05

概要

記事を投稿、管理できるアプリを作成した。機能としては、アカウント登録、ログイン機能、記事作成&編集&削除機能がある。見た目は以下のようになる。

見た目

見た目は以下のようになる。

ログイン画面:bootstrap使用。
1.png

登録画面
2.png

記事リスト:ページ分け、編集削除
3.png

記事詳細:書式がそのまま保存
4.png

記事投稿:リーチテキストエディタ使用
5.png

ソースコード

ソースコードをgithubにアップした。
https://github.com/akikisai/Article-management-app

使用したライブラリー

使用したものやバージョンは以下になる。

"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
"express-session": "^1.17.2",
"http-errors": "~1.6.3",
"moment": "^2.29.1",
"mongodb": "^4.4.0",
"morgan": "~1.9.1"

環境構築

まず環境構築を行う。

node.js環境構築

以下のリンクからnode.js推奨版をダウンロードし、特に何も変更せずインストーラーの流れに沿ってインストールする。これでnpmコマンドが使えるようになる。
node.jsダウンロードサイト

mongoDB環境構築

以下のリンクの内容に沿って環境構築する。
mongoDB環境構築

mongoコマンドを打ってmongo shellが現れれば成功。

プロジェクト作成

デスクトップ上にプロジェクトを作成したい。まずcdコマンドで移動し、アプリケーションを素早く作成できるようにするためexpressをインストールする。

$ cd Desktop
$ npm install express-generator -g

textappというプロジェクトを作成する。この時デスクトップにtextappフォルダーが作られる。
その後textappフォルダーな中に入り、npm installで初期のパッケージを使用できるようにインストールする。

$ express -e textapp
$ cd textapp
$ npm install

※テキストエディタを使うが、ない場合visual studio codeもしくはatomが推奨。
visual studio code公式ページ
Atom公式ページ

Node.jsでは、変更を有効にするためにプロセスを再起動する必要があるが、nodemonを使用してプロセスを自動的に再起動するので、余分な手順を排除る。これをインストールする。

$ npm install nodemon -g

package.jsonファイル内の"start": "node ./bin/www"を以下のように変更し、これでnodemonに対応できる。

"start": "nodemon ./bin/www"

npm startでプロジェクト起動する。

$ npm start

ファイル内にポートが3000と設定されていることがわかり、
binファイル > wwwファイル > var port = normalizePort(process.env.PORT || '3000');
ウェブサイトに以下のリンクを入力すると、
http://localhost:3000/
welcome to expressが表示される。
ここで注意したいのが、次の作業に当たり、npm startを入力したターミナルは閉じないこと。

mongoDBと接続

mongoDBの環境構築は済んでいて、ここでは新しいgit bashを開き、以下のコマンドでデータベース保存場所を決める。この場合C:\mongodb\dataに保存している。

$ mongod --dbpath "C:\mongodb\data"

mongoDBと接続するためには、npm startを入力したシェルと、mongoコマンドを打って出てきたmongoシェルが同時に存在しなければならない。mongoコマンドを打って出てきたmongoシェルがあるということはmongoDBに接続成功。なお、ターミナルにおいてmongoシェルにうまく入力できない場合、windowsのコマンドプロンプトでも大丈夫。

$ mongo

mongoDBとつなぎ、shell を使えるようになる。

データベースの中身を確認

>show dbs 

デフォルトはお以下のようになる。

admin   0.000GB
config  0.000GB
local   0.000GB

textappというデータベース作成
use textapp

userコレクションを作る。
db.createCollection('users');

コレクションを表示する。
show collections
正しく表示される。

mongoDBコマンド参考用

nodejsとmongodbを接続するために、mongodb nodejs driverを使う。
※ほかにもMongooseを使用する方法がある、こっちのほうが人気らしい。

インストール含め使い方はmongodb nodejs driverドキュメント参照。

cd textappでtextappフォルダ内でmongodb nodejs driverをインストールする。
npm install mongodb --save

ドキュメントの例を参考にし、mongodbに接続するコードを書く。ファイルの新規作成などの細かい手順は全部省略する(githubソースコードを見ればわかるので)この記事では主に機能についてまとめたい。

model/index.js
var MongoClient = require('mongodb').MongoClient;

var url = 'mongodb://localhost:27017/textapp';
var dbName = 'textapp';

//データベースへ接続用関数
function connect(callback) {//関数化
  MongoClient.connect(url, function(err, client) {//コールバック関数

	//エラーの場合
    if (err) {
      console.log('データベース接続エラー', err);
	//エラーじゃない場合
    } else {
	//接続成功
      var db = client.db(dbName);
      callback && callback(db);
    }
  });
}


module.exports = {
  connect
}

接続する機能を関数化し、あとで使えるようにする。

登録&ログイン

ルーティング設定をする。

routes/index.js
// 登録ページ
router.get('/regist', function(req, res, next) {
  res.render('regist', {})
});

// ログインページ
router.get('/login', function(req, res, next) {
  res.render('login', {})
});

登録ページのコードは以下になる。

views/login.ejs
<body>

  <div class="container">
	   <div class="login-container">
            <div class="form-box">
                <form action="/users/regist" method="post">
                  <input type="text" name="username" value="" placeholder="ユーザ名">
                  <input type="password" name="password" value="" placeholder="パスワード">
                    <button class="btn btn-info btn-block login" type="submit">登録する</button>
                </form>
                <div>アカウントをお持ちの場合、<br><a href="/login">ログイン</a>してください。</div>
            </div>
      </div>
  </div>

</body>

これにより、ユーザ名とパスワードは"/users/regist"へ送信される。

routes/users.js
// 登録
router.post('/regist', function(req, res, next) {
  var data = {
    username: req.body.username,
    password: req.body.password
  };


  // データの接続
  model.connect(function(db) {
    //コレクションusersにデータ挿入する
    db.collection('users').insertOne(data, function(err, ret) {
      //登録失敗
      if (err) {
        console.log('登録失敗');
        res.redirect('/regist');
      } else {
        //登録成功、ログインページへ
        res.redirect('/login');
      }
    });
  })
})

送信されてきた情報はdataに格納され、データベースに接続し、照らし合わせることで登録成功か失敗か。ちなみにmongoDBインストール時、MongoDB compassをインストールしていれば、それを開いて、connectを押すことでmongoDBへ接続でき、GUI画面で登録された情報は実際あるのかを確認できる。mongoDB compass参考

ログインページのコードは以下のようになる。

views/login.ejs
<body>

  <div class="container">
	<div class="login-container">

            <div class="form-box">
                <form action="/users/login" method="post">
                    <input name="username" type="text" placeholder="ユーザ名">
                    <input name="password" type="password" placeholder="パスワード">
                    <button class="btn btn-info btn-block login" type="submit">ログイン</button>
                </form>
                <div>アカウントをお持ちでない場合<a href="/regist">作成</a>できます。</div>

            </div>
        </div>

</div>

</body>

これにより、ユーザ名とパスワードは"/users/login"へ送信される。

routes/users.js
// ログイン
router.post('/login', function(req, res, next) {
  var data = {
    username: req.body.username,
    password: req.body.password
  }

  //データベースに接続する。
  model.connect(function(db) {
    //コレクションに同じデータがあるか、あればログイン成功。
    db.collection('users').find(data).toArray(function(err, docs) {
      if (err) {
		//ログイン失敗→もう一回ログインページへ
        res.redirect('/login');
      }else {
	  //調べたdocsの結果が0以上であれば成功
        if (docs.length > 0) {
          // ログイン成功 session保存
          req.session.username = data.username;//ユーザーネームをセッションに保存
          res.redirect('/');
        } else {
          res.redirect('/login');
        }
      }
    })
  })
})

送信されてきた情報はdataに格納され、データベースに接続し、照らし合わせることでログイン成功か失敗か。失敗したらもう一度ログインページへ行く。また、上のコードでsessionを使用している。これはログイン状態を保存するため(毎回ログインするのはめんどくさいので)

sessionを使うためまずexpress-sessionをインストールする。textappフォルダ内で以下のコマンドを実行する。使い方はexpress-sessionドキュメント参照。

$ npm install express-session -S

最初に設定をする。

app.js
var session = require('express-session');

//セッション
app.use(session({
  secret: 'sai textapp',//何でもよい
  resave: false,
  saveUninitialized: true,
  cookie: { maxAge: 1000 * 60 * 20 } //cookie有効時間=ログイン有効期間
}))

ここでのログイン有効期間を20分とした。また上のログインが成功した場合ユーザ名をsessionに格納される。もしsessionにユーザ名がある場合、ログインせずにホームぺーズへ飛ぶことができるようになる。ユーザ名がニア場合、かつログインページと登録ページ以外ならば、ログインページへ戻る。これでログインしていない状態だとホームページは見れない。

app.js
// sessionの中身判定
app.get('*', function(req, res, next) {
  var username = req.session.username;
  //パスを判断したいので宣言
  var path = req.path;
  
  //ログインページと登録ページ以外で、ユーザ名なければログインページへ飛ぶ。
  if (path != '/login' && path != '/regist') {
    if (!username) {
      res.redirect('/login');
    }
  }
  next();
})

ログアウト

ヘッダーに投稿ボタンとログアウトボタンを配置する。

views/bar.ejs
<li class="header_navi_item"><a href="/users/logout"><span>ログアウト</span></a></li>

ログアウトするには簡単で、sessionの中身を空っぽにしてlogin画面へ飛べばよい。

routes/users.js
//ログアウト
router.get('/logout',function(req,res,next){
  req.session.username = null;
  res.redirect('/login');
})

記事投稿

ホームページに投稿するボタンを用意する。投稿画面/writeへ飛ぶ。

views/index.ejs
<li class="header_navi_item"><a href="/write"><span>投稿する</span></a></li>

write画面を作っていく。

views/write.ejs
<body>

  <%- include('bar', {username: username}) %>
  <div class="article">
    <form action="/article/add" method="post">

      <input type="text" name="title" placeholder="タイトル" value="">
      <textarea name="content" class="xheditor" id="mytextarea"></textarea>

	  <input type="submit" value="投稿">
      
    </form>
  </div>

</body>

なお、ここでのbarはbar.ejsで、ヘッダーを作ります。ヘッダーは数画面同じものを使うので便利さを求めて別々にbar.ejsとして作り、includeによって呼び出して使う。この場合usernameを求めているので、routes/index.ejsの
router.get('/write', function(req, res, next) {})の中身にはusernameを取得しusernameを返す必要がある。そうするとusernameがbar(ヘッダー)のユーザ名のところに格納し、一番上の見た目のようにユーザ名が表示される。

ここで、投稿するためにリッチテキストエディターを実装したい。
リッチテキストエディタとは、HTMLを知らなくても文字、画像、表などをツールボタンを使って作成・編集することができるもの。実際にコピーしたものを投稿内容欄に張り付けても書式が残る。
→ブログ投稿をする際のエディタのようなもの

今回使用するのはTinyMCEというもの。公式ページの始め方参照はこちら
やや古いですがとても分かりやすいTinyMCE使い方記事はこちら
なお、ほかのリーチテキストエディタを使いたい場合、リッチテキストエディタ一覧を参考して、各自調べてください。

ドキュメント通りにコードを書く。バージョン古くなるかもしれないので必ずドキュメント参照推奨。ドキュメントのやり方だと、

<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>

http://から始まって直接読み込むことができるので便利ですが、今回はTinyMCEを直接ダウンロードページからDownlord TinyMCE SDK nowをクリックし、ダウンロードして解凍して中身のファイルをtextapp/public/javascriptsの中にいれ、リンクは代わりにtinymce.min.jsを適切なパスで読み込む。今回の例では
javascripts/tinymce_5.10.3/tinymce/js/tinymce/
の中に存在する。

write.jsのheadタグ内をいじる。2つのscriptタグを追加した。

views/write.ejs
<head>

  <title>記事作成</title>
  <%- include head %>

  <script src="javascripts/tinymce_5.10.3/tinymce/js/tinymce/tinymce.min.js" referrerpolicy="origin"></script>

   <script>
     tinymce.init({
       selector: 'textarea',
       language: 'ja'
     });
   </script>

</head>

言語を日本語化にしたいので、TinyMCE言語パッケージページからjaを見つけ、ダウンロードする。解凍したlangsフォルダの「ja.js」を「tinymce/」にある「langsフォルダ」へ移動する。
うえのようにlanguage: 'ja'と記入すれば完成。

記事投稿するので、データベースに新しいコレクションを追加。
mongo shellで以下のコマンドを入力し、articlesコレクションを作る。

use textapp
db.createCollection('articles');

次は実際に記事書いたら、記事を投稿する機能を持つボタンを作る。ここで入力内容はformの/article/addへ行く。ということで作成していく。

routes/article.js

// 追加
router.post('/add', function(req, res, next) {
	//データを受け取る
	var data = {
      title: req.body.title,//タイトル
      content: req.body.content,//記事内容
      username: req.session.username,//ユーザ名
      id: Date.now()//時間

	//データベースに接続し、データを挿入する
    model.connect(function(db) {
      db.collection('articles').insertOne(data, function(err, ret) {
        if(err) {
          console.log('文件发布失败', err)
		//失敗、もう一回記事投稿ページ
          res.redirect('/write')
        } else {
		//成功、ホームページへ
          res.redirect('/')
        }
      })
    })
  }
})

正しく入っているかどうか、Mongo shellで確認するか、MongoDB compassで確認する。

記事表示(ホームページ)

まずroutes/index.jsの"/"へ飛ぶ時の処理を編集する。
時間を扱いたいので、gitbashでtextapp内においてmomentをインストール。
npm install moment -S
公式ドキュメントはこちら

routes/index.js
/* ホームページ*/
router.get('/', function(req, res, next) {
  var username = req.session.username;

  //データベース接続
  model.connect(function(db) {
    // すべての記事を調べる。
    db.collection('articles').find().toArray(function(err, docs) {
      var list = docs;
	//ここでマップを使って、時間を表しているidを日付形式に変換し、timeと名付ける。
     list.map(function(ele, index) {
        ele['time'] = moment(ele.id).format('YYYY-MM-DD HH:mm:ss')
     res.render('index', { username: username, data: list });    
    })
  })

});

次に記事表示画面を作る。取得されたitemの中に必要な情報が入っている。

views/index.ejs
<% list.map(function(item,index){ %>
        <div class="row">
          <span><%= index+1 %></span>
          <span><%= item.username %></span>
          <span><%= item.title %></span>
			<!-- 定義したtimeを使用している。-->
          <span><%= item.time %></span>
          <span>
            <a href="">編集</a>
            <a href="">削除</a>
          </span>
        </div>
      <% }) %>

記事リストページ分け

記事が多い場合、ページ分けして1ページに何個記事表示できるかを設定。
routes/index.jsやviews/index.jsを編集する。

routes/index.js
/* ホームページ*/
router.get('/', function(req, res, next) {
  var username = req.session.username;
  //フロント側で今は何ページ目にいるかデータを受け取る、なければ1
  var page = req.query.page || 1;
  var data = {
    total: 0,//トータル何ページ必要か
    curPage: page,//現在あるページ数
    list:[]//今表示しているページの記事リスト
  }
  var pageSize = 2;//表示しているページに記事何個ひょうじするか

  model.connect(function(db) {
    // すべての記事を調べる。
    db.collection('articles').find().toArray(function(err, docs) {
      //全部の記事数÷1ぺ時に表示する記事数=トータル何ページ必要か
      //Math.ceil() 関数は、引数として与えた数以上の最小の整数を返す
      data.total = Math.ceil(docs.length / pageSize)
      // 今のページの記事リストを調べる。
      model.connect(function(db) {
        // sort()  limit()  skip()
        //降順で、一回に調べる記事数を限定(2)、スキップする個数(1ページ目なら(1-1)*2で0,スキップせず最初から2つ調べる)
        db.collection('articles').find().sort({_id: -1}).limit(pageSize).skip((page-1)*pageSize).toArray(function(err, docs2) {
          //時間変換
	      docs2.map(function(ele, index) {
            ele['time'] = moment(ele.id).format('YYYY-MM-DD HH:mm:ss')
          })
          data.list = docs2
          res.render('index', { username: username, data: data });
        })
      })
    })
  })

});

記事を分ける際、必要な情報は、

  1. total: トータルで何ページ必要か
  2. cuiPage: 現在あるページ数 (ここでは必要ないが後で削除機能のために使う)
  3. list: 今表示しているページの記事のリスト
  4. page: フロント側で今は何ページ目にいるかデータ
  5. pageSize: 1ページに表示したい記事数

であり、取得した記事数によって、
total =記事数÷pageSize(小数の場合大き目の整数に変換)

views/index.js
<body>
    <%- include('bar', {username}) %>

    <div class="list">
      <!-- ページ分け、何ページ目 -->
      <div class="pages">
        <% for(let i=1; i<=data.total; i++) { %>
		<!-- ここでクリックすると今いる何ページ目かが"/"にわたり、そのページ内容を表示したい-->
          <a class="pages_style" href="/?page=<%= i %>"><%= i %></a>
        <% } %>
      </div>

      <div class="row">
        <span>順番</span>
        <span>作者</span>
        <span>タイトル</span>
        <span>公開日</span>
        <span>
          <i class="fa-solid fa-pen-to-square"></i>
        </span>

      </div>

      <!-- 記事リスト -->
      <% data.list.map(function(item,index){ %>
        <div class="row">
          <span><%= index+1 %></span>
          <span><%= item.username %></span>
          <span><%= item.title %></span>
          <span><%= item.time %></span>
          <span>
            <a href="">編集</a>
            <a href="">削除</a>
          </span>
        </div>
      <% }) %>

    </div>

</body>

for文でdata.totalを使用し、実際のページ数を表示。また、ページボタンに今のいるページ何番目かを意味するpageを"/"に渡しているので、この情報を使って表示すべき記事を選出し、data.listに格納される。

記事削除

views/index.jsの削除のところを下のように直す。
削除するために必要な情報は、記事のidはもちろん、もう1つは今何ページ目にいるかという、pageが必要。なお、?id=<%=item.id%>&page=<%=data.curPage%>のところ、
?の後ろにはURLパラメータが来ていて、この値を行先に渡すイメージ。

views/index.js
<a href="/article/delete?id=<%=item.id%>&page=<%=data.curPage%>">削除</a>

routes/article.jsも編集。

routes/article.js

// 記事削除
router.get('/delete', function(req, res, next) {
 var id = parseInt(req.query.id)
 var page = req.query.page
 //接続
 model.connect(function(db) {
   //idで調べる
   db.collection('articles').deleteOne({id: id}, function(err, ret) {
     if (err) {
       console.log('削除失敗');
     } else {
       console.log('削除成功');
     }
     //リダイレクトする
     res.redirect('/?page='+page);
   })
 })
})

なおこの状態では、たとえ削除したページに記事が1つもなくても、表示できるので、不適切である。これを直すため、routes/index.jsに判定条件を追加する。

routes/index.js
/* ホームページ*/
router.get('/', function(req, res, next) {
  var username = req.session.username;
  //フロント側で今は何ページ目にいるかデータを受け取る、なければ1
  var page = req.query.page || 1;
  var data = {
    total: 0,//トータル何ページ必要か
    curPage: page,//現在あるページ数
    list:[]//今表示しているページの記事リスト
  }
  var pageSize = 2;//表示しているページに記事何個ひょうじするか

  model.connect(function(db) {
    // すべての記事を調べる。
    db.collection('articles').find().toArray(function(err, docs) {
      //全部の記事数÷1ぺ時に表示する記事数=トータル何ページ必要か
      //Math.ceil() 関数は、引数として与えた数以上の最小の整数を返す
      data.total = Math.ceil(docs.length / pageSize)
      // 今のページの記事リストを調べる。
      model.connect(function(db) {
        // sort()  limit()  skip()
        //降順で、一回に調べる記事数を限定(2)、スキップする個数(1ページ目なら(1-1)*2で0,スキップせず最初から2つ調べる)
        db.collection('articles').find().sort({_id: -1}).limit(pageSize).skip((page-1)*pageSize).toArray(function(err, docs2) {
			//記事がなければ
           if (docs2.length == 0) {
             //前のページに戻る、最小1
             res.redirect('/?page='+((page-1) || 1))
           } else {
		   //記事があれば、時間の処理をする。
            docs2.map(function(ele, index) {
              ele['time'] = moment(ele.id).format('YYYY-MM-DD HH:mm:ss')
            })
            data.list = docs2
           }
          res.render('index', { username: username, data: data });
        })
      })
    })
  })

});

記事編集

writeへ飛ぶ。同じくidとcurPageが必要。

views/index.js
<a href="/write?id=<%=item.id%>&page=<%=data.curPage%>">編集</a>

router.get('/write', function(req, res, next) {})は以下のようになる。

routes/index.js
// 投稿&編集ページ
router.get('/write', function(req, res, next) {
  var username = req.session.username || ''
  var id = parseInt(req.query.id)
  var page = req.query.page
  var item = {
    title: '',
    content: ''
  }
  if (id) {  // idがあれば編集
    model.connect(function(db) {
      db.collection('articles').findOne({id: id} , function(err, docs) {
        if (err) {
          console.log('失败')
        } else {
          item = docs
          item['page'] = page
          res.render('write', {username: username, item: item})
        }
      })
    })
  } else {  // idがなければ追加
    res.render('write', {username: username, item: item})
  }
})

また、write画面も編集する。

views/write.ejs
<body>

  <%- include('bar', {username: username}) %>
  <div class="article">
    <form action="/article/add" method="post">

      <input type="hidden" name="id" value="<%- item.id %>">
      <input type="hidden" name="page" value="<%- item.page %>">
      <input type="text" name="title" placeholder="タイトル" value="<%= item.title %>">
      <textarea name="content" class="xheditor" id="mytextarea"><%- item.content %></textarea>
      <% if (item.id) { %>
        <input type="submit" value="編集">
      <%} else {%>
        <input type="submit" value="投稿">
      <%}%>
    </form>
  </div>

</body>

ボタンもif文でidがあれば編集、なければ投稿と表示。
また、編集をするためにpageとidを渡さなければならない。
編集成功したらpageに飛びたいので。

<input type="hidden" name="id" value="<%- item.id %>">
<input type="hidden" name="page" value="<%- item.page %>">

hidenを使って見えなくし、配置する。
/article/addのところも編集する。同じくidで編集する。

routes/article.js

// 追加、編集
router.post('/add', function(req, res, next) {
  var id = parseInt(req.body.id)
  if (id) {  //編集
    var page = req.body.page
    var title = req.body.title
    var content = req.body.content
    model.connect(function(db) {
      db.collection('articles').updateOne({id: id}, {$set: {
        title: title,
        content: content
      }}, function(err, ret) {
        if (err) {
          console.log('編集失敗', err)
        } else {
          console.log('編集成功')
          res.redirect('/?page='+page)
        }
      })
    })
  } else {   //追加
    var data = {
      title: req.body.title,
      content: req.body.content,
      username: req.session.username,
      id: Date.now()
    }
    model.connect(function(db) {
      db.collection('articles').insertOne(data, function(err, ret) {
        if(err) {
          console.log('失败', err)
          res.redirect('/write')
        } else {
          res.redirect('/')
        }
      })
    })
  }
})

記事表示

タイトルのところに行先を設定。

view/index.ejs
<span><a href="/detail?id=<%=item.id%>"><%= item.title %></a></span>

記事表示ページを作成する。

view/detail.ejs
<!DOCTYPE html>
<html lang="ja">
<head>

  <title><%=item.title%></title>
  <%- include head %>
</head>
<body>

  <%- include('bar', {username: username}) %>
  <div class="detail">
    <div class="title"><%=item.title%></div>
    <div class='desc'>
      <span>作者:<%=item.username%></span>
      <span>投稿日:<%=item.time%></span>
    </div>
    <div class="content"><%-item.content%></div>
  </div>

</body>
</html>

routes/index.jsにもコードを追加する。

routes/index.js
// 記事詳細ページ
router.get('/detail', function(req, res, next) {
  var id = parseInt(req.query.id)
  var username = req.session.username || ''
  model.connect(function(db) {
    db.collection('articles').findOne({id: id}, function(err, docs) {
      if (err) {
        console.log('失败', err)
      } else {
        var item = docs
        item['time'] = moment(item.id).format('YYYY-MM-DD HH:mm:ss')
        res.render('detail', {item: item, username: username})
      }
    })
  })
})

これでタイトルを押したら、該当記事の詳細を確認できる。

その他

このままだと、実はほかの人でも他人の記事を編集&削除できる。これはまずいので、変えていく。やり方は簡単、以下のように修正すればいい。

views/index.ejs
<!-- 記事リスト -->
      <% data.list.map(function(item,index){ %>
        <div class="row">
          <span><%= index+1 %></span>
          <span><%= item.username %></span>
          <span><a href="/detail?id=<%=item.id%>"><%= item.title %></a></span>
          <span><%= item.time %></span>
          <span>
            <% if (item.username===username) { %>
              <a href="/write?id=<%=item.id%>&page=<%=data.curPage%>">編集</a>
              <a href="/article/delete?id=<%=item.id%>&page=<%=data.curPage%>">削除</a>
            <%} else {%>
              <span class="grey">編集</span>
              <span class="grey">編集</span>
            <%}%>
          </span>
        </div>
      <% }) %>

ここではif文を使って判定している。usernameはsessionから取得した今ログイン中のユーザ名、item.usernameとはそれぞれの記事の作者名である。一致しなければ編集&削除ができない。

デプロイ(公開)

まだ未公開ですが、EC2で公開したいと思っていて、以下の記事参考になりそうなのでいったんメモしておきます。

ec2
【AWS】EC2とは?概要から使い方までざっくり解説(実践あり)

node.js
[AWS] 手順に沿った画像付きでAmazon EC2にNode.js, Express環境の構築する
チュートリアル: Amazon EC2 インスタンスでの Node.js のセットアップ
AWS EC2でNodeを動作させる

mongodb
MongoDB CommunityEditionをAmazonLinuxにインストールします


途中までやっていた。コマンドメモ↓↓

$ ssh -i textapp-key.pem ec2-user@18.183.176.255

$ ssh -i textapp-key.pem ec2-user@18.183.176.255
$ sudo yum update
$ sudo yum install git gcc-c++ make openssl-devel
$ nvm install v16.14.0
$ nvm alias default v16.14.0
$ npm install express-generator -g

npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)

$ npm update -g mkdirp
$ npm install -g npm@8.5.3

$ git clone https://github.com/akikisai/Article-management-app.git
$ ls -a
$ cd Article-management-app/
$ npm install


$ grep ^NAME /etc/*release
/etc/os-release:NAME="Amazon Linux"

$ cd /etc/yum.repos.d
$ sudo touch mongo-org-5.0.repo
$ sudo su
# vi mongo-org-5.0.repo


[mongodb-org-5.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/amazon/2/mongodb-org/5.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-5.0.asc

:wq!

# sudo yum install -y mongodb-org

exclude=mongodb-org,mongodb-org-database,mongodb-org-server,mongodb-org-shell,mongodb-org-mongos,mongodb-org-tools

# ps --no-headers -o comm 1
systemd

# sudo systemctl start mongod

# sudo systemctl status mongod
 Active: active (running) since Sat 2022-03-05 12:30:03 UTC; 29s ago

$ mongosh
>use textapp

>db.createCollection('users');
>db.createCollection('articles');


$ cd Article-management-app/
node ./bin/www

躓いた。。もう少しいろいろ試したい。


問題点

今回は機能を作ってみるだけにしましたが、まだしっかりとしたエラー処理を書いていなく(ユーザ名判定など) マークダウンにも対応させたいと思っていて、全然新しい機能を追加する余地はあるが、今回は一旦締め切りにする。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?