Compare commits
8 Commits
3f8e8479d0
...
0c05f3d357
Author | SHA1 | Date | |
---|---|---|---|
|
0c05f3d357 | ||
|
98adc24389 | ||
24ae647142 | |||
df2afd36e2 | |||
26524549c1 | |||
8db4e1ef54 | |||
|
284be0ecdd | ||
10091f28ff |
24
.eslintrc
Normal file
24
.eslintrc
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"extends": ["eslint:recommended"],
|
||||||
|
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 8
|
||||||
|
},
|
||||||
|
|
||||||
|
"globals": {
|
||||||
|
"after": "readable"
|
||||||
|
},
|
||||||
|
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"rules": {
|
||||||
|
"quotes": ["warn", "single"],
|
||||||
|
"semi": ["error", "never"],
|
||||||
|
"indent": ["warn", 2],
|
||||||
|
"no-unused-vars": ["warn"],
|
||||||
|
"no-console": ["off"],
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,37 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router()
|
||||||
const { Article } = require('../db/models');
|
const { Article } = require('../db/models')
|
||||||
const buildPage = require('./buildPage');
|
const buildPage = require('../scripts/buildPage')
|
||||||
|
const buildTable = require('../scripts/buildArticleTable')
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router
|
||||||
|
|
||||||
router.get('/', async (req, res, next) => {
|
router.get('/', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const articles = await Article.findAll();
|
const articles = await Article.findAll()
|
||||||
const tbl = articles
|
res.status(201).send(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) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
router.get('/:id', async (req, res, next) => {
|
router.get('/:id', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const article = await Article.findById(req.params.id);
|
const article = await Article.findById(req.params.id)
|
||||||
console.log(article.title);
|
console.log(article.title)
|
||||||
console.log(`by: ${article.author}`);
|
console.log(`by: ${article.author}`)
|
||||||
console.log(article.text);
|
console.log(article.text)
|
||||||
res.status(201).send(article);
|
res.status(201).send(article)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
router.post('/', async (req, res, next) => {
|
router.post('/', async (req, res, next) => {
|
||||||
const body = req.body;
|
const body = req.body
|
||||||
try {
|
try {
|
||||||
const article = await Article.create(body);
|
const article = await Article.create(body)
|
||||||
res.redirect(article.id);
|
res.redirect(article.id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
module.exports = listString =>
|
|
||||||
`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h2>Haxor Newz</h2>
|
|
||||||
<h3>"uber l337"</h3>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Link</th>
|
|
||||||
</thead>
|
|
||||||
${listString}
|
|
||||||
<tfoot>
|
|
||||||
ATTACK!
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
<footer>Ⓐ anarchy planet</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
`;
|
|
25
api/comments.js
Normal file
25
api/comments.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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']
|
||||||
|
})
|
||||||
|
} 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('/items', require('./items'))
|
||||||
router.use('/articles', require('./articles'));
|
router.use('/articles', require('./articles'))
|
||||||
|
router.use('/comments', require('./comments'))
|
||||||
|
|
||||||
router.get('/', async (req, res, next) => {
|
router.get('/', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
res.send('/n-------/nHello from Express!/n--------/n');
|
res.send('/n-------/nHello from Express!/n--------/n')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
const error = new Error('Not Found!!!!!!!');
|
const error = new Error('Not Found!!!!!!!')
|
||||||
error.status = 404;
|
error.status = 404
|
||||||
next(error);
|
next(error)
|
||||||
});
|
})
|
||||||
|
22
api/items.js
22
api/items.js
@ -1,24 +1,24 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router()
|
||||||
const { Item } = require('../db/models');
|
const { Item } = require('../db/models')
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router
|
||||||
|
|
||||||
router.get('/', async (req, res, next) => {
|
router.get('/', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const items = await Item.findAll();
|
const items = await Item.findAll()
|
||||||
|
|
||||||
res.status(201).send(items);
|
res.status(201).send(items)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
router.post('/', async (req, res, next) => {
|
router.post('/', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const item = await Item.create(req.body);
|
const item = await Item.create(req.body)
|
||||||
|
|
||||||
res.status(201).json(item);
|
res.status(201).json(item)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router()
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router
|
||||||
|
|
||||||
// what you will hit at /api/robots
|
// what you will hit at /api/robots
|
||||||
router.get('/', async (req, res, next) => {
|
router.get('/', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
console.log('NSA was here');
|
console.log('NSA was here')
|
||||||
res.status(201).send('FUCK YOU NSA\n');
|
res.status(201).send('FUCK YOU NSA\n')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
4
ascii.js
4
ascii.js
@ -21,6 +21,6 @@ const ascii = String.raw`
|
|||||||
____^/\___^--____/\____O______________/\/\---/\___________--
|
____^/\___^--____/\____O______________/\/\---/\___________--
|
||||||
/\^ ^ ^ ^ ^^ ^ '\ ^ ^
|
/\^ ^ ^ ^ ^^ ^ '\ ^ ^
|
||||||
-- - -- - - --- __ ^
|
-- - -- - - --- __ ^
|
||||||
-- __ ___-- ^ ^`;
|
-- __ ___-- ^ ^`
|
||||||
|
|
||||||
module.exports = ascii;
|
module.exports = ascii
|
||||||
|
22
db/db.js
22
db/db.js
@ -1,26 +1,24 @@
|
|||||||
const Sequelize = require('sequelize');
|
const Sequelize = require('sequelize')
|
||||||
const pkg = require('../package.json');
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
const databaseName =
|
const databaseName = pkg.name + (process.env.NODE_ENV === 'test' ? '-test' : '')
|
||||||
pkg.name + (process.env.NODE_ENV === 'test' ? '-test' : '');
|
|
||||||
|
|
||||||
const createDB = () => {
|
const createDB = () => {
|
||||||
const db = new Sequelize(
|
const db = new Sequelize(
|
||||||
process.env.DATABASE_URL || `postgres://localhost:5432/${databaseName}`,
|
process.env.DATABASE_URL || `postgres://localhost:5432/${databaseName}`,
|
||||||
{
|
{
|
||||||
logging: false,
|
operatorsAliases: false,
|
||||||
operatorsAliases: false
|
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
return db;
|
return db
|
||||||
};
|
}
|
||||||
|
|
||||||
const db = createDB();
|
const db = createDB()
|
||||||
|
|
||||||
module.exports = db;
|
module.exports = db
|
||||||
|
|
||||||
// This is a global Mocha hook used for resource cleanup.
|
// This is a global Mocha hook used for resource cleanup.
|
||||||
// Otherwise, Mocha v4+ does not exit after tests.
|
// Otherwise, Mocha v4+ does not exit after tests.
|
||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
after('close database connection', () => db.close());
|
after('close database connection', () => db.close())
|
||||||
}
|
}
|
||||||
|
20
db/index.js
20
db/index.js
@ -1,6 +1,20 @@
|
|||||||
const db = require('./db');
|
const db = require('./db')
|
||||||
|
|
||||||
|
// 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!
|
||||||
|
// 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
|
||||||
|
// regiistration required
|
||||||
|
// 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
|
||||||
|
// votes: https://irc.anarchyplanet.org/git/notnull/hacker-news-cli/issues/5
|
||||||
|
|
||||||
// register models
|
// register models
|
||||||
require('./models');
|
require('./models')
|
||||||
|
|
||||||
module.exports = db;
|
module.exports = db
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const Sequelize = require('sequelize');
|
const Sequelize = require('sequelize')
|
||||||
const db = require('../db');
|
const db = require('../db')
|
||||||
|
|
||||||
const Article = db.define('articles', {
|
const Article = db.define('articles', {
|
||||||
title: {
|
title: {
|
||||||
@ -9,6 +9,6 @@ const Article = db.define('articles', {
|
|||||||
link: {
|
link: {
|
||||||
type: Sequelize.STRING
|
type: Sequelize.STRING
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
module.exports = Article;
|
module.exports = Article
|
||||||
|
15
db/models/comment.js
Normal file
15
db/models/comment.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const Sequelize = require('sequelize')
|
||||||
|
const db = require('../db')
|
||||||
|
|
||||||
|
const Comment = db.define('comment', {
|
||||||
|
title: {
|
||||||
|
type: Sequelize.STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
content: {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = Comment
|
@ -1,4 +1,19 @@
|
|||||||
const Item = require('./item');
|
const Item = require('./item')
|
||||||
const Article = require('./article');
|
const Article = require('./article')
|
||||||
|
const Comment = require('./comment')
|
||||||
|
const User = require('./user')
|
||||||
|
|
||||||
module.exports = { Item, Article };
|
Article.hasMany(Comment) // allows for addComment
|
||||||
|
Comment.belongsTo(Article)
|
||||||
|
|
||||||
|
User.hasMany(Article)
|
||||||
|
Article.belongsTo(User) // allows for setUser
|
||||||
|
|
||||||
|
User.hasMany(Comment)
|
||||||
|
Comment.belongsTo(User)
|
||||||
|
|
||||||
|
// i understand more now: parent must be set instead of reply.
|
||||||
|
|
||||||
|
Comment.belongsTo(Comment, { as: 'parent' }) // setParent
|
||||||
|
|
||||||
|
module.exports = { Item, Article, Comment, User }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const Sequelize = require('sequelize');
|
const Sequelize = require('sequelize')
|
||||||
const db = require('../db');
|
const db = require('../db')
|
||||||
|
|
||||||
const Item = db.define('items', {
|
const Item = db.define('items', {
|
||||||
name: {
|
name: {
|
||||||
type: Sequelize.STRING,
|
type: Sequelize.STRING,
|
||||||
allowNull: false
|
allowNull: false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
module.exports = Item;
|
module.exports = Item
|
||||||
|
84
db/models/user.js
Normal file
84
db/models/user.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
note: this currently isn't exported because I will
|
||||||
|
have to investigate the salting etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const Sequelize = require('sequelize')
|
||||||
|
const db = require('../db')
|
||||||
|
|
||||||
|
const User = db.define('user', {
|
||||||
|
email: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
firstName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
unique: true,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
defaultValue: 'novatore.jpg',
|
||||||
|
},
|
||||||
|
|
||||||
|
password: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
// Making `.password` act like a func hides it when serializing to JSON.
|
||||||
|
// This is a hack to get around Sequelize's lack of a "private" option.
|
||||||
|
get() {
|
||||||
|
return () => this.getDataValue('password')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
salt: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
// Making `.salt` act like a function hides it when serializing to JSON.
|
||||||
|
// This is a hack to get around Sequelize's lack of a "private" option.
|
||||||
|
get() {
|
||||||
|
return () => this.getDataValue('salt')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = User
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instanceMethods
|
||||||
|
*/
|
||||||
|
User.prototype.correctPassword = function(candidatePwd) {
|
||||||
|
return User.encryptPassword(candidatePwd, this.salt()) === this.password()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* classMethods
|
||||||
|
*/
|
||||||
|
User.generateSalt = function() {
|
||||||
|
return crypto.randomBytes(16).toString('base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
User.encryptPassword = function(plainText, salt) {
|
||||||
|
return crypto
|
||||||
|
.createHash('RSA-SHA256')
|
||||||
|
.update(plainText)
|
||||||
|
.update(salt)
|
||||||
|
.digest('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hooks
|
||||||
|
*/
|
||||||
|
const setSaltAndPassword = user => {
|
||||||
|
if (user.changed('password')) {
|
||||||
|
user.salt = User.generateSalt()
|
||||||
|
user.password = User.encryptPassword(user.password(), user.salt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
User.beforeCreate(setSaltAndPassword)
|
||||||
|
User.beforeUpdate(setSaltAndPassword)
|
13
db/models/votes.js
Normal file
13
db/models/votes.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const Sequelize = require('sequelize')
|
||||||
|
const db = require('../db')
|
||||||
|
|
||||||
|
// votes should be up or down, could either do 'up/'down' or 1/-1
|
||||||
|
const Vote = db.define('votes', {
|
||||||
|
valence: {
|
||||||
|
type: Sequelize.ENUM,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = Vote
|
68
db/seed.js
68
db/seed.js
@ -1,27 +1,63 @@
|
|||||||
const db = require('../db');
|
const db = require('../db')
|
||||||
const { Article } = require('./models');
|
const { Article, Comment, User } = require('./models')
|
||||||
|
|
||||||
const testArticle = {
|
const testArticle = {
|
||||||
title: 'read desert',
|
title: 'read desert',
|
||||||
link: 'https://readdesert.org'
|
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!',
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
username: 'nn',
|
||||||
|
}
|
||||||
|
|
||||||
console.log(Article);
|
|
||||||
async function runSeed() {
|
async function runSeed() {
|
||||||
await db.sync({ force: true });
|
await db.sync({ force: true })
|
||||||
console.log('db synced!');
|
console.log('db synced!')
|
||||||
console.log('seeding...');
|
console.log('seeding...')
|
||||||
try {
|
try {
|
||||||
await Article.create(testArticle);
|
const article = await Article.create(testArticle)
|
||||||
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) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
process.exitCode = 1;
|
process.exitCode = 1
|
||||||
} finally {
|
} finally {
|
||||||
console.log('closing db connection');
|
console.log('closing db connection')
|
||||||
await db.close();
|
await db.close()
|
||||||
console.log('db connection closed');
|
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
|
// 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 TOP_STORIES = 'topstories'
|
||||||
const ITEM = 'item';
|
const ITEM = 'item'
|
||||||
|
|
||||||
function hnFetch(type, id = '') {
|
function hnFetch(type, id = '') {
|
||||||
const url = id
|
const url = id ? `${HN_PREFIX}${type}/${id}.json` : `${HN_PREFIX}${type}.json`
|
||||||
? `${HN_PREFIX}${type}/${id}.json`
|
|
||||||
: `${HN_PREFIX}${type}.json`;
|
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!isStatusOk(res.status)) {
|
if (!isStatusOk(res.status)) {
|
||||||
throw res;
|
throw res
|
||||||
}
|
}
|
||||||
return res.json();
|
return res.json()
|
||||||
})
|
})
|
||||||
.then(res => res)
|
.then(res => res)
|
||||||
.catch(error => console.error(error));
|
.catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStatusOk(statusCode) {
|
function isStatusOk(statusCode) {
|
||||||
return statusCode === 200 || statusCode === 304;
|
return statusCode === 200 || statusCode === 304
|
||||||
}
|
}
|
||||||
async function main() {
|
async function main() {
|
||||||
const storyIds = await hnFetch(TOP_STORIES);
|
const storyIds = await hnFetch(TOP_STORIES)
|
||||||
const stories = await Promise.all(
|
const stories = await Promise.all(
|
||||||
storyIds.slice(0, 20).map(storyId => hnFetch(ITEM, storyId))
|
storyIds.slice(0, 20).map(storyId => hnFetch(ITEM, storyId))
|
||||||
);
|
)
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
stories.map(story => {
|
stories.map(story => {
|
||||||
delete story.kids;
|
delete story.kids
|
||||||
return story;
|
return story
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main()
|
||||||
|
67
index.js
67
index.js
@ -1,41 +1,52 @@
|
|||||||
const express = require('express');
|
const express = require('express')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const morgan = require('morgan');
|
const morgan = require('morgan')
|
||||||
const ascii = require('./ascii');
|
const ascii = require('./ascii')
|
||||||
const db = require('./db');
|
const db = require('./db')
|
||||||
|
const axios = require('axios')
|
||||||
const app = express();
|
const app = express()
|
||||||
const port = process.env.PORT || 1337;
|
const port = process.env.PORT || 1337
|
||||||
|
const buildPage = require('./scripts/buildPage')
|
||||||
db.authenticate()
|
db.authenticate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('Connection has been established successfully.');
|
console.log('Connection has been established successfully.')
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Unable to connect to the database:', err);
|
console.error('Unable to connect to the database:', err)
|
||||||
});
|
})
|
||||||
|
|
||||||
app.use(morgan('tiny'));
|
app.use(morgan('tiny'))
|
||||||
|
|
||||||
// body parsing middleware
|
// body parsing middleware
|
||||||
app.use(express.json());
|
app.use(express.json())
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }))
|
||||||
app.use(require('body-parser').text());
|
app.use(require('body-parser').text())
|
||||||
app.use('/api', require('./api'));
|
app.use('/api', require('./api'))
|
||||||
|
|
||||||
app.get('*', (req, res) =>
|
app.get('/articles', async (req, res) => {
|
||||||
res.sendFile(path.resolve(__dirname, 'public', 'articles.html'))
|
console.log('path:', app.mountpath)
|
||||||
);
|
try {
|
||||||
|
const { data } = await axios.get(`http://localhost:${port}/api/articles`)
|
||||||
|
console.log(data)
|
||||||
|
const page = buildPage(data)
|
||||||
|
res.send(page)
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('*', (req, res) => res.send('try again.'))
|
||||||
|
|
||||||
// error handling endware
|
// error handling endware
|
||||||
app.use((err, req, res, next) => {
|
app.use((err, req, res, next) => {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
console.error(err.stack);
|
console.error(err.stack)
|
||||||
res.status(err.status || 500).send(err.message || 'Internal server error.');
|
res.status(err.status || 500).send(err.message || 'Internal server error.')
|
||||||
next();
|
next()
|
||||||
});
|
})
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`\n${ascii}\n`);
|
console.log(`\n${ascii}\n`)
|
||||||
console.log(`Doin' haxor stuff on port ${port}`);
|
console.log('mountpath:', app.mountpath)
|
||||||
});
|
console.log(`Doin' haxor stuff on port ${port}`)
|
||||||
|
})
|
||||||
|
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
|
28
scripts/buildPage.js
Normal file
28
scripts/buildPage.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const buildTable = require('./buildArticleTable')
|
||||||
|
|
||||||
|
const buildPage = arr => {
|
||||||
|
const listString = buildTable(arr)
|
||||||
|
return `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>Haxor Newz</h2>
|
||||||
|
<h3>"uber l337"</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Link</th>
|
||||||
|
</thead>
|
||||||
|
${listString}
|
||||||
|
<tfoot>
|
||||||
|
ATTACK!
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
<footer>Ⓐ anarchy planet</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = buildPage
|
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…
x
Reference in New Issue
Block a user