├── .gitignore ├── keys.js ├── models └── Post.js ├── package.json ├── app.js ├── routes └── post.js └── client ├── index.html └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea -------------------------------------------------------------------------------- /keys.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongoURI: 'mongodb://vladilen:123456@ds245228.mlab.com:45228/fullstack-blog' 3 | } -------------------------------------------------------------------------------- /models/Post.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const Schema = mongoose.Schema 3 | 4 | const postSchema = new Schema({ 5 | title: { 6 | type: String, 7 | required: true 8 | }, 9 | text: { 10 | type: String, 11 | required: true 12 | }, 13 | date: { 14 | type: Date, 15 | default: Date.now 16 | } 17 | }) 18 | 19 | module.exports = mongoose.model('posts', postSchema) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fullstack-blog", 3 | "version": "1.0.0", 4 | "description": "simple blog", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js" 9 | }, 10 | "keywords": [ 11 | "blog", 12 | "nodejs", 13 | "fullstack" 14 | ], 15 | "author": "Vladilen Minin", 16 | "license": "ISC", 17 | "dependencies": { 18 | "body-parser": "^1.18.3", 19 | "express": "^4.16.3", 20 | "mongoose": "^5.1.2" 21 | }, 22 | "devDependencies": { 23 | "nodemon": "^1.17.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const mongoose = require('mongoose') 3 | const bodyParser = require('body-parser') 4 | const path = require('path') 5 | const postRouter = require('./routes/post') 6 | const keys = require('./keys') 7 | 8 | const port = process.env.PORT || 5000 9 | const clientPath = path.join(__dirname, 'client') 10 | 11 | mongoose.connect(keys.mongoURI) 12 | .then(() => console.log('MongoDB connected.')) 13 | .catch(err => console.error(err)) 14 | 15 | const app = express() 16 | app.use(bodyParser.json()) 17 | app.use('/api/post', postRouter) 18 | app.use(express.static(clientPath)) 19 | 20 | app.listen(port, () => { 21 | console.log(`Server has been started on port ${port}`) 22 | }) 23 | 24 | -------------------------------------------------------------------------------- /routes/post.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const Post = require('../models/Post') 4 | 5 | // http://localhost:5000/api/post (GET) 6 | router.get('/', async (req, res) => { 7 | const posts = await Post.find({}) 8 | res.status(200).json(posts) 9 | }) 10 | 11 | // http://localhost:5000/api/post (POST) 12 | router.post('/', async (req, res) => { 13 | const postData = { 14 | title: req.body.title, 15 | text: req.body.text 16 | } 17 | 18 | const post = new Post(postData) 19 | 20 | await post.save() 21 | res.status(201).json(post) 22 | }) 23 | 24 | // http://localhost:5000/api/post/23 (DELETE) 25 | router.delete('/:postId', async (req, res) => { 26 | await Post.remove({_id: req.params.postId}) 27 | res.status(200).json({ 28 | message: 'Удалено' 29 | }) 30 | }) 31 | 32 | 33 | module.exports = router -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | Fullstack blog 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 | 39 | add 40 | 41 |
42 | 43 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | const card = post => { 2 | return ` 3 |
4 |
5 | ${post.title} 6 |

${post.text}

7 | ${new Date(post.date).toLocaleDateString()} 8 |
9 |
10 | 13 |
14 |
15 | ` 16 | } 17 | 18 | let posts = [] 19 | let modal 20 | const BASE_URL = '/api/post' 21 | 22 | class PostApi { 23 | static fetch() { 24 | return fetch(BASE_URL, {method: 'get'}).then(res => res.json()) 25 | } 26 | 27 | static create(post) { 28 | return fetch(BASE_URL, { 29 | method: 'post', 30 | body: JSON.stringify(post), 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json' 34 | } 35 | }).then(res => res.json()) 36 | } 37 | 38 | static remove(id) { 39 | return fetch(`${BASE_URL}/${id}`, { 40 | method: 'delete' 41 | }).then(res => res.json()) 42 | } 43 | } 44 | 45 | document.addEventListener('DOMContentLoaded', () => { 46 | PostApi.fetch().then(backendPosts => { 47 | posts = backendPosts.concat() 48 | renderPosts(posts) 49 | }) 50 | 51 | modal = M.Modal.init(document.querySelector('.modal')) 52 | document.querySelector('#createPost').addEventListener('click', onCreatePost) 53 | document.querySelector('#posts').addEventListener('click', onDeletePost) 54 | }) 55 | 56 | function renderPosts(_posts = []) { 57 | const $posts = document.querySelector('#posts') 58 | 59 | if (_posts.length > 0) { 60 | $posts.innerHTML = _posts.map(post => card(post)).join(' ') 61 | } else { 62 | $posts.innerHTML = `
Постов пока нет
` 63 | } 64 | } 65 | 66 | 67 | function onCreatePost() { 68 | const $title = document.querySelector('#title') 69 | const $text = document.querySelector('#text') 70 | 71 | if ($title.value && $text.value) { 72 | const newPost = { 73 | title: $title.value, 74 | text: $text.value 75 | } 76 | PostApi.create(newPost).then(post => { 77 | posts.push(post) 78 | renderPosts(posts) 79 | }) 80 | modal.close() 81 | $title.value = '' 82 | $text.value = '' 83 | M.updateTextFields() 84 | } 85 | } 86 | 87 | function onDeletePost(event) { 88 | if (event.target.classList.contains('js-remove')) { 89 | const decision = confirm('Вы уверены, что хотите удалить пост?') 90 | 91 | if (decision) { 92 | const id = event.target.getAttribute('data-id') 93 | 94 | PostApi.remove(id).then(() => { 95 | const postIndex = posts.findIndex(post => post._id === id) 96 | posts.splice(postIndex, 1) 97 | renderPosts(posts) 98 | }) 99 | } 100 | } 101 | } --------------------------------------------------------------------------------