adding a comment backend, experimenting with static html frontend (plus bash)
This commit is contained in:
parent
df2afd36e2
commit
24ae647142
@ -1,43 +1,40 @@
|
||||
const router = require('express').Router();
|
||||
const { Article } = require('../db/models');
|
||||
const buildPage = require('./buildPage');
|
||||
const router = require('express').Router()
|
||||
const { Article } = require('../db/models')
|
||||
const buildPage = require('../scripts/buildPage')
|
||||
const buildTable = require('../scripts/buildArticleTable')
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const articles = await Article.findAll();
|
||||
const tbl = articles
|
||||
.map(
|
||||
article => `<tr><td>${article.title}</td><td>${article.link}</td></tr>`
|
||||
)
|
||||
.join();
|
||||
const page = buildPage(tbl);
|
||||
console.log(page);
|
||||
res.status(201).send(page);
|
||||
const articles = await Article.findAll()
|
||||
const tbl = buildTable(articles)
|
||||
const page = buildPage(tbl)
|
||||
console.log('REEEEEEEEEEEEEEEEEEE\n', page, tbl)
|
||||
res.status(201).send(page)
|
||||
} catch (err) {
|
||||
next(err);
|
||||
next(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.findById(req.params.id);
|
||||
console.log(article.title);
|
||||
console.log(`by: ${article.author}`);
|
||||
console.log(article.text);
|
||||
res.status(201).send(article);
|
||||
const article = await Article.findById(req.params.id)
|
||||
console.log(article.title)
|
||||
console.log(`by: ${article.author}`)
|
||||
console.log(article.text)
|
||||
res.status(201).send(article)
|
||||
} catch (err) {
|
||||
next(err);
|
||||
next(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
router.post('/', async (req, res, next) => {
|
||||
const body = req.body;
|
||||
const body = req.body
|
||||
try {
|
||||
const article = await Article.create(body);
|
||||
res.redirect(article.id);
|
||||
const article = await Article.create(body)
|
||||
res.redirect(article.id)
|
||||
} catch (err) {
|
||||
next(err);
|
||||
next(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
27
api/comments.js
Normal file
27
api/comments.js
Normal file
@ -0,0 +1,27 @@
|
||||
const router = require('express').Router()
|
||||
const { Comment } = require('../db/models')
|
||||
const buildPage = require('../scripts/buildPage')
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const comments = await Comment.findAll({
|
||||
attributes: ['id', 'title', 'content', 'userId', 'parentId'],
|
||||
})
|
||||
const page = buildPage(
|
||||
res.status(201).send()
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', async (req, res, next) => {
|
||||
const newComment = req.body // good sanitization
|
||||
|
||||
try {
|
||||
const comment = await Comment.create(newComment)
|
||||
res.redirect('http://localhost:1337')
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
23
api/index.js
23
api/index.js
@ -1,20 +1,21 @@
|
||||
const router = require('express').Router();
|
||||
const router = require('express').Router()
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
||||
router.use('/items', require('./items'));
|
||||
router.use('/articles', require('./articles'));
|
||||
router.use('/items', require('./items'))
|
||||
router.use('/articles', require('./articles'))
|
||||
router.use('/comments', require('./comments'))
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
res.send('/n-------/nHello from Express!/n--------/n');
|
||||
res.send('/n-------/nHello from Express!/n--------/n')
|
||||
} catch (err) {
|
||||
next(err);
|
||||
next(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
router.use((req, res, next) => {
|
||||
const error = new Error('Not Found!!!!!!!');
|
||||
error.status = 404;
|
||||
next(error);
|
||||
});
|
||||
const error = new Error('Not Found!!!!!!!')
|
||||
error.status = 404
|
||||
next(error)
|
||||
})
|
||||
|
13
api/users.js
Normal file
13
api/users.js
Normal file
@ -0,0 +1,13 @@
|
||||
const router = require('express').Router()
|
||||
const { User } = require('../db/models')
|
||||
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const users = await User.findAll({ include: User })
|
||||
res.status(201).json(users)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
3
db/db.js
3
db/db.js
@ -7,8 +7,7 @@ const createDB = () => {
|
||||
const db = new Sequelize(
|
||||
process.env.DATABASE_URL || `postgres://localhost:5432/${databaseName}`,
|
||||
{
|
||||
logging: false,
|
||||
operatorsAliases: false
|
||||
operatorsAliases: false,
|
||||
}
|
||||
)
|
||||
return db
|
||||
|
22
db/index.js
22
db/index.js
@ -1,28 +1,14 @@
|
||||
const db = require('./db')
|
||||
|
||||
const {Article, Vote, User} = require('./models');
|
||||
// sigh
|
||||
// I am using Sequelize as an ORM for the database.
|
||||
// It has its own syntax for defining the relations
|
||||
// in the database. Something like the following:
|
||||
Article.belongsTo(User)
|
||||
User.hasMany(Article)
|
||||
|
||||
Article.hasMany(Vote)
|
||||
Vote.belongsTo(Article)
|
||||
|
||||
Vote.belongsTo(User)
|
||||
User.hasMany(Vote)
|
||||
|
||||
// the questions are:
|
||||
// 1. how to store votes in the database? as a count on the article? this would be less accurate but ok for the beginning <- I am not a fan of this approach
|
||||
// 2. what votes do we count? (anon, per ip address, cookies?) to build relations between votes we need a concept of users based identification <- yep!
|
||||
// 1. how to store votes in the database? as a count on the article? this would be less accurate but ok for the beginning <- I am not a fan of this approach
|
||||
// 2. what votes do we count? (anon, per ip address, cookies?) to build relations between votes we need a concept of users based identification <- yep!
|
||||
// 3. also downvotes? <- so I think to start we shoudl copy what HN does agreed :)
|
||||
// TODO figure out how HN implements votes (I think non-registered voters can't vote for example)
|
||||
// re: users: I can implement some hacky user auth to start with... or maybe that's something we delegate to rw (totally_not_fb)
|
||||
// hn voting
|
||||
// hn voting
|
||||
// regiistration required
|
||||
// min karma required for downvoting (comments only?)
|
||||
// min karma required for downvoting (comments only?)
|
||||
// so we could start writing tickets at this point ... ? makes sense
|
||||
// view content: https://irc.anarchyplanet.org/git/notnull/hacker-news-cli/issues/4
|
||||
// post content: https://irc.anarchyplanet.org/git/notnull/hacker-news-cli/issues/3
|
||||
|
@ -1,19 +1,19 @@
|
||||
const Item = require('./item')
|
||||
const Article = require('./article')
|
||||
const Comment = require('./comment')
|
||||
//const User = require('./user')
|
||||
const User = require('./user')
|
||||
|
||||
Article.hasMany(Comment) //puts postId on Comment
|
||||
Article.hasMany(Comment) // allows for addComment
|
||||
Comment.belongsTo(Article)
|
||||
|
||||
//User.hasMany(Article) //puts userId on Post, creates instance method 'user.getPosts()'
|
||||
//Article.belongsTo(User) // creates instance method 'post.getUser()''
|
||||
User.hasMany(Article)
|
||||
Article.belongsTo(User) // allows for setUser
|
||||
|
||||
//User.hasMany(Comment) //puts userId on Comment
|
||||
//Comment.belongsTo(User) //puts userId on Comment
|
||||
User.hasMany(Comment)
|
||||
Comment.belongsTo(User)
|
||||
|
||||
// TODO
|
||||
//Comment.belongsTo(Comment, { as: 'parent' })
|
||||
//Comment.hasMany(Comment, { as: { singular: 'reply', plural: 'replies' } })
|
||||
// i understand more now: parent must be set instead of reply.
|
||||
|
||||
module.exports = { Item, Article, Comment }
|
||||
Comment.belongsTo(Comment, { as: 'parent' }) // setParent
|
||||
|
||||
module.exports = { Item, Article, Comment, User }
|
||||
|
@ -11,22 +11,21 @@ const User = db.define('user', {
|
||||
email: {
|
||||
type: Sequelize.STRING,
|
||||
unique: true,
|
||||
allowNull: false
|
||||
},
|
||||
firstName: {
|
||||
type: Sequelize.STRING
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
lastName: {
|
||||
type: Sequelize.STRING
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
username: {
|
||||
type: Sequelize.STRING,
|
||||
unique: true,
|
||||
allowNull: false
|
||||
allowNull: false,
|
||||
},
|
||||
imageUrl: {
|
||||
type: Sequelize.STRING,
|
||||
defaultValue: 'novatore.jpg'
|
||||
defaultValue: 'novatore.jpg',
|
||||
},
|
||||
|
||||
password: {
|
||||
@ -35,7 +34,7 @@ const User = db.define('user', {
|
||||
// This is a hack to get around Sequelize's lack of a "private" option.
|
||||
get() {
|
||||
return () => this.getDataValue('password')
|
||||
}
|
||||
},
|
||||
},
|
||||
salt: {
|
||||
type: Sequelize.STRING,
|
||||
@ -43,11 +42,8 @@ const User = db.define('user', {
|
||||
// This is a hack to get around Sequelize's lack of a "private" option.
|
||||
get() {
|
||||
return () => this.getDataValue('salt')
|
||||
}
|
||||
},
|
||||
},
|
||||
googleId: {
|
||||
type: Sequelize.STRING
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = User
|
||||
|
78
db/seed.js
78
db/seed.js
@ -1,41 +1,63 @@
|
||||
const db = require("../db");
|
||||
const { Article, Comment, User } = require("./models");
|
||||
// WHYYYYYYY
|
||||
const testArticle = {
|
||||
title: "read desert",
|
||||
link: "https://readdesert.org"
|
||||
};
|
||||
const db = require('../db')
|
||||
const { Article, Comment, User } = require('./models')
|
||||
|
||||
const testArticle = {
|
||||
title: 'read desert',
|
||||
link: 'https://readdesert.org',
|
||||
}
|
||||
|
||||
const testArticle2 = {
|
||||
title: 'the best place ever',
|
||||
link: 'https://irc.anarchyplanet.org',
|
||||
}
|
||||
const testComment = {
|
||||
title: "best essay ever",
|
||||
content: "read the sand book already!"
|
||||
};
|
||||
title: 'best essay ever',
|
||||
content: 'read the sand book already!',
|
||||
}
|
||||
|
||||
const testReply = {
|
||||
title: 'u r so dumb',
|
||||
content: 'i hate anews :P',
|
||||
}
|
||||
|
||||
const testReply2 = {
|
||||
title: 'best essay ever',
|
||||
content: 'read the sand book already!',
|
||||
}
|
||||
|
||||
const testUser = {
|
||||
nick: "nn"
|
||||
};
|
||||
username: 'nn',
|
||||
}
|
||||
|
||||
async function runSeed() {
|
||||
await db.sync({ force: true });
|
||||
console.log("db synced!");
|
||||
console.log("seeding...");
|
||||
await db.sync({ force: true })
|
||||
console.log('db synced!')
|
||||
console.log('seeding...')
|
||||
try {
|
||||
const article = await Article.create(testArticle);
|
||||
const article = await Article.create(testArticle)
|
||||
|
||||
const user = await User.create(testUser);
|
||||
const c1 = await Comment.create(testComment);
|
||||
c1.addUser(user);
|
||||
article.addUser(user);
|
||||
article.addComment(c1);
|
||||
console.log("seeded successfully");
|
||||
const user = await User.create(testUser)
|
||||
const c1 = await Comment.create(testComment)
|
||||
const c2 = await Comment.create(testReply)
|
||||
const c3 = await Comment.create(testReply2)
|
||||
await article.setUser(user)
|
||||
await c1.setUser(user)
|
||||
await c2.setUser(user)
|
||||
await article.addComment(c1)
|
||||
|
||||
await c2.setParent(c1)
|
||||
await c3.setParent(c2)
|
||||
// await c2.setParent(c1)
|
||||
|
||||
console.log('seeded successfully')
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
process.exitCode = 1;
|
||||
console.error(err)
|
||||
process.exitCode = 1
|
||||
} finally {
|
||||
console.log("closing db connection");
|
||||
await db.close();
|
||||
console.log("db connection closed");
|
||||
console.log('closing db connection')
|
||||
await db.close()
|
||||
console.log('db connection closed')
|
||||
}
|
||||
}
|
||||
|
||||
runSeed();
|
||||
runSeed()
|
||||
|
36
hn-client.js
36
hn-client.js
@ -1,47 +1,45 @@
|
||||
const fetch = require('node-fetch');
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
// implemented from: https://github.com/HackerNews/API
|
||||
|
||||
const HN_PREFIX = 'https://hacker-news.firebaseio.com/v0/';
|
||||
const HN_PREFIX = 'https://hacker-news.firebaseio.com/v0/'
|
||||
|
||||
const TOP_STORIES = 'topstories';
|
||||
const ITEM = 'item';
|
||||
const TOP_STORIES = 'topstories'
|
||||
const ITEM = 'item'
|
||||
|
||||
function hnFetch(type, id = '') {
|
||||
const url = id
|
||||
? `${HN_PREFIX}${type}/${id}.json`
|
||||
: `${HN_PREFIX}${type}.json`;
|
||||
const url = id ? `${HN_PREFIX}${type}/${id}.json` : `${HN_PREFIX}${type}.json`
|
||||
return fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
if (!isStatusOk(res.status)) {
|
||||
throw res;
|
||||
throw res
|
||||
}
|
||||
return res.json();
|
||||
return res.json()
|
||||
})
|
||||
.then(res => res)
|
||||
.catch(error => console.error(error));
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
|
||||
function isStatusOk(statusCode) {
|
||||
return statusCode === 200 || statusCode === 304;
|
||||
return statusCode === 200 || statusCode === 304
|
||||
}
|
||||
async function main() {
|
||||
const storyIds = await hnFetch(TOP_STORIES);
|
||||
const storyIds = await hnFetch(TOP_STORIES)
|
||||
const stories = await Promise.all(
|
||||
storyIds.slice(0, 20).map(storyId => hnFetch(ITEM, storyId))
|
||||
);
|
||||
)
|
||||
|
||||
console.log(
|
||||
stories.map(story => {
|
||||
delete story.kids;
|
||||
return story;
|
||||
delete story.kids
|
||||
return story
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
main();
|
||||
main()
|
||||
|
5
index.js
5
index.js
@ -22,10 +22,9 @@ app.use(express.json())
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
app.use(require('body-parser').text())
|
||||
app.use('/api', require('./api'))
|
||||
app.use('/articles', require('./api/articles'))
|
||||
|
||||
app.get('*', (req, res) =>
|
||||
res.sendFile(path.resolve(__dirname, 'public', 'articles.html'))
|
||||
)
|
||||
app.get('*', (req, res) => res.send('try again.'))
|
||||
|
||||
// error handling endware
|
||||
app.use((err, req, res, next) => {
|
||||
|
43
public/articles.js
Normal file
43
public/articles.js
Normal file
@ -0,0 +1,43 @@
|
||||
const router = require('express').Router()
|
||||
const { Article } = require('../db/models')
|
||||
const buildPage = require('./buildPage')
|
||||
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const articles = await Article.findAll()
|
||||
const tbl = articles
|
||||
.map(
|
||||
article => `<tr><td>${article.title}</td><td>${article.link}</td></tr>`
|
||||
)
|
||||
.join()
|
||||
const page = buildPage(tbl)
|
||||
console.log(page)
|
||||
res.status(201).send(page)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.findById(req.params.id)
|
||||
console.log(article.title)
|
||||
console.log(`by: ${article.author}`)
|
||||
console.log(article.text)
|
||||
res.status(201).send(article)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', async (req, res, next) => {
|
||||
const body = req.body
|
||||
try {
|
||||
const article = await Article.create(body)
|
||||
res.redirect(article.id)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
21
public/form.html
Normal file
21
public/form.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Haxor Newz</h2>
|
||||
<h3>much l337. very inform.</h3>
|
||||
|
||||
<form action="http://localhost:1337/api/comments" method="POST">
|
||||
title:<br>
|
||||
<input type="text" name="title" value="">
|
||||
<br>
|
||||
content:<br>
|
||||
<textarea rows="4" cols="50" name="content"> </textarea>
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
|
||||
<p> Ⓐ anarchy planet </p>
|
||||
|
||||
</body>
|
||||
</html>
|
11
scripts/buildArticleTable.js
Normal file
11
scripts/buildArticleTable.js
Normal file
@ -0,0 +1,11 @@
|
||||
const buildTable = articles =>
|
||||
articles
|
||||
.map(
|
||||
article =>
|
||||
`<tr><td><a href="${article.link}">${article.title}</a></td><td>${
|
||||
article.content
|
||||
}</td></tr>`
|
||||
)
|
||||
.join()
|
||||
|
||||
module.exports = buildTable
|
@ -19,4 +19,4 @@ module.exports = listString =>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
`;
|
||||
`
|
16
scripts/post.sh
Executable file
16
scripts/post.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# read user input: http://mywiki.wooledge.org/BashFAQ/078
|
||||
URL='http://localhost:1337/api/comments'
|
||||
|
||||
read -p "title: " title
|
||||
read -p "content: " content
|
||||
DATA="{\"title\": \"$title\", \"content\": \"$content\"}"
|
||||
|
||||
echo "Posting $DATA to $URL"
|
||||
curl -H "Content-Type: application/json" -X POST -d "$DATA" "$URL"
|
||||
|
||||
exit 0
|
||||
|
||||
#http://goinbigdata.com/using-curl-for-ad-hoc-testing-of-restful-microservices/
|
||||
|
||||
#https://stackoverflow.com/questions/7172784/how-to-post-json-data-with-curl-from-terminal-commandline-to-test-spring-rest
|
Loading…
Reference in New Issue
Block a user