This commit is contained in:
Troplo 2020-09-08 18:11:42 +10:00
parent a28b22b6b1
commit 193498d708
10 changed files with 222 additions and 8 deletions

BIN
frontend/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -0,0 +1,41 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('BlogPosts', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
name: {
type: Sequelize.TEXT,
allowNull: false
},
slug: Sequelize.TEXT,
content: {
type: Sequelize.TEXT,
allowNull: false
},
postNumber: Sequelize.INTEGER,
removed: {
type: Sequelize.BOOLEAN,
defaultValue: false
},
UserId: Sequelize.INTEGER,
comments: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
}, {
charset: 'utf8mb4'
})
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('BlogPosts');
}
};

View file

@ -0,0 +1,29 @@
module.exports = {
up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn(
'Settings',
'logo',
{
type: Sequelize.STRING,
required: true,
default: "https://cdn.kaverti.com/logo.png",
allowNull: false
},
),
queryInterface.addColumn(
'Settings',
'icon',
{
type: Sequelize.STRING,
required: true,
default: "/favicon.png",
allowNull: false
},
)
]);
},
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn('Users', 'theme');
}
};

135
models/blog.js Normal file
View file

@ -0,0 +1,135 @@
let urlSlug = require('url-slug')
module.exports = (sequelize, DataTypes) => {
let Thread = sequelize.define('Thread', {
name: {
type: DataTypes.TEXT,
set (val) {
this.setDataValue('name', val)
if(val) {
this.setDataValue(
'slug',
//if you don't covert to lowercase it doesn't
//correctly slugify diacritics, e.g. thrËad
//becomes 'thr-ead' not 'thread'
urlSlug(val.toString().toLowerCase() || '') || '_'
)
}
},
allowNull: false,
validate: {
notEmpty: {
msg: 'Did you forget something? (Fill this in!)'
},
len: {
args: [4, 256],
msg: 'The title must be between 4 and 256 characters'
},
isString (val) {
if(typeof val !== 'string') {
throw new sequelize.ValidationError('The title must be a string')
}
}
}
},
slug: DataTypes.TEXT,
postsCount: {
type: DataTypes.INTEGER,
defaultValue: 0
},
locked: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {
instanceMethods: {
getMeta (limit) {
let meta = {}
let posts = this.Posts
let firstPost = posts[0]
let lastPost = posts.slice(-1)[0]
//next url
if(!lastPost || lastPost.postNumber+1 === this.postsCount) {
meta.nextURL = null
} else {
meta.nextURL =
`/api/v1/blog/post/${this.id}?limit=${limit}&from=${lastPost.postNumber + 1}`
}
//previous url
if(!firstPost || firstPost.postNumber === 0) {
meta.previousURL = null
} else if(firstPost.postNumber - limit < 0) {
meta.previousURL =
`/api/v1/forums/thread/${this.id}?limit=${firstPost.postNumber}&from=0`
} else {
meta.previousURL =
`/api/v1/forums/thread/${this.id}?limit=${limit}&from=${firstPost.postNumber - limit}`
}
//remaining posts
if(lastPost === undefined) {
meta.nextPostsCount = 0
meta.previousPostsCount = 0
meta.postsRemaining = 0
} else {
let postsRemaining =
this.postsCount - lastPost.postNumber - 1
meta.postsRemaining = postsRemaining
if(postsRemaining < limit) {
meta.nextPostsCount = postsRemaining
} else {
meta.nextPostsCount = limit
}
if(firstPost.postNumber === 0) {
meta.previousPostsCount = 0
} else if(firstPost.postNumber - limit < 0) {
meta.previousPostsCount = firstPost.postNumber
} else {
meta.previousPostsCount = limit
}
}
return meta
}
},
classMethods: {
associate (models) {
Thread.belongsTo(models.User)
Thread.belongsTo(models.Category)
Thread.belongsTo(models.PollQuestion)
Thread.hasMany(models.Post, { foreignKeyConstraint: true, onDelete: 'CASCADE' })
},
includeOptions (from, limit) {
let models = sequelize.models
return [
{ model: models.User, attributes: ['username', 'createdAt', 'color', 'picture', 'updatedAt', 'id'] },
models.Category,
{
model: models.Post,
where: { postNumber: { $gte: from } },
order: [['id', 'ASC']],
limit,
include: [
{ model: models.Thread, attributes: ['slug'] },
{ model: models.User, as: 'Likes', attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
{ model: models.User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'admin'] },
{
model: models.Post, as: 'Replies', include:
[{ model: models.User, attributes: ['username', 'id', 'color', 'picture'] }]
}
]
}
]
}
}
})
return Thread
}

View file

@ -42,6 +42,14 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
defaultValue: "wss://gateway.kaverti.com"
},
logo: {
type: DataTypes.STRING,
defaultValue: "https://cdn.kaverti.com/logo.png"
},
icon: {
type: DataTypes.STRING,
defaultValue: "/favicon.png"
},
RegistrationsDisabled: {
type: DataTypes.BOOLEAN,
defaultValue: false
@ -49,7 +57,6 @@ module.exports = (sequelize, DataTypes) => {
}, {
classMethods: {
set (values) {
values.id = 1
return Settings.upsert(values)
},
get () {

0
routes/blog.js Normal file
View file

View file

@ -9,7 +9,7 @@ router.get('/:id', async (req, res, next) => {
let id = req.params.id
let pollQuestion = await PollQuestion.findById(id, {
include: [
{ model: User, attributes: { exclude: ['hash', 'email'] } },
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] } },
{ model: PollAnswer, include: [PollVote] }
]
})

View file

@ -44,13 +44,13 @@ router.get('/thread', async (req, res, next) => {
include: [
{
model: Post,
include: [{ model: User, attributes: { exclude: ['hash', 'email'] } }],
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode']} }],
where: {
postNumber: 0
}
},
{ model: Category },
{ model: User, attributes: { exclude: ['hash', 'email'] } }
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] } }
],
limit
})
@ -63,14 +63,14 @@ router.get('/thread', async (req, res, next) => {
include: [
{
model: Post,
include: [{ model: User, attributes: { exclude: ['hash', 'email'] } }],
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] } }],
where: {
postNumber: 0,
plainText: { $like: '%' + searchString + '%' }
}
},
{ model: Category },
{ model: User, attributes: { exclude: ['hash', 'email'] } }
{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] } }
],
limit
})
@ -112,7 +112,7 @@ router.get('/thread', async (req, res, next) => {
$or: whereClause
},
order: [ ['ThreadId', 'DESC'] ],
include: [{ model: User, attributes: { exclude: ['hash', 'email'] } }]
include: [{ model: User, attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] } }]
})
//Merge latest posts with threads array

View file

@ -7,6 +7,8 @@ let { Settings, Ban, Sequelize } = require('../models')
router.get('/', async (req, res, next) => {
try {
let settings = await Settings.get()
exclude: ['id']
if(!settings) throw Errors.noSettings

View file

@ -10,7 +10,7 @@ router.get('/users', async (req, res, next) => {
username
},
order: [ ['username', 'DESC'] ],
attributes: { exclude: ['hash', 'email'] },
attributes: { exclude: ['hash', 'email', 'emailVerified', 'currency1', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode'] },
offset
})