mirror of https://github.com/MaxLeiter/Drift
Merge pull request #40 from maxall41/better-validation
server: Better validation using celebratepull/52/head
commit
056a2bd3ce
@ -1,57 +1,72 @@
|
||||
import { Router } from 'express'
|
||||
import secretKey from '../lib/middleware/secret-key';
|
||||
import { File } from '../lib/models/File'
|
||||
import { celebrate, Joi } from "celebrate";
|
||||
import { Router } from "express";
|
||||
import { File } from "@lib/models/File";
|
||||
import secretKey from "@lib/middleware/secret-key";
|
||||
|
||||
export const files = Router()
|
||||
export const files = Router();
|
||||
|
||||
files.get("/raw/:id", secretKey, async (req, res, next) => {
|
||||
files.get("/raw/:id",
|
||||
celebrate({
|
||||
params: {
|
||||
id: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
secretKey,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const file = await File.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
},
|
||||
attributes: ["title", "content"],
|
||||
})
|
||||
|
||||
|
||||
if (!file) {
|
||||
return res.status(404).json({ error: "File not found" })
|
||||
}
|
||||
|
||||
// TODO: JWT-checkraw files
|
||||
if (file?.post?.visibility === "private") {
|
||||
// jwt(req as UserJwtRequest, res, () => {
|
||||
// res.json(file);
|
||||
// })
|
||||
res.json(file);
|
||||
} else {
|
||||
res.json(file);
|
||||
}
|
||||
const file = await File.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
},
|
||||
attributes: ["title", "content"],
|
||||
})
|
||||
|
||||
if (!file) {
|
||||
return res.status(404).json({ error: "File not found" })
|
||||
}
|
||||
|
||||
// TODO: JWT-checkraw files
|
||||
if (file?.post?.visibility === "private") {
|
||||
// jwt(req as UserJwtRequest, res, () => {
|
||||
// res.json(file);
|
||||
// })
|
||||
res.json(file);
|
||||
} else {
|
||||
res.json(file);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
next(e);
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
files.get("/html/:id", async (req, res, next) => {
|
||||
files.get("/html/:id",
|
||||
celebrate({
|
||||
params: {
|
||||
id: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const file = await File.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
},
|
||||
attributes: ["html"],
|
||||
})
|
||||
|
||||
if (!file) {
|
||||
return res.status(404).json({ error: "File not found" })
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'text/plain')
|
||||
res.setHeader('Cache-Control', 'public, max-age=4800')
|
||||
res.status(200).write(file.html)
|
||||
res.end()
|
||||
const file = await File.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
},
|
||||
attributes: ["html"],
|
||||
})
|
||||
|
||||
if (!file) {
|
||||
return res.status(404).json({ error: "File not found" })
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'text/plain')
|
||||
res.setHeader('Cache-Control', 'public, max-age=4800')
|
||||
res.status(200).write(file.html)
|
||||
res.end()
|
||||
} catch (error) {
|
||||
next(error)
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export { auth } from './auth';
|
||||
export { posts } from './posts';
|
||||
export { users } from './users';
|
||||
export { files } from './files';
|
||||
export { auth } from "./auth";
|
||||
export { posts } from "./posts";
|
||||
export { users } from "./users";
|
||||
export { files } from "./files";
|
||||
|
||||
@ -1,182 +1,209 @@
|
||||
import { Router } from 'express'
|
||||
// import { Movie } from '../models/Post'
|
||||
import { File } from '../lib/models/File'
|
||||
import { Post } from '../lib/models/Post';
|
||||
import jwt, { UserJwtRequest } from '../lib/middleware/jwt';
|
||||
import { Router } from "express";
|
||||
import { celebrate, Joi } from "celebrate";
|
||||
import { File } from '@lib/models/File'
|
||||
import { Post } from '@lib/models/Post';
|
||||
import jwt, { UserJwtRequest } from '@lib/middleware/jwt';
|
||||
import * as crypto from "crypto";
|
||||
import { User } from '../lib/models/User';
|
||||
import secretKey from '../lib/middleware/secret-key';
|
||||
import markdown from '../lib/render-markdown';
|
||||
|
||||
export const posts = Router()
|
||||
|
||||
posts.post('/create', jwt, async (req, res, next) => {
|
||||
import { User } from '@lib/models/User';
|
||||
import secretKey from '@lib/middleware/secret-key';
|
||||
import markdown from '@lib/render-markdown';
|
||||
|
||||
export const posts = Router();
|
||||
|
||||
const postVisibilitySchema = (value: string) => {
|
||||
if (value === 'public' || value === 'private') {
|
||||
return value;
|
||||
} else {
|
||||
throw new Error('Invalid post visibility');
|
||||
}
|
||||
}
|
||||
|
||||
posts.post(
|
||||
"/create",
|
||||
jwt,
|
||||
celebrate({
|
||||
body: {
|
||||
title: Joi.string().required(),
|
||||
files: Joi.any().required(),
|
||||
visibility: Joi.string().custom(postVisibilitySchema, 'valid visibility').required(),
|
||||
userId: Joi.string().required(),
|
||||
password: Joi.string().optional(),
|
||||
},
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
if (!req.body.files) {
|
||||
throw new Error("Please provide files.")
|
||||
}
|
||||
|
||||
if (!req.body.title) {
|
||||
throw new Error("Please provide a title.")
|
||||
}
|
||||
|
||||
if (!req.body.userId) {
|
||||
throw new Error("No user id provided.")
|
||||
}
|
||||
|
||||
if (!req.body.visibility) {
|
||||
throw new Error("Please provide a visibility.")
|
||||
}
|
||||
|
||||
if (req.body.visibility === 'protected' && !req.body.password) {
|
||||
throw new Error("Please provide a password.")
|
||||
}
|
||||
|
||||
let hashedPassword: string = ''
|
||||
if (req.body.visibility === 'protected') {
|
||||
hashedPassword = crypto.createHash('sha256').update(req.body.password).digest('hex');
|
||||
}
|
||||
|
||||
const newPost = new Post({
|
||||
title: req.body.title,
|
||||
visibility: req.body.visibility,
|
||||
password: hashedPassword,
|
||||
let hashedPassword: string = ''
|
||||
if (req.body.visibility === 'protected') {
|
||||
hashedPassword = crypto.createHash('sha256').update(req.body.password).digest('hex');
|
||||
}
|
||||
|
||||
const newPost = new Post({
|
||||
title: req.body.title,
|
||||
visibility: req.body.visibility,
|
||||
password: hashedPassword,
|
||||
})
|
||||
|
||||
await newPost.save()
|
||||
await newPost.$add('users', req.body.userId);
|
||||
const newFiles = await Promise.all(req.body.files.map(async (file) => {
|
||||
const html = getHtmlFromFile(file);
|
||||
const newFile = new File({
|
||||
title: file.title,
|
||||
content: file.content,
|
||||
sha: crypto.createHash('sha256').update(file.content).digest('hex').toString(),
|
||||
html
|
||||
})
|
||||
|
||||
await newPost.save()
|
||||
await newPost.$add('users', req.body.userId);
|
||||
const newFiles = await Promise.all(req.body.files.map(async (file) => {
|
||||
const renderAsMarkdown = ['markdown', 'md', 'mdown', 'mkdn', 'mkd', 'mdwn', 'mdtxt', 'mdtext', 'text', '']
|
||||
const fileType = () => {
|
||||
const pathParts = file.title.split(".")
|
||||
const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : ""
|
||||
return language
|
||||
}
|
||||
const type = fileType()
|
||||
let contentToRender: string = (file.content || '');
|
||||
await newFile.$set("user", req.body.userId);
|
||||
await newFile.$set("post", newPost.id);
|
||||
await newFile.save();
|
||||
return newFile;
|
||||
}))
|
||||
|
||||
if (!renderAsMarkdown.includes(type)) {
|
||||
contentToRender =
|
||||
`~~~${type}
|
||||
${file.content}
|
||||
~~~`
|
||||
} else {
|
||||
contentToRender = '\n' + file.content;
|
||||
}
|
||||
const html = markdown(contentToRender)
|
||||
const newFile = new File({
|
||||
title: file.title,
|
||||
content: file.content,
|
||||
sha: crypto.createHash('sha256').update(file.content).digest('hex').toString(),
|
||||
html
|
||||
})
|
||||
|
||||
await newFile.$set("user", req.body.userId);
|
||||
await newFile.$set("post", newPost.id);
|
||||
await newFile.save();
|
||||
return newFile;
|
||||
}))
|
||||
|
||||
await Promise.all(newFiles.map((file) => {
|
||||
newPost.$add("files", file.id);
|
||||
newPost.save();
|
||||
}))
|
||||
|
||||
res.json(newPost);
|
||||
await Promise.all(newFiles.map((file) => {
|
||||
newPost.$add("files", file.id);
|
||||
newPost.save();
|
||||
}))
|
||||
|
||||
res.json(newPost);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
posts.get("/", secretKey, async (req, res, next) => {
|
||||
try {
|
||||
const posts = await Post.findAll({
|
||||
attributes: ["id", "title", "visibility", "createdAt"],
|
||||
})
|
||||
res.json(posts);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
try {
|
||||
const posts = await Post.findAll({
|
||||
attributes: ["id", "title", "visibility", "createdAt"],
|
||||
})
|
||||
|
||||
res.json(posts);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
posts.get("/mine", jwt, secretKey, async (req: UserJwtRequest, res, next) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: "Unauthorized" })
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: "Unauthorized" })
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await User.findByPk(req.user.id, {
|
||||
include: [
|
||||
{
|
||||
model: Post,
|
||||
as: "posts",
|
||||
include: [
|
||||
{
|
||||
model: File,
|
||||
as: "files"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
})
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" })
|
||||
}
|
||||
return res.json(user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
posts.get(
|
||||
"/:id",
|
||||
celebrate({
|
||||
params: {
|
||||
id: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
async (req: UserJwtRequest, res, next) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.user.id, {
|
||||
include: [
|
||||
{
|
||||
model: Post,
|
||||
as: "posts",
|
||||
include: [
|
||||
{
|
||||
model: File,
|
||||
as: "files"
|
||||
}
|
||||
]
|
||||
},
|
||||
const post = await Post.findOne({
|
||||
where: {
|
||||
id: req.params.id,
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: File,
|
||||
as: "files",
|
||||
attributes: [
|
||||
"id",
|
||||
"title",
|
||||
"content",
|
||||
"sha",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
],
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: "users",
|
||||
attributes: ["id", "username"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: "Post not found" })
|
||||
}
|
||||
|
||||
// if public or unlisted, cache
|
||||
if (post.visibility === 'public' || post.visibility === 'unlisted') {
|
||||
res.set('Cache-Control', 'public, max-age=4800')
|
||||
}
|
||||
|
||||
if (post.visibility === 'public' || post?.visibility === 'unlisted') {
|
||||
secretKey(req, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" })
|
||||
}
|
||||
return res.json(user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()))
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
posts.get("/:id", async (req, res, next) => {
|
||||
try {
|
||||
const post = await Post.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: File,
|
||||
as: "files",
|
||||
attributes: ["id", "title", "content", "sha", "createdAt", "updatedAt"],
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: "users",
|
||||
attributes: ["id", "username"],
|
||||
},
|
||||
]
|
||||
} else if (post.visibility === 'private') {
|
||||
jwt(req as UserJwtRequest, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
|
||||
if (!post) {
|
||||
throw new Error("Post not found.")
|
||||
}
|
||||
|
||||
|
||||
if (post.visibility === 'public' || post?.visibility === 'unlisted') {
|
||||
secretKey(req, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
} else if (post.visibility === 'private') {
|
||||
jwt(req as UserJwtRequest, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
} else if (post.visibility === 'protected') {
|
||||
const { password } = req.query
|
||||
if (!password || typeof password !== 'string') {
|
||||
return jwt(req as UserJwtRequest, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
}
|
||||
const hash = crypto.createHash('sha256').update(password).digest('hex').toString()
|
||||
if (hash !== post.password) {
|
||||
return res.status(400).json({ error: "Incorrect password." })
|
||||
}
|
||||
|
||||
} else if (post.visibility === 'protected') {
|
||||
const { password } = req.query
|
||||
if (!password || typeof password !== 'string') {
|
||||
return jwt(req as UserJwtRequest, res, () => {
|
||||
res.json(post);
|
||||
})
|
||||
}
|
||||
const hash = crypto.createHash('sha256').update(password).digest('hex').toString()
|
||||
if (hash !== post.password) {
|
||||
return res.status(400).json({ error: "Incorrect password." })
|
||||
}
|
||||
|
||||
res.json(post);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
next(e);
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function getHtmlFromFile(file: any) {
|
||||
const renderAsMarkdown = ['markdown', 'md', 'mdown', 'mkdn', 'mkd', 'mdwn', 'mdtxt', 'mdtext', 'text', ''];
|
||||
const fileType = () => {
|
||||
const pathParts = file.title.split(".");
|
||||
const language = pathParts.length > 1 ? pathParts[pathParts.length - 1] : "";
|
||||
return language;
|
||||
};
|
||||
const type = fileType();
|
||||
let contentToRender: string = (file.content || '');
|
||||
|
||||
if (!renderAsMarkdown.includes(type)) {
|
||||
contentToRender =
|
||||
`~~~${type}
|
||||
${file.content}
|
||||
~~~`;
|
||||
} else {
|
||||
contentToRender = '\n' + file.content;
|
||||
}
|
||||
const html = markdown(contentToRender);
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import { Router } from 'express'
|
||||
// import { Movie } from '../models/Post'
|
||||
import { User } from '../lib/models/User'
|
||||
import jwt from '../lib/middleware/jwt'
|
||||
import { Router } from "express";
|
||||
// import jwt from "@lib/middleware/jwt";
|
||||
// import { User } from "@lib/models/User";
|
||||
|
||||
export const users = Router()
|
||||
|
||||
users.get('/', jwt, async (req, res, next) => {
|
||||
try {
|
||||
const allUsers = await User.findAll()
|
||||
res.json(allUsers)
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
export const users = Router();
|
||||
|
||||
// users.get("/", jwt, async (req, res, next) => {
|
||||
// try {
|
||||
// const allUsers = await User.findAll();
|
||||
// res.json(allUsers);
|
||||
// } catch (error) {
|
||||
// next(error);
|
||||
// }
|
||||
// });
|
||||
|
||||
Loading…
Reference in New Issue