diff --git a/openapi.yaml b/openapi.yaml index ebdffdb79b..e181a27d62 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8,11 +8,15 @@ info: license: name: MIT url: 'https://opensource.org/licenses/MIT' +servers: + - url: /api/v1 tags: - name: users description: 'Account related calls (create, modify, delete, etc.)' + - name: categories + description: Administrative calls to manage categories paths: - /: + /users/: post: tags: - users @@ -94,7 +98,7 @@ paths: $ref: '#/components/schemas/Status' response: type: object - '/{uid}': + '/users/{uid}': put: tags: - users @@ -155,7 +159,7 @@ paths: $ref: '#/components/schemas/Status' response: type: object - '/{uid}/password': + '/users/{uid}/password': put: tags: - users @@ -195,7 +199,7 @@ paths: $ref: '#/components/schemas/Status' response: type: object - '/{uid}/follow': + '/users/{uid}/follow': post: tags: - users @@ -242,7 +246,7 @@ paths: $ref: '#/components/schemas/Status' response: type: object - '/{uid}/ban': + '/users/{uid}/ban': put: tags: - users @@ -302,6 +306,65 @@ paths: $ref: '#/components/schemas/Status' response: type: object + /categories/: + post: + tags: + - categories + summary: creates a category + description: This operation creates a new category + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: + type: string + parentCid: + type: number + cloneFromCid: + type: number + icon: + type: string + description: A ForkAwesome icon without the `fa-` prefix + bgColor: + type: string + color: + type: string + link: + type: string + class: + type: string + backgroundImage: + type: string + required: + - name + example: + name: My New Category + description: Lorem ipsum, dolor sit amet + parentCid: 0 + cloneFromCid: 0 + icon: bullhorn + bgColor: '#ffffff' + color: '#000000' + link: 'https://example.org' + class: 'col-md-3 col-xs-6' + backgroundImage: '/assets/relative/path/to/image' + responses: + '200': + description: category successfully created + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + $ref: '#/components/schemas/CategoryObj' components: schemas: Status: @@ -479,6 +542,78 @@ components: This is a paragraph all about how my life got twist-turned upside-down and I'd like to take a minute and sit right here, to tell you all about how I because the administrator of NodeBB + CategoryObj: + properties: + cid: + type: number + example: 1 + name: + type: string + example: My New Category + description: + type: string + example: Lorem ipsum, dolor sit amet + descriptionParsed: + type: string + example: Lorem ipsum, dolor sit amet + icon: + type: string + example: bullhorn + bgColor: + type: string + example: '#ffffff' + color: + type: string + example: '#000000' + slug: + type: string + example: 1/my-new-category + parentCid: + type: number + example: 0 + topic_count: + type: number + example: 0 + post_count: + type: number + example: 0 + disabled: + type: number + example: 0 + order: + type: number + example: 5 + link: + type: number + example: 'https://example.org' + numRecentReplies: + type: number + example: 1 + class: + type: string + example: col-md-3 col-xs-6 + imageClass: + type: string + example: cover + isSection: + type: number + example: 0 + totalPostCount: + type: number + example: 0 + totalTopicCount: + type: number + example: 0 + tagWhitelist: + type: array + example: + - some-tag + - another-tag + unread-class: + type: string + backgroundImage: + type: string + example: '/assets/images/covers/Circuit1.png' responses: '400': description: Bad Request diff --git a/src/controllers/write/categories.js b/src/controllers/write/categories.js new file mode 100644 index 0000000000..0fac35a1a4 --- /dev/null +++ b/src/controllers/write/categories.js @@ -0,0 +1,13 @@ +'use strict'; + +const categories = require('../../categories'); + +const helpers = require('../helpers'); + +const Categories = module.exports; + +Categories.create = async (req, res) => { + const response = await categories.create(req.body); + const categoryObjs = await categories.getCategories([response.cid]); + helpers.formatApiResponse(200, res, categoryObjs[0]); +}; diff --git a/src/controllers/write/index.js b/src/controllers/write/index.js index b7a4f0f50b..be642c2807 100644 --- a/src/controllers/write/index.js +++ b/src/controllers/write/index.js @@ -3,3 +3,4 @@ const Write = module.exports; Write.users = require('./users'); +Write.categories = require('./categories'); diff --git a/src/routes/write/categories.js b/src/routes/write/categories.js new file mode 100644 index 0000000000..adccb9b1bf --- /dev/null +++ b/src/routes/write/categories.js @@ -0,0 +1,86 @@ +'use strict'; + +const router = require('express').Router(); +const middleware = require('../../middleware'); +const controllers = require('../../controllers'); +const routeHelpers = require('../../routes/helpers'); + +const setupApiRoute = routeHelpers.setupApiRoute; + +module.exports = function () { + const middlewares = [middleware.authenticate]; + + setupApiRoute(router, '/', middleware, [...middlewares, middleware.checkRequired.bind(null, ['name'])], 'post', controllers.write.categories.create); + + // app.post('/', apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) { + // if (!utils.checkRequired(['name'], req, res)) { + // return false; + // } + + // Categories.create(req.body, function(err, categoryObj) { + // return errorHandler.handle(err, res, categoryObj); + // }); + // }); + + // app.route('/:cid') + // .put(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCid, function(req, res) { + // var payload = {}; + // payload[req.params.cid] = req.body; + + // Categories.update(payload, function(err) { + // return errorHandler.handle(err, res); + // }); + // }) + // .delete(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCid, function(req, res) { + // Categories.purge(req.params.cid, req.user.uid, function(err) { + // return errorHandler.handle(err, res); + // }); + // }); + + // app.route('/:cid/state') + // .put(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCid, function(req, res) { + // var payload = {}; + // payload[req.params.cid] = { + // disabled: 0 + // }; + + // Categories.update(payload, function(err) { + // return errorHandler.handle(err, res); + // }); + // }) + // .delete(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCid, function(req, res) { + // var payload = {}; + // payload[req.params.cid] = { + // disabled: 1 + // }; + + // Categories.update(payload, function(err) { + // return errorHandler.handle(err, res); + // }); + // }); + + // app.route('/:cid/privileges') + // .put(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCidIncludingGlobal, function(req, res) { + // changeGroupMembership(req.params.cid, req.body.privileges, req.body.groups, 'join', function(err) { + // return errorHandler.handle(err, res); + // }); + // }) + // .delete(apiMiddleware.requireUser, apiMiddleware.requireAdmin, apiMiddleware.validateCidIncludingGlobal, function(req, res) { + // changeGroupMembership(req.params.cid, req.body.privileges, req.body.groups, 'leave', function(err) { + // return errorHandler.handle(err, res); + // }); + // }); + + // function changeGroupMembership(cid, privileges, groups, action, callback) { + // privileges = Array.isArray(privileges) ? privileges : [privileges]; + // groups = Array.isArray(groups) ? groups : [groups]; + + // async.each(groups, function(group, groupCb) { + // async.each(privileges, function(privilege, privilegeCb) { + // Groups[action]('cid:' + cid + ':privileges:' + privilege, group, privilegeCb); + // }, groupCb); + // }, callback); + // } + + return router; +}; diff --git a/src/routes/write/index.js b/src/routes/write/index.js index 266b4dc577..62de9501be 100644 --- a/src/routes/write/index.js +++ b/src/routes/write/index.js @@ -24,7 +24,7 @@ Write.reload = (params) => { // router.use('/groups', require('./groups')(coreMiddleware)); // router.use('/posts', require('./posts')(coreMiddleware)); // router.use('/topics', require('./topics')(coreMiddleware)); - // router.use('/categories', require('./categories')(coreMiddleware)); + router.use('/api/v1/categories', require('./categories')()); // router.use('/util', require('./util')(coreMiddleware)); router.get('/api/v1/ping', function (req, res) { diff --git a/src/routes/write/users.js b/src/routes/write/users.js index 5835d010ce..6fa3aad485 100644 --- a/src/routes/write/users.js +++ b/src/routes/write/users.js @@ -6,12 +6,6 @@ const controllers = require('../../controllers'); const routeHelpers = require('../helpers'); const setupApiRoute = routeHelpers.setupApiRoute; -// Messaging = require.main.require('./src/messaging'), -// apiMiddleware = require('./middleware'), -// errorHandler = require('../../lib/errorHandler'), -// auth = require('../../lib/auth'), -// utils = require('./utils'), -// async = require.main.require('async'); // eslint-disable-next-line no-unused-vars function guestRoutes() { @@ -35,52 +29,6 @@ function authenticatedRoutes() { setupApiRoute(router, '/:uid/ban', middleware, [...middlewares, middleware.exposePrivileges], 'put', controllers.write.users.ban); setupApiRoute(router, '/:uid/ban', middleware, [...middlewares, middleware.exposePrivileges], 'delete', controllers.write.users.unban); - // app.route('/:uid/ban') - // .put(apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) { - // Users.bans.ban(req.params.uid, req.body.until || 0, req.body.reason || '', function(err) { - // errorHandler.handle(err, res); - // }); - // }) - // .delete(apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) { - // Users.bans.unban(req.params.uid, function(err) { - // errorHandler.handle(err, res); - // }); - // }); - - // app.route('/:uid/tokens') - // .get(apiMiddleware.requireUser, function(req, res) { - // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) { - // return errorHandler.respond(401, res); - // } - - // auth.getTokens(req.params.uid, function(err, tokens) { - // return errorHandler.handle(err, res, { - // tokens: tokens - // }); - // }); - // }) - // .post(apiMiddleware.requireUser, function(req, res) { - // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid)) { - // return errorHandler.respond(401, res); - // } - - // auth.generateToken(req.params.uid, function(err, token) { - // return errorHandler.handle(err, res, { - // token: token - // }); - // }); - // }); - - // app.delete('/:uid/tokens/:token', apiMiddleware.requireUser, function(req, res) { - // if (parseInt(req.params.uid, 10) !== req.user.uid) { - // return errorHandler.respond(401, res); - // } - - // auth.revokeToken(req.params.token, 'user', function(err) { - // errorHandler.handle(err, res); - // }); - // }); - /** * Chat routes were not migrated because chats may get refactored... also the logic is derpy * It also does not take into account multiple chats for a given user. @@ -126,6 +74,43 @@ function authenticatedRoutes() { // } // }); // }); + + /** + * Implement this later... + */ + // app.route('/:uid/tokens') + // .get(apiMiddleware.requireUser, function(req, res) { + // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) { + // return errorHandler.respond(401, res); + // } + + // auth.getTokens(req.params.uid, function(err, tokens) { + // return errorHandler.handle(err, res, { + // tokens: tokens + // }); + // }); + // }) + // .post(apiMiddleware.requireUser, function(req, res) { + // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid)) { + // return errorHandler.respond(401, res); + // } + + // auth.generateToken(req.params.uid, function(err, token) { + // return errorHandler.handle(err, res, { + // token: token + // }); + // }); + // }); + + // app.delete('/:uid/tokens/:token', apiMiddleware.requireUser, function(req, res) { + // if (parseInt(req.params.uid, 10) !== req.user.uid) { + // return errorHandler.respond(401, res); + // } + + // auth.revokeToken(req.params.token, 'user', function(err) { + // errorHandler.handle(err, res); + // }); + // }); } module.exports = function () { diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index d1ae771c60..c3cacaa0c2 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -8,10 +8,13 @@ const categories = require('../../categories'); const privileges = require('../../privileges'); const plugins = require('../../plugins'); const events = require('../../events'); +const sockets = require('..'); const Categories = module.exports; Categories.create = async function (socket, data) { + sockets.warnDeprecated(socket, 'POST /api/v1/categories'); + if (!data) { throw new Error('[[error:invalid-data]]'); }