Commit b2836491 authored by Jarrod's avatar Jarrod 💬

Remove stuff

parent 9f2eb347
################################################
# ┌─┐┬┌┬┐╦╔═╗╔╗╔╔═╗╦═╗╔═╗
# │ ┬│ │ ║║ ╦║║║║ ║╠╦╝║╣
# o└─┘┴ ┴ ╩╚═╝╝╚╝╚═╝╩╚═╚═╝
#
# > Files to exclude from your app's repo.
#
# This file (`.gitignore`) is only relevant if
# you are using git.
#
# It exists to signify to git that certain files
# and/or directories should be ignored for the
# purposes of version control.
#
# This keeps tmp files and sensitive credentials
# from being uploaded to your repository. And
# it allows you to configure your app for your
# machine without accidentally committing settings
# which will smash the local settings of other
# developers on your team.
#
# Some reasonable defaults are included below,
# but, of course, you should modify/extend/prune
# to fit your needs!
#
################################################
package-lock.json
################################################
# Local Configuration
#
# Explicitly ignore files which contain:
#
# 1. Sensitive information you'd rather not push to
# your git repository.
# e.g., your personal API keys or passwords.
#
# 2. Developer-specific configuration
# Basically, anything that would be annoying
# to have to change every time you do a
# `git pull` on your laptop.
# e.g. your local development database, or
# the S3 bucket you're using for file uploads
# during development.
#
################################################
config/local.js
################################################
# Dependencies
#
#
# When releasing a production app, you _could_
# hypothetically include your node_modules folder
# in your git repo, but during development, it
# is always best to exclude it, since different
# developers may be working on different kernels,
# where dependencies would need to be recompiled
# anyway.
#
# Most of the time, the node_modules folder can
# be excluded from your code repository, even
# in production, thanks to features like the
# package-lock.json file / NPM shrinkwrap.
#
# But no matter what, since this is a Sails app,
# you should always push up the package-lock.json
# or shrinkwrap file to your repository, to avoid
# accidentally pulling in upgraded dependencies
# and breaking your code.
#
# That said, if you are having trouble with
# dependencies, (particularly when using
# `npm link`) this can be pretty discouraging.
# But rather than just adding the lockfile to
# your .gitignore, try this first:
# ```
# rm -rf node_modules
# rm package-lock.json
# npm install
# ```
#
# [?] For more tips/advice, come by and say hi
# over at https://sailsjs.com/support
#
################################################
node_modules
################################################
#
# > Do you use bower?
# > re: the bower_components dir, see this:
# > http://addyosmani.com/blog/checking-in-front-end-dependencies/
# > (credit Addy Osmani, @addyosmani)
#
################################################
################################################
# Temporary files generated by Sails/Waterline.
################################################
.tmp
################################################
# Miscellaneous
#
# Common files generated by text editors,
# operating systems, file systems, dbs, etc.
################################################
*~
*#
.DS_STORE
.netbeans
nbproject
.idea
*.iml
.vscode
.node_history
dump.rdb
npm-debug.log
lib-cov
*.seed
*.log
*.out
*.pid
module.exports = {
friendlyName: `Create a blog post`,
description: ``,
inputs: {
slug: {
type: 'string',
required: true
},
title: {
type: 'string',
required: true
},
excerpt: {
type: 'string',
required: true
},
body: {
type: 'string',
required: true
},
published: {
type: 'boolean',
defaultsTo: false
},
category: {
type: 'number',
isInteger: true,
allowNull: true
},
featureImage: {
type: 'string',
allowNull: true
},
createdAt: {
type: 'ref',
}
},
exits: {
created: {
statusCode: 201,
description: 'Blog post was created'
}
},
fn: async function (inputs, exits) {
let post = await BlogPost.create(inputs).fetch()
// All done.
return exits.created({
post
})
}
}
module.exports = {
friendlyName: `Delete a blog image`,
description: ``,
inputs: {
post: {
type: 'number',
isInteger: true
},
filename: {
type: 'string',
required: true,
description: 'The name of the file to delete'
}
},
exits: {
badRequest: {
responseType: 'badRequest',
statusCode: 400
}
},
fn: async function (inputs) {
inputs.filename = inputs.filename.replace(/^article\//, '')
console.log('Removing ', inputs.filename)
await sails.rm(inputs.filename, sails.config.uploads.blog)
if (inputs.post) {
BlogPost
.update({ id: inputs.post })
.set({ featureImage: null })
.then(() => {})
}
// All done.
return {
message: 'ok'
}
}
}
module.exports = {
friendlyName: `Delete a blog post`,
description: ``,
inputs: {
id: {
type: 'number',
isInteger: true,
required: true
}
},
exits: { },
fn: async function (inputs) {
let post = await BlogPost.findOne({
id: inputs.id
})
if (!post) {
return this.res.badRequest('Post not found')
}
post = await BlogPost
.destroyOne({ id: inputs.id })
// Remove any images for this post
sails.rm(inputs.id + '/*', sails.config.uploads.blog, () => {})
// All done.
return {
post
}
}
}
module.exports = {
friendlyName: 'Admin Blog Index',
description: 'Get a list of all blog posts.',
inputs: {
// page: {
// type: 'number',
// isInteger: true,
// defaultsTo: 1,
// min: 1
// }
},
exits: {
},
fn: async function (inputs) {
const posts = await BlogPost
.find({ })
.select(['id', 'slug', 'title', 'published', 'excerpt', 'featureImage', 'createdAt'])
.sort('createdAt DESC')
.populate('category')
const categories = await BlogCategory
.find()
.sort('name ASC')
// All done.
return {
posts,
categories
}
}
}
module.exports = {
friendlyName: `Get Article as an administrator`,
description: `Get a blog post regardless of whether it's "published" or not`,
inputs: {
id: {
type: 'number',
isInteger: true,
required: true
}
},
exits: { },
fn: async function (inputs) {
const post = await BlogPost.findOne({
id: inputs.id
})
if (!post) {
return this.res.notFound('Post not found')
}
// All done.
return {
post
}
}
}
module.exports = {
friendlyName: `Update a blog post`,
description: ``,
inputs: {
id: {
type: 'number',
isInteger: true,
required: true
},
slug: {
type: 'string',
required: true
},
title: {
type: 'string',
required: true
},
excerpt: {
type: 'string',
required: true
},
body: {
type: 'string',
required: true
},
published: {
type: 'boolean',
defaultsTo: false
},
category: {
type: 'number',
isInteger: true,
allowNull: true
},
featureImage: {
type: 'string',
allowNull: true
},
createdAt: {
type: 'ref',
}
},
exits: { },
fn: async function (inputs) {
let post = await BlogPost.findOne({
id: inputs.id
})
if (!post) {
return this.res.badRequest('Post not found')
}
post = await BlogPost
.updateOne({ id: inputs.id })
.set(_.omit(inputs, ['id']))
// All done.
return {
post
}
}
}
module.exports = {
friendlyName: `Upload an image for a blog post`,
description: ``,
files: ['file'],
inputs: {
post: {
type: 'number',
isInteger: true,
required: true,
description: 'The ID of the post this image is attached to'
},
file: {
type: 'ref',
description: 'The file to upload',
required: true
}
},
exits: {
badRequest: {
responseType: 'badRequest',
statusCode: 400
}
},
fn: async function (inputs) {
const post = await BlogPost.findOne({ id: inputs.post })
if (!post) {
return this.res.badRequest('Invalid post ID')
}
const uploadConfig = _.merge(sails.config.uploads.blog || {}, {
saveAs: function (__file, cb) {
// Generate a filename like 'postId/RANDOMoriginalfile.jpg'
const pseudoRandomString = Math.random().toString(36).substring(7) + '-'
return cb(null, post.id + '/' + pseudoRandomString + __file.filename)
},
})
let file = await sails.uploadOne(inputs.file, uploadConfig)
.intercept('E_INVALID_FILE_TYPE', () => {
return { badRequest: 'Uploaded file type not allowed!' }
})
.intercept('E_EXCEEDS_UPLOAD_LIMIT', () => {
return { badRequest: 'Uploaded file is too large!' }
})
.intercept((err) => {
sails.log.error(`Unable to upload image for blog post`, err.stack)
return new Error('Unable to save uploaded file(s)')
})
let url = 'article/' + file.fd;
BlogPost.updateOne({ id: post.id })
.set({ featureImage: url })
.then(() => { })
// All done.
return {
url
}
}
}
module.exports = {
friendlyName: `Get all categories`,
description: ``,
inputs: { },
exits: { },
fn: async function (inputs) {
const categories = await BlogCategory.find({ })
// All done.
return {
categories
}
}
}
module.exports = {
friendlyName: 'Blog Category Index',
description: 'Get a list of blog posts in the given category.',
inputs: {
slug: {
type: 'string',
required: true
},
// page: {
// type: 'number',
// isInteger: true,
// defaultsTo: 1,
// min: 1
// }
},
exits: { },
fn: async function (inputs) {
const category = await BlogCategory
.findOne({ slug: inputs.slug })
if (!category) {
return this.res.notFound('Category not found')
}
const posts = await BlogPost
.find({ category: category.id, published: true })
.select(['id', 'slug', 'title', 'excerpt', 'featureImage', 'createdAt'])
.sort('createdAt DESC')
.populate('category')
// All done.
return {
category,
posts
}
}
}
module.exports = {
friendlyName: 'Get Article',
description: 'Get a blog post / article.',
inputs: {
slug: {
type: 'string',
required: true
}
},
exits: {
},
fn: async function (inputs) {
const { req } = this
const where = {
slug: inputs.slug
}
// Allow admins to 'preview' unpublished posts
// TODO: Replace with actual 'permissions' handling stuff
if (!req.me || !req.me.isAdmin) {
where.published = true
}
const post = await BlogPost.findOne(where)
.select(['id', 'slug', 'title', 'excerpt', 'body', 'featureImage', 'createdAt'])
.populate('category')
if (!post) {
return this.res.notFound('Post not found')
}
const recentPosts = await BlogPost.find({
published: true,
id: { '!=': post.id }
})
.select(['id', 'slug', 'title', 'excerpt', 'featureImage', 'createdAt'])
.populate('category')
.sort('createdAt DESC')
.limit(3)
// All done.
return {
post,
recentPosts
}
}
}
module.exports = {
friendlyName: 'Blog Index',
description: 'Get a list of blog categories and recent articles.',
inputs: {
// page: {
// type: 'number',
// isInteger: true,
// defaultsTo: 1,
// min: 1
// }
},
exits: {
},
fn: async function (inputs) {
const posts = await BlogPost
.find({ published: true })
.select(['id', 'slug', 'title', 'excerpt', 'featureImage', 'createdAt'])
.sort('createdAt DESC')
.populate('category')
const categories = await BlogCategory
.find()
.sort('name ASC')
// All done.
return {
posts,
categories
}
}
}
/**
* Forum
*/
const Pluggable = require('@nahanil/ahoy/lib/pluggable')
module.exports = new Pluggable({
key: 'blog',
apiBase: '',
dir: __dirname,
// Add permissions @ core ahoy level?
permissions: [
'blog'
],
actions: {
// Public routes
'GET /blog': { action: 'index', cache: 10 },
'GET /blog/categories': { action: 'get-categories', cache: 10 },
'GET /blog/category/:slug': { action: 'get-category', cache: 10 },
'GET /blog/post/:slug': { action: 'get-post', cache: 10 },
// Admin routes
'GET /admin/blog/list': { action: 'admin/get-all-posts', permission: 'blog' },
'GET /admin/blog/post/:id': { action: 'admin/get-post', permission: 'blog' },
'POST /admin/blog/post': { action: 'admin/create-post', permission: 'blog' },
'PUT /admin/blog/post/:id': { action: 'admin/update-post', permission: 'blog' },
'DELETE /admin/blog/post/:id': { action: 'admin/delete-post', permission: 'blog' },
'POST /admin/blog/image': { action: 'admin/upload-image', permission: 'blog' },
'DELETE /admin/blog/image': { action: 'admin/delete-image', permission: 'blog' },
}
})
/**
* BlogCategory.js
*
* A category
*/
module.exports = {
attributes: {
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
name: {
type: 'string',
required: true,
description: 'Name of the category'
},
slug: {
type: 'string',
required: true,
unique: true,
description: 'URL slug for this article'
},
posts: {
collection: 'blogpost',
via: 'category'
}
},
customToJSON () {
return _.pick(this, ['id', 'name', 'slug', 'posts'])
}
};
/**
* BlogPost.js
*
* An article / blog post
*/
module.exports = {
attributes: {
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
title: {
type: 'string',
required: true,
description: 'Title of the article'
},
slug: {
type: 'string',
required: true,
unique: true,
description: 'URL slug for this article'
},