mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-23 22:18:06 +01:00
Split snippet controllers into separate files
This commit is contained in:
@@ -1,298 +0,0 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
|
||||||
import { QueryTypes, Op } from 'sequelize';
|
|
||||||
import { sequelize } from '../db';
|
|
||||||
import { asyncWrapper } from '../middleware';
|
|
||||||
import { SnippetModel, Snippet_TagModel, TagModel } from '../models';
|
|
||||||
import { ErrorResponse, tagParser, Logger, createTags } from '../utils';
|
|
||||||
import { Body, SearchQuery } from '../typescript/interfaces';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Create new snippet
|
|
||||||
* @route /api/snippets
|
|
||||||
* @request POST
|
|
||||||
*/
|
|
||||||
export const createSnippet = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
// Get tags from request body
|
|
||||||
const { language, tags: requestTags } = <Body>req.body;
|
|
||||||
const parsedRequestTags = tagParser([
|
|
||||||
...requestTags,
|
|
||||||
language.toLowerCase()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create snippet
|
|
||||||
const snippet = await SnippetModel.create({
|
|
||||||
...req.body,
|
|
||||||
tags: [...parsedRequestTags].join(',')
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create tags
|
|
||||||
await createTags(parsedRequestTags, snippet.id);
|
|
||||||
|
|
||||||
// Get raw snippet values
|
|
||||||
const rawSnippet = snippet.get({ plain: true });
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
data: {
|
|
||||||
...rawSnippet,
|
|
||||||
tags: [...parsedRequestTags]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Get all snippets
|
|
||||||
* @route /api/snippets
|
|
||||||
* @request GET
|
|
||||||
*/
|
|
||||||
export const getAllSnippets = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const snippets = await SnippetModel.findAll({
|
|
||||||
include: {
|
|
||||||
model: TagModel,
|
|
||||||
as: 'tags',
|
|
||||||
attributes: ['name'],
|
|
||||||
through: {
|
|
||||||
attributes: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const populatedSnippets = snippets.map(snippet => {
|
|
||||||
const rawSnippet = snippet.get({ plain: true });
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rawSnippet,
|
|
||||||
tags: rawSnippet.tags?.map(tag => tag.name)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: populatedSnippets
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Get single snippet by id
|
|
||||||
* @route /api/snippets/:id
|
|
||||||
* @request GET
|
|
||||||
*/
|
|
||||||
export const getSnippet = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const snippet = await SnippetModel.findOne({
|
|
||||||
where: { id: req.params.id },
|
|
||||||
include: {
|
|
||||||
model: TagModel,
|
|
||||||
as: 'tags',
|
|
||||||
attributes: ['name'],
|
|
||||||
through: {
|
|
||||||
attributes: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!snippet) {
|
|
||||||
return next(
|
|
||||||
new ErrorResponse(
|
|
||||||
404,
|
|
||||||
`Snippet with id of ${req.params.id} was not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawSnippet = snippet.get({ plain: true });
|
|
||||||
const populatedSnippet = {
|
|
||||||
...rawSnippet,
|
|
||||||
tags: rawSnippet.tags?.map(tag => tag.name)
|
|
||||||
};
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: populatedSnippet
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Update snippet
|
|
||||||
* @route /api/snippets/:id
|
|
||||||
* @request PUT
|
|
||||||
*/
|
|
||||||
export const updateSnippet = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
let snippet = await SnippetModel.findOne({
|
|
||||||
where: { id: req.params.id }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!snippet) {
|
|
||||||
return next(
|
|
||||||
new ErrorResponse(
|
|
||||||
404,
|
|
||||||
`Snippet with id of ${req.params.id} was not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get tags from request body
|
|
||||||
const { language, tags: requestTags } = <Body>req.body;
|
|
||||||
let parsedRequestTags = tagParser([...requestTags, language.toLowerCase()]);
|
|
||||||
|
|
||||||
// Update snippet
|
|
||||||
snippet = await snippet.update({
|
|
||||||
...req.body,
|
|
||||||
tags: [...parsedRequestTags].join(',')
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete old tags and create new ones
|
|
||||||
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
|
|
||||||
await createTags(parsedRequestTags, snippet.id);
|
|
||||||
|
|
||||||
// Get raw snippet values
|
|
||||||
const rawSnippet = snippet.get({ plain: true });
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: {
|
|
||||||
...rawSnippet,
|
|
||||||
tags: [...parsedRequestTags]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Delete snippet
|
|
||||||
* @route /api/snippets/:id
|
|
||||||
* @request DELETE
|
|
||||||
*/
|
|
||||||
export const deleteSnippet = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const snippet = await SnippetModel.findOne({
|
|
||||||
where: { id: req.params.id }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!snippet) {
|
|
||||||
return next(
|
|
||||||
new ErrorResponse(
|
|
||||||
404,
|
|
||||||
`Snippet with id of ${req.params.id} was not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
|
|
||||||
await snippet.destroy();
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Count tags
|
|
||||||
* @route /api/snippets/statistics/count
|
|
||||||
* @request GET
|
|
||||||
*/
|
|
||||||
export const countTags = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const result = await sequelize.query(
|
|
||||||
`SELECT
|
|
||||||
COUNT(tags.name) as count,
|
|
||||||
tags.name
|
|
||||||
FROM snippets_tags
|
|
||||||
INNER JOIN tags ON snippets_tags.tag_id = tags.id
|
|
||||||
GROUP BY tags.name
|
|
||||||
ORDER BY name ASC`,
|
|
||||||
{
|
|
||||||
type: QueryTypes.SELECT
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: result
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Get raw snippet code
|
|
||||||
* @route /api/snippets/raw/:id
|
|
||||||
* @request GET
|
|
||||||
*/
|
|
||||||
export const getRawCode = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const snippet = await SnippetModel.findOne({
|
|
||||||
where: { id: req.params.id },
|
|
||||||
raw: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!snippet) {
|
|
||||||
return next(
|
|
||||||
new ErrorResponse(
|
|
||||||
404,
|
|
||||||
`Snippet with id of ${req.params.id} was not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).send(snippet.code);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Search snippets
|
|
||||||
* @route /api/snippets/search
|
|
||||||
* @request POST
|
|
||||||
*/
|
|
||||||
export const searchSnippets = asyncWrapper(
|
|
||||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
||||||
const { query, tags, languages } = <SearchQuery>req.body;
|
|
||||||
|
|
||||||
// Check if query is empty
|
|
||||||
if (query === '' && !tags.length && !languages.length) {
|
|
||||||
res.status(200).json({
|
|
||||||
data: []
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const languageFilter = languages.length
|
|
||||||
? { [Op.in]: languages }
|
|
||||||
: { [Op.notIn]: languages };
|
|
||||||
|
|
||||||
const tagFilter = tags.length ? { [Op.in]: tags } : { [Op.notIn]: tags };
|
|
||||||
|
|
||||||
const snippets = await SnippetModel.findAll({
|
|
||||||
where: {
|
|
||||||
[Op.and]: [
|
|
||||||
{
|
|
||||||
[Op.or]: [
|
|
||||||
{ title: { [Op.substring]: `${query}` } },
|
|
||||||
{ description: { [Op.substring]: `${query}` } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
language: languageFilter
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
model: TagModel,
|
|
||||||
as: 'tags',
|
|
||||||
attributes: ['name'],
|
|
||||||
where: {
|
|
||||||
name: tagFilter
|
|
||||||
},
|
|
||||||
through: {
|
|
||||||
attributes: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
|
||||||
data: snippets
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
31
src/controllers/snippets/countTags.ts
Normal file
31
src/controllers/snippets/countTags.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { QueryTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../db';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Count tags
|
||||||
|
* @route /api/snippets/statistics/count
|
||||||
|
* @request GET
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const countTags = asyncWrapper(
|
||||||
|
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||||
|
const result = await sequelize.query(
|
||||||
|
`SELECT
|
||||||
|
COUNT(tags.name) as count,
|
||||||
|
tags.name
|
||||||
|
FROM snippets_tags
|
||||||
|
INNER JOIN tags ON snippets_tags.tag_id = tags.id
|
||||||
|
GROUP BY tags.name
|
||||||
|
ORDER BY name ASC`,
|
||||||
|
{
|
||||||
|
type: QueryTypes.SELECT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
48
src/controllers/snippets/createSnippet.ts
Normal file
48
src/controllers/snippets/createSnippet.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel } from '../../models';
|
||||||
|
import { Snippet, UserInfoRequest } from '../../typescript/interfaces';
|
||||||
|
import { tagParser, createTags } from '../../utils';
|
||||||
|
|
||||||
|
interface RequestBody extends Snippet {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Create new snippet
|
||||||
|
* @route /api/snippets
|
||||||
|
* @request POST
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const createSnippet = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: UserInfoRequest<RequestBody>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
// Get tags from request body
|
||||||
|
const { language, tags: requestTags = [] } = req.body;
|
||||||
|
|
||||||
|
const parsedRequestTags = tagParser([
|
||||||
|
...requestTags,
|
||||||
|
language.toLowerCase()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Create snippet
|
||||||
|
const snippet = await SnippetModel.create({
|
||||||
|
...req.body,
|
||||||
|
createdBy: req.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create tags
|
||||||
|
await createTags(parsedRequestTags, snippet.id);
|
||||||
|
|
||||||
|
// Get raw snippet values
|
||||||
|
const rawSnippet = snippet.get({ plain: true });
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
data: {
|
||||||
|
...rawSnippet,
|
||||||
|
tags: [...parsedRequestTags]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
50
src/controllers/snippets/deleteSnippet.ts
Normal file
50
src/controllers/snippets/deleteSnippet.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel, Snippet_TagModel } from '../../models';
|
||||||
|
import { Snippet, UserInfoRequest } from '../../typescript/interfaces';
|
||||||
|
import { tagParser, createTags, ErrorResponse } from '../../utils';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Delete snippet
|
||||||
|
* @route /api/snippets/:id
|
||||||
|
* @request DELETE
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const deleteSnippet = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: UserInfoRequest<{}, Params>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const snippet = await SnippetModel.findOne({
|
||||||
|
where: { id: req.params.id }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!snippet) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(
|
||||||
|
404,
|
||||||
|
`Snippet with the id of ${req.params.id} was not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snippet.createdBy != req.user.id && !req.user.isAdmin) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(401, `You are not authorized to modify this resource`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all snippet <> tag relations
|
||||||
|
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
|
||||||
|
await snippet.destroy();
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
43
src/controllers/snippets/getAllSnippets.ts
Normal file
43
src/controllers/snippets/getAllSnippets.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel, TagModel } from '../../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get all snippets
|
||||||
|
* @route /api/snippets
|
||||||
|
* @request GET
|
||||||
|
*/
|
||||||
|
export const getAllSnippets = asyncWrapper(
|
||||||
|
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||||
|
const snippets = await SnippetModel.findAll({
|
||||||
|
include: {
|
||||||
|
model: TagModel,
|
||||||
|
as: 'tags',
|
||||||
|
attributes: ['name'],
|
||||||
|
through: {
|
||||||
|
attributes: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const populatedSnippets = snippets.map(snippet => {
|
||||||
|
const rawSnippet = snippet.get({ plain: true });
|
||||||
|
let tags: string[] = [];
|
||||||
|
|
||||||
|
if (rawSnippet.tags) {
|
||||||
|
// @ts-ignore
|
||||||
|
const rawTags = rawSnippet.tags as { name: string }[];
|
||||||
|
tags = rawTags.map(tag => tag.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rawSnippet,
|
||||||
|
tags
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: populatedSnippets
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
38
src/controllers/snippets/getRawCode.ts
Normal file
38
src/controllers/snippets/getRawCode.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel } from '../../models';
|
||||||
|
import { ErrorResponse } from '../../utils';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get raw snippet code
|
||||||
|
* @route /api/snippets/raw/:id
|
||||||
|
* @request GET
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const getRawCode = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: Request<Params>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const snippet = await SnippetModel.findOne({
|
||||||
|
where: { id: req.params.id },
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!snippet) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(
|
||||||
|
404,
|
||||||
|
`Snippet with the id of ${req.params.id} was not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).send(snippet.code);
|
||||||
|
}
|
||||||
|
);
|
||||||
66
src/controllers/snippets/getSnippet.ts
Normal file
66
src/controllers/snippets/getSnippet.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel, TagModel } from '../../models';
|
||||||
|
import { UserInfoRequest } from '../../typescript/interfaces';
|
||||||
|
import { ErrorResponse } from '../../utils';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get single snippet by id
|
||||||
|
* @route /api/snippets/:id
|
||||||
|
* @request GET
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const getSnippet = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: UserInfoRequest<{}, Params>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const snippet = await SnippetModel.findOne({
|
||||||
|
where: { id: req.params.id },
|
||||||
|
include: {
|
||||||
|
model: TagModel,
|
||||||
|
as: 'tags',
|
||||||
|
attributes: ['name'],
|
||||||
|
through: {
|
||||||
|
attributes: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!snippet) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(
|
||||||
|
404,
|
||||||
|
`Snippet with the id of ${req.params.id} was not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snippet.createdBy != req.user.id && !req.user.isAdmin) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(401, `You are not authorized to access this resource`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawSnippet = snippet.get({ plain: true });
|
||||||
|
|
||||||
|
if (rawSnippet.tags) {
|
||||||
|
// @ts-ignore
|
||||||
|
const rawTags = rawSnippet.tags as { name: string }[];
|
||||||
|
|
||||||
|
const populatedSnippet = {
|
||||||
|
...rawSnippet,
|
||||||
|
tags: rawTags.map(tag => tag.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: populatedSnippet
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
8
src/controllers/snippets/index.ts
Normal file
8
src/controllers/snippets/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export * from './createSnippet';
|
||||||
|
export * from './getAllSnippets';
|
||||||
|
export * from './getSnippet';
|
||||||
|
export * from './deleteSnippet';
|
||||||
|
export * from './countTags';
|
||||||
|
export * from './getRawCode';
|
||||||
|
export * from './searchSnippets';
|
||||||
|
export * from './updateSnippet';
|
||||||
72
src/controllers/snippets/searchSnippets.ts
Normal file
72
src/controllers/snippets/searchSnippets.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel, TagModel } from '../../models';
|
||||||
|
import { Op } from 'sequelize';
|
||||||
|
|
||||||
|
interface Body {
|
||||||
|
query: string;
|
||||||
|
tags: string[];
|
||||||
|
languages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Search snippets
|
||||||
|
* @route /api/snippets/search
|
||||||
|
* @request POST
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const searchSnippets = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: Request<{}, {}, Body>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const { query, tags, languages } = req.body;
|
||||||
|
|
||||||
|
// Check if query is empty
|
||||||
|
if (query === '' && !tags.length && !languages.length) {
|
||||||
|
res.status(200).json({
|
||||||
|
data: []
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const languageFilter = languages.length
|
||||||
|
? { [Op.in]: languages }
|
||||||
|
: { [Op.notIn]: languages };
|
||||||
|
|
||||||
|
const tagFilter = tags.length ? { [Op.in]: tags } : { [Op.notIn]: tags };
|
||||||
|
|
||||||
|
const snippets = await SnippetModel.findAll({
|
||||||
|
where: {
|
||||||
|
[Op.and]: [
|
||||||
|
{
|
||||||
|
[Op.or]: [
|
||||||
|
{ title: { [Op.substring]: `${query}` } },
|
||||||
|
{ description: { [Op.substring]: `${query}` } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
language: languageFilter
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
model: TagModel,
|
||||||
|
as: 'tags',
|
||||||
|
attributes: ['name'],
|
||||||
|
where: {
|
||||||
|
name: tagFilter
|
||||||
|
},
|
||||||
|
through: {
|
||||||
|
attributes: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: snippets
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
70
src/controllers/snippets/updateSnippet.ts
Normal file
70
src/controllers/snippets/updateSnippet.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { SnippetModel, Snippet_TagModel } from '../../models';
|
||||||
|
import { ErrorResponse, tagParser, createTags } from '../../utils';
|
||||||
|
import { Snippet, UserInfoRequest } from '../../typescript/interfaces';
|
||||||
|
|
||||||
|
interface Body extends Snippet {}
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Update snippet
|
||||||
|
* @route /api/snippets/:id
|
||||||
|
* @request PUT
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
export const updateSnippet = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: UserInfoRequest<Body, Params>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
let snippet = await SnippetModel.findOne({
|
||||||
|
where: { id: req.params.id }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!snippet) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(
|
||||||
|
404,
|
||||||
|
`Snippet with the id of ${req.params.id} was not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snippet.createdBy != req.user.id && !req.user.isAdmin) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(401, `You are not authorized to modify this resource`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tags from request body
|
||||||
|
const { language, tags: requestTags = [] } = req.body;
|
||||||
|
let parsedRequestTags = tagParser([...requestTags, language.toLowerCase()]);
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// check if tags and/or lang was changed
|
||||||
|
|
||||||
|
// Update snippet
|
||||||
|
snippet = await snippet.update({
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete old tags and create new ones
|
||||||
|
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
|
||||||
|
await createTags(parsedRequestTags, snippet.id);
|
||||||
|
|
||||||
|
// Get raw snippet values
|
||||||
|
const rawSnippet = snippet.get({ plain: true });
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: {
|
||||||
|
...rawSnippet,
|
||||||
|
tags: [...parsedRequestTags]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,29 +1,34 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
|
import { authenticate, requireBody } from '../middleware';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
countTags,
|
|
||||||
createSnippet,
|
createSnippet,
|
||||||
deleteSnippet,
|
|
||||||
getAllSnippets,
|
getAllSnippets,
|
||||||
getRawCode,
|
|
||||||
getSnippet,
|
getSnippet,
|
||||||
|
deleteSnippet,
|
||||||
|
countTags,
|
||||||
|
getRawCode,
|
||||||
searchSnippets,
|
searchSnippets,
|
||||||
updateSnippet
|
updateSnippet
|
||||||
} from '../controllers/snippets';
|
} from '../controllers/snippets/';
|
||||||
import { requireBody } from '../middleware';
|
|
||||||
|
|
||||||
export const snippetRouter = Router();
|
export const snippetRouter = Router();
|
||||||
|
|
||||||
snippetRouter
|
snippetRouter
|
||||||
.route('/')
|
.route('/')
|
||||||
.post(requireBody('title', 'language', 'code'), createSnippet)
|
.post(
|
||||||
|
authenticate,
|
||||||
|
requireBody('title', 'language', 'code', 'tags'),
|
||||||
|
createSnippet
|
||||||
|
)
|
||||||
.get(getAllSnippets);
|
.get(getAllSnippets);
|
||||||
|
|
||||||
snippetRouter
|
snippetRouter
|
||||||
.route('/:id')
|
.route('/:id')
|
||||||
.get(getSnippet)
|
.get(authenticate, getSnippet)
|
||||||
.put(updateSnippet)
|
.put(authenticate, updateSnippet)
|
||||||
.delete(deleteSnippet);
|
.delete(authenticate, deleteSnippet);
|
||||||
|
|
||||||
snippetRouter.route('/statistics/count').get(countTags);
|
snippetRouter.route('/statistics/count').get(countTags);
|
||||||
snippetRouter.route('/raw/:id').get(getRawCode);
|
snippetRouter.route('/raw/:id').get(getRawCode);
|
||||||
snippetRouter.route('/search').post(searchSnippets);
|
snippetRouter.route('/search').post(searchSnippets);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
|
|
||||||
export interface UserInfoRequest<body = {}> extends Request<{}, {}, body> {
|
export interface UserInfoRequest<body = {}, params = {}>
|
||||||
|
extends Request<params, {}, body> {
|
||||||
user: {
|
user: {
|
||||||
id: number;
|
id: number;
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ export interface Snippet extends Model {
|
|||||||
code: string;
|
code: string;
|
||||||
docs: string;
|
docs: string;
|
||||||
isPinned: number;
|
isPinned: number;
|
||||||
tags?: { name: string }[];
|
tags?: string[];
|
||||||
createdBy: number;
|
createdBy: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SnippetCreationAttributes
|
export interface SnippetCreationAttributes
|
||||||
extends Optional<Snippet, 'id' | 'createdAt' | 'updatedAt'> {}
|
extends Optional<Snippet, 'id' | 'createdAt' | 'updatedAt' | 'tags'> {}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const signToken = (data: Token): string => {
|
|||||||
const secret = process.env.JWT_SECRET || 'secret';
|
const secret = process.env.JWT_SECRET || 'secret';
|
||||||
|
|
||||||
const token = sign(data, secret, {
|
const token = sign(data, secret, {
|
||||||
expiresIn: '30d'
|
expiresIn: '14d'
|
||||||
});
|
});
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
Reference in New Issue
Block a user