news
This commit is contained in:
parent
1a389ec1dd
commit
d32e47d3ff
@ -221,11 +221,6 @@ const API = {
|
||||
return {error: null, user: user}
|
||||
}
|
||||
}
|
||||
},
|
||||
External: {
|
||||
serviceCallback: async function () {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
107
server/api/news.js
Normal file
107
server/api/news.js
Normal file
@ -0,0 +1,107 @@
|
||||
import API from './index'
|
||||
import Models from './models'
|
||||
import config from '../../scripts/load-config'
|
||||
import database from '../../scripts/load-database'
|
||||
|
||||
const perPage = 8
|
||||
|
||||
function slugify (title) {
|
||||
return title.toLowerCase().replace(/\W/g, '-').substring(0, 16)
|
||||
}
|
||||
|
||||
//** ppp - Posts Per Page; dcount - Post Count; page - number of current page
|
||||
function Pagination (ppp, dcount, page) {
|
||||
if (!ppp) ppp = 5
|
||||
if (!dcount) return null
|
||||
|
||||
let pageCount = Math.ceil(dcount / ppp)
|
||||
if (page > pageCount) page = pageCount
|
||||
|
||||
let offset = (page - 1) * ppp
|
||||
|
||||
return {
|
||||
page: page,
|
||||
perPage: ppp,
|
||||
pages: pageCount,
|
||||
offset: offset,
|
||||
total: dcount
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanArticle (entry, shortenContent = false) {
|
||||
let poster = await API.User.get(entry.user_id)
|
||||
let article = {
|
||||
id: entry.id,
|
||||
slug: slugify(entry.title),
|
||||
title: entry.title,
|
||||
content: entry.content,
|
||||
tags: entry.tags.split(','),
|
||||
created_at: entry.created_at,
|
||||
updated_at: entry.updated_at
|
||||
}
|
||||
|
||||
if (poster) {
|
||||
article.author = {
|
||||
id: poster.id,
|
||||
display_name: poster.display_name
|
||||
}
|
||||
}
|
||||
|
||||
if (shortenContent) {
|
||||
article.content = article.content.replace(/(<([^>]+)>)/ig, '').substring(0, 128) + '...'
|
||||
}
|
||||
|
||||
return article
|
||||
}
|
||||
|
||||
const News = {
|
||||
preview: async () => {
|
||||
// Fetch 3 latest stories
|
||||
let news = await Models.News.query().orderBy('created_at', 'desc').limit(3)
|
||||
|
||||
if (!news.length) return []
|
||||
|
||||
let articles = []
|
||||
for (let i in news) {
|
||||
let entry = news[i]
|
||||
articles.push(await cleanArticle(entry, true))
|
||||
}
|
||||
|
||||
return articles
|
||||
},
|
||||
listNews: async (page) => {
|
||||
let count = await Models.News.query().count('id as ids')
|
||||
if (page < 1) page = 1
|
||||
|
||||
if (!count.length || !count[0]['ids'] || isNaN(page)) {
|
||||
return {error: 'No articles found'}
|
||||
}
|
||||
|
||||
count = count[0].ids
|
||||
let paginated = Pagination(perPage, parseInt(count), page)
|
||||
let news = await Models.News.query().orderBy('created_at', 'desc').offset(paginated.offset).limit(perPage)
|
||||
|
||||
let articles = []
|
||||
for (let i in news) {
|
||||
let entry = news[i]
|
||||
|
||||
articles.push(await cleanArticle(entry))
|
||||
}
|
||||
|
||||
return {
|
||||
page: paginated,
|
||||
articles: articles
|
||||
}
|
||||
},
|
||||
article: async (id) => {
|
||||
let article = await Models.News.query().where('id', id)
|
||||
if (!article.length) return {}
|
||||
article = article[0]
|
||||
|
||||
let poster = await API.User.get(article.user_id)
|
||||
|
||||
return await cleanArticle(article)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = News
|
@ -9,7 +9,7 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
|
||||
}
|
||||
|
||||
if (createAllowFuture) {
|
||||
if (!req.body || (typeof req.body['decision']) === 'undefined') {
|
||||
if (!req.body || (typeof req.body['decision']) === undefined) {
|
||||
throw new error.InvalidRequest('No decision parameter passed')
|
||||
} else if (req.body['decision'] === '0') {
|
||||
throw new error.AccessDenied('User denied access to the resource')
|
||||
|
@ -9,7 +9,7 @@ module.exports = async (req, res, client, scope, user, redirectUri, createAllowF
|
||||
}
|
||||
|
||||
if (createAllowFuture) {
|
||||
if (!req.body || (typeof req.body['decision']) === 'undefined') {
|
||||
if (!req.body || (typeof req.body['decision']) === undefined) {
|
||||
throw new error.InvalidRequest('No decision parameter passed')
|
||||
} else if (req.body['decision'] === '0') {
|
||||
throw new error.AccessDenied('User denied access to the resource')
|
||||
|
@ -1,5 +1,3 @@
|
||||
module.exports = function (req, res, client, scope, user) {
|
||||
res.locals.client = client
|
||||
res.locals.scope = scope
|
||||
res.render('authorization')
|
||||
res.render('authorization', { client: client, scope: scope })
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import middleware from './middleware'
|
||||
import controller from './controller'
|
||||
import decision from './controller/decision'
|
||||
import response from './response'
|
||||
import error from './error'
|
||||
import model from './model'
|
||||
|
||||
class OAuth2Provider {
|
||||
@ -10,8 +8,6 @@ class OAuth2Provider {
|
||||
this.bearer = middleware
|
||||
this.controller = controller
|
||||
this.decision = decision
|
||||
this.response = response
|
||||
this.error = error.OAuth2Error
|
||||
this.model = model
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import config from '../../scripts/load-config'
|
||||
import wrap from '../../scripts/asyncRoute'
|
||||
import API from '../api'
|
||||
import APIExtern from '../api/external'
|
||||
import News from '../api/news'
|
||||
|
||||
let router = express.Router()
|
||||
|
||||
@ -206,4 +207,46 @@ router.get('/external/discord/callback', wrap(async (req, res) => {
|
||||
res.redirect(uri)
|
||||
}))
|
||||
|
||||
/* ========
|
||||
* NEWS
|
||||
* ========
|
||||
*/
|
||||
|
||||
// Get page of articles
|
||||
router.get('/news/all/:page', wrap(async (req, res) => {
|
||||
if (!req.params.page || isNaN(parseInt(req.params.page))) {
|
||||
return res.status(400).jsonp({error: 'Invalid page number.'})
|
||||
}
|
||||
|
||||
let page = parseInt(req.params.page)
|
||||
|
||||
let articles = await News.listNews(page)
|
||||
|
||||
res.jsonp(articles)
|
||||
}))
|
||||
|
||||
// Redirect to page one
|
||||
router.get('/news/all/', (req, res) => {
|
||||
res.redirect('/api/news/all/1')
|
||||
})
|
||||
|
||||
// Fetch article
|
||||
router.get('/news/:id', wrap(async (req, res) => {
|
||||
if (!req.params.id || isNaN(parseInt(req.params.id))) {
|
||||
return res.status(400).jsonp({error: 'Invalid ID number.'})
|
||||
}
|
||||
|
||||
let id = parseInt(req.params.id)
|
||||
|
||||
let article = await News.article(id)
|
||||
res.jsonp(article)
|
||||
}))
|
||||
|
||||
// Preview endpoint
|
||||
router.get('/news', wrap(async (req, res) => {
|
||||
let articles = await News.preview()
|
||||
|
||||
res.jsonp(articles)
|
||||
}))
|
||||
|
||||
module.exports = router
|
||||
|
@ -7,6 +7,7 @@ import config from '../../scripts/load-config'
|
||||
import wrap from '../../scripts/asyncRoute'
|
||||
import http from '../../scripts/http'
|
||||
import API from '../api'
|
||||
import News from '../api/news'
|
||||
|
||||
import apiRouter from './api'
|
||||
import oauthRouter from './oauth2'
|
||||
@ -358,6 +359,31 @@ router.get('/docs/:name', (req, res) => {
|
||||
res.render('document', {doc: doc})
|
||||
})
|
||||
|
||||
router.get('/news/:id?-*', wrap(async (req, res) => {
|
||||
let id = parseInt(req.params.id)
|
||||
if (isNaN(id)) {
|
||||
return res.status(404).render('article', {article: null})
|
||||
}
|
||||
|
||||
let article = await News.article(id)
|
||||
if (!article.id) {
|
||||
return res.status(404).render('article', {article: null})
|
||||
}
|
||||
|
||||
res.render('article', {article: article})
|
||||
}))
|
||||
|
||||
router.get('/news/', wrap(async (req, res) => {
|
||||
let page = parseInt(req.query.page)
|
||||
if (isNaN(page)) {
|
||||
page = 1
|
||||
}
|
||||
|
||||
let news = await News.listNews(page)
|
||||
|
||||
res.render('news', {news: news})
|
||||
}))
|
||||
|
||||
/*
|
||||
=========
|
||||
OTHER
|
||||
|
@ -34,6 +34,7 @@ router.post('/introspect', oauth.controller.introspection)
|
||||
router.get('/user', oauth.bearer, wrap(async (req, res) => {
|
||||
let accessToken = req.oauth2.accessToken
|
||||
let user = await uapi.User.get(accessToken.user_id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).jsonp({
|
||||
error: 'No such user'
|
||||
@ -42,14 +43,17 @@ router.get('/user', oauth.bearer, wrap(async (req, res) => {
|
||||
|
||||
let udata = {
|
||||
id: user.id,
|
||||
name: user.display_name,
|
||||
username: user.username,
|
||||
display_name: user.display_name,
|
||||
avatar_file: user.avatar_file
|
||||
}
|
||||
|
||||
// Include Email
|
||||
if (accessToken.scope.indexOf('email') != -1) {
|
||||
udata.email = user.email
|
||||
}
|
||||
|
||||
// Include privilege number
|
||||
if (accessToken.scope.indexOf('privilege') != -1) {
|
||||
udata.privilege = user.nw_privilege
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ window.$ = require('jquery')
|
||||
|
||||
$(document).ready(function () {
|
||||
if (window.location.hash) {
|
||||
let hash = window.location.hash
|
||||
if ($(hash).length) {
|
||||
$(window).scrollTop($(hash).offset().top - $('.navigator').innerHeight() * 2)
|
||||
var locha = window.location.hash
|
||||
if ($(locha).length) {
|
||||
$(window).scrollTop($(locha).offset().top - $('.navigator').innerHeight() * 2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ $(document).ready(function () {
|
||||
if (!$(this.hash).length) return
|
||||
e.preventDefault()
|
||||
|
||||
let dest = 0
|
||||
var dest = 0
|
||||
if ($(this.hash).offset().top > $(document).height() - $(window).height()) {
|
||||
dest = $(document).height() - $(window).height()
|
||||
} else {
|
||||
@ -55,8 +55,8 @@ $(document).ready(function () {
|
||||
|
||||
if ($('#repeatcheck').length) {
|
||||
function pwcheck (e) {
|
||||
let pw = $('#password').val()
|
||||
let pwa = $('#password_repeat').val()
|
||||
var pw = $('#password').val()
|
||||
var pwa = $('#password_repeat').val()
|
||||
if (pwa !== pw) {
|
||||
$('#password_repeat').addClass('invalid')
|
||||
$('#repeatcheck').show()
|
||||
@ -76,6 +76,30 @@ $(document).ready(function () {
|
||||
})
|
||||
}
|
||||
|
||||
if ($('.newsfeed').length) {
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
url: '/api/news',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (!data.length) {
|
||||
return $('.newsfeed').html('There is nothing to show at this moment.')
|
||||
}
|
||||
var html = ''
|
||||
for (var i in data) {
|
||||
var article = data[i]
|
||||
html += '<div class="prvarticle">'
|
||||
html += '<a class="title" href="/news/' + article.id + '-' + article.slug + '">' + article.title + '</a>'
|
||||
html += '<span class="timestamp">Published at ' + new Date(article.created_at) + '</span>'
|
||||
html += '<div class="prvcontent">' + article.content + '</div>'
|
||||
html += '<a href="/news/' + article.id + '-' + article.slug + '">Read More</a>'
|
||||
html += '</div>'
|
||||
}
|
||||
$('.newsfeed').html(html)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.checkLoginState = function () {
|
||||
FB.getLoginStatus(function(response) {
|
||||
$.ajax({
|
||||
@ -83,7 +107,7 @@ $(document).ready(function () {
|
||||
url: '/api/external/facebook/callback',
|
||||
dataType: 'json',
|
||||
data: response,
|
||||
success: (data) => {
|
||||
success: function (data) {
|
||||
if (data.error) {
|
||||
$('.message').addClass('error')
|
||||
$('.message span').text(data.error)
|
||||
|
@ -216,7 +216,7 @@ input:not([type="submit"])
|
||||
box-shadow: inset 2px 2px 5px #ddd
|
||||
transition: border 0.1s linear
|
||||
|
||||
input[type="submit"]
|
||||
.button, input[type="submit"]
|
||||
display: block
|
||||
padding: 5px 10px
|
||||
background-color: #fbfbfb
|
||||
@ -226,6 +226,9 @@ input[type="submit"]
|
||||
margin: 10px 0
|
||||
cursor: pointer
|
||||
|
||||
.button
|
||||
display: inline-block
|
||||
|
||||
.boxcont
|
||||
.box
|
||||
.left, .right
|
||||
@ -249,6 +252,14 @@ input[type="submit"]
|
||||
h1, h2, h3
|
||||
margin-top: 0
|
||||
|
||||
.pgn
|
||||
display: inline-block
|
||||
margin-left: 10px
|
||||
|
||||
.pagenum
|
||||
display: inline-block
|
||||
padding: 10px
|
||||
|
||||
.dlbtn
|
||||
display: block
|
||||
img
|
||||
@ -371,6 +382,45 @@ input.invalid
|
||||
color: red
|
||||
padding: 10px
|
||||
|
||||
.article
|
||||
.title
|
||||
font-size: 200%
|
||||
font-weight: bold
|
||||
.author
|
||||
font-size: 80%
|
||||
color: #565656
|
||||
.timestamp
|
||||
display: inline-block
|
||||
.content
|
||||
margin-top: 20px
|
||||
margin-bottom: 20px
|
||||
|
||||
.newsfeed
|
||||
.load
|
||||
font-size: 120%
|
||||
span
|
||||
vertical-align: super
|
||||
margin-left: 10px
|
||||
.prvarticle
|
||||
margin-bottom: 10px
|
||||
.title
|
||||
font-size: 200%
|
||||
display: block
|
||||
font-weight: bold
|
||||
.timestamp
|
||||
font-size: 80%
|
||||
color: #565656
|
||||
.prvcontent
|
||||
margin: 10px 0
|
||||
|
||||
.return
|
||||
margin-top: 40px
|
||||
|
||||
.content
|
||||
hr
|
||||
border-color: #ffffff
|
||||
margin: 20px 0
|
||||
|
||||
@media all and (max-width: 800px)
|
||||
.navigator
|
||||
padding: 0 10px
|
||||
|
24
views/article.pug
Normal file
24
views/article.pug
Normal file
@ -0,0 +1,24 @@
|
||||
extends layout.pug
|
||||
|
||||
block title
|
||||
if article
|
||||
|Icy Network - News - #{article.title}
|
||||
else
|
||||
|Icy Network - News - 404
|
||||
|
||||
block body
|
||||
.document
|
||||
.content
|
||||
if !article
|
||||
span.error No such article
|
||||
else
|
||||
.article
|
||||
.title= article.title
|
||||
.author Published by
|
||||
span #{article.author.display_name}
|
||||
|at
|
||||
.timestamp #{new Date(article.created_at)}
|
||||
.content!= article.content
|
||||
.return
|
||||
a(href="/news") Back to news directory
|
||||
|
@ -22,3 +22,8 @@ block body
|
||||
section#news
|
||||
.content
|
||||
h1 Icy Network News
|
||||
.newsfeed
|
||||
span.load
|
||||
i.fa.fa-spin.fa-spinner.fa-2x
|
||||
span Loading feed
|
||||
a.older(href="/news") View older articles
|
||||
|
34
views/news.pug
Normal file
34
views/news.pug
Normal file
@ -0,0 +1,34 @@
|
||||
extends layout.pug
|
||||
|
||||
block title
|
||||
|Icy Network - News
|
||||
|
||||
block body
|
||||
.document
|
||||
.content
|
||||
h1 Icy Network News
|
||||
if news.error
|
||||
span.error There are no articles to show.
|
||||
else
|
||||
if news.page
|
||||
span.pagenum
|
||||
|Page #{news.page.page} of #{news.page.pages}
|
||||
.pgn
|
||||
if news.page.page > 1
|
||||
a.button(href="/news/?page=" + news.page.page - 1) Previous
|
||||
- var n = 0
|
||||
while n < news.page.pages
|
||||
a.button(href="/news/?page=" + (n + 1))= n + 1
|
||||
- n++
|
||||
if news.page.pages > news.page.page
|
||||
a.button(href="/news/?page=" + news.page.page + 1) Next
|
||||
each val in news.articles
|
||||
.article
|
||||
a.title(href="/news/" + val.id + "-" + val.slug)= val.title
|
||||
.author Published by
|
||||
span #{val.author.display_name}
|
||||
|at
|
||||
.timestamp #{new Date(val.created_at)}
|
||||
.content!= val.content
|
||||
hr
|
||||
|
Reference in New Issue
Block a user