-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bee41b3
commit 914d264
Showing
14 changed files
with
430 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,12 @@ const mongoose = require('mongoose'); | |
const path = require('path'); | ||
const multer = require('multer'); | ||
const { v4: uuidv4 } = require('uuid'); | ||
const { graphqlHTTP } = require('express-graphql'); | ||
|
||
const feedRoutes = require('./router/feed'); | ||
const authRoutes = require('./router/auth'); | ||
const graphqlSchema = require('./graphql/schema'); | ||
const graphqlResolver = require('./graphql/resolvers'); | ||
const auth = require('./middleware/auth'); | ||
const { clearImage } = require('./utils/file'); | ||
|
||
const MONGODB_URI = | ||
'mongodb+srv://alterego:[email protected]/messages?retryWrites=true&w=majority'; | ||
|
@@ -48,11 +51,41 @@ app.use((req, res, next) => { | |
'GET, POST, PUT, PATCH, DELETE' | ||
); | ||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); | ||
if (req.method === 'OPTIONS') { | ||
return res.sendStatus(200); | ||
} | ||
next(); | ||
}); | ||
|
||
app.use('/feed', feedRoutes); | ||
app.use('/auth', authRoutes); | ||
app.use(auth); | ||
|
||
app.put('/post-image', (req, res, next) => { | ||
if (!req.isAuth) throw new Error('Not authenticated'); | ||
if (!req.file) res.status(200).json({ message: 'No image provided' }); | ||
if (req.body.oldPath) clearImage(req.body.oldPath); | ||
return res.status(201).json({ | ||
message: 'File stored', | ||
filePath: req.file.path.replace('\\', '/'), | ||
}); | ||
}); | ||
|
||
app.use( | ||
'/graphql', | ||
graphqlHTTP({ | ||
schema: graphqlSchema, | ||
rootValue: graphqlResolver, | ||
graphiql: true, | ||
customFormatErrorFn(err) { | ||
if (!err.originalError) { | ||
return err; | ||
} | ||
const data = err.originalError.data; | ||
const message = err.message || 'An error occurred'; | ||
const code = err.originalError.code || 500; | ||
return { message: message, status: code, data: data }; | ||
}, | ||
}) | ||
); | ||
|
||
app.use((error, req, res, next) => { | ||
console.log(error); | ||
|
@@ -65,12 +98,6 @@ app.use((error, req, res, next) => { | |
mongoose | ||
.connect(MONGODB_URI) | ||
.then(() => { | ||
const server = app.listen(8080); | ||
const io = require('./socket').init(server, { | ||
cors: { origin: 'http://localhost:3000', methods: ['GET', 'POST'] }, | ||
}); | ||
io.on('connection', (socket) => { | ||
console.log('Client connected.'); | ||
}); | ||
app.listen(8080); | ||
}) | ||
.catch((err) => console.log(err)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
const bcrypt = require('bcryptjs'); | ||
const validator = require('validator'); | ||
const jwt = require('jsonwebtoken'); | ||
|
||
const User = require('../models/user'); | ||
const Post = require('../models/post'); | ||
const { clearImage } = require('../utils/file'); | ||
|
||
module.exports = { | ||
createUser: async function ({ userInput }, req) { | ||
const errors = []; | ||
if (!validator.isEmail(userInput.email)) { | ||
errors.push({ message: 'Email is invalid' }); | ||
} | ||
if (!validator.isLength(userInput.password, { min: 5 })) { | ||
errors.push({ message: 'Password is too short' }); | ||
} | ||
if (errors.length > 0) { | ||
const error = new Error('Invalid input'); | ||
error.data = errors; | ||
error.code = 422; | ||
throw error; | ||
} | ||
const existingUser = await User.findOne({ email: userInput.email }); | ||
if (existingUser) { | ||
throw new Error('User exist already'); | ||
} | ||
const hashedPw = await bcrypt.hash(userInput.password, 12); | ||
|
||
const user = new User({ | ||
email: userInput.email, | ||
name: userInput.name, | ||
password: hashedPw, | ||
}); | ||
const createdUser = await user.save(); | ||
return { ...createdUser._doc, _id: createdUser._id.toString() }; | ||
}, | ||
login: async function ({ email, password }) { | ||
const user = await User.findOne({ email: email }); | ||
if (!user) { | ||
const error = new Error('User not found.'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const isEqual = await bcrypt.compare(password, user.password); | ||
if (!isEqual) { | ||
const error = new Error('Password is incorrect'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const token = jwt.sign( | ||
{ | ||
userId: user._id.toString(), | ||
email: user.email, | ||
}, | ||
'secret', | ||
{ expiresIn: '1h' } | ||
); | ||
return { token: token, userId: user._id.toString() }; | ||
}, | ||
createPost: async function ({ postInput }, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const errors = []; | ||
if (!validator.isLength(postInput.title, { min: 5 })) { | ||
errors.push({ message: 'Title is too short' }); | ||
} | ||
if (!validator.isLength(postInput.content, { min: 5 })) { | ||
errors.push({ message: 'Content is too short' }); | ||
} | ||
if (errors.length > 0) { | ||
const error = new Error('Invalid input'); | ||
error.data = errors; | ||
error.code = 422; | ||
throw error; | ||
} | ||
const user = await User.findById(req.userId); | ||
if (!user) { | ||
const error = new Error('No such user'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const post = new Post({ | ||
title: postInput.title, | ||
imageUrl: postInput.imageUrl, | ||
content: postInput.content, | ||
creator: user, | ||
}); | ||
await post.save(); | ||
user.posts.push(post); | ||
await user.save(); | ||
return { ...post._doc, createdAt: post.createdAt.toDateString() }; | ||
}, | ||
showPosts: async function ({ page }, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
if (!page) page = 1; | ||
const perPage = 2; | ||
const postsCount = await Post.find().countDocuments(); | ||
const posts = await Post.find() | ||
.sort({ createdAt: -1 }) | ||
.skip((page - 1) * perPage) | ||
.limit(perPage) | ||
.populate('creator'); | ||
return { | ||
posts: posts.map((post) => { | ||
return { ...post._doc, createdAt: post.createdAt.toDateString() }; | ||
}), | ||
totalPosts: postsCount, | ||
}; | ||
}, | ||
post: async function ({ id }, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const post = await Post.findById(id).populate('creator'); | ||
if (!post) { | ||
const error = new Error('No current post'); | ||
error.code = 404; | ||
throw error; | ||
} | ||
return { ...post._doc, createdAt: post.createdAt.toDateString() }; | ||
}, | ||
updatePost: async function ({ id, postInput }, req) { | ||
// if (!req.isAuth) { | ||
// const error = new Error('Not authenticated'); | ||
// error.code = 401; | ||
// throw error; | ||
// } | ||
const post = await Post.findById(id).populate('creator'); | ||
if (!post) { | ||
const error = new Error('No current post'); | ||
error.code = 404; | ||
throw error; | ||
} | ||
// if (post.creator._id.toString() !== req.userId.toString()) { | ||
// const error = new Error('Not authorized'); | ||
// error.code = 403; | ||
// throw error; | ||
// } | ||
const errors = []; | ||
if (!validator.isLength(postInput.title, { min: 5 })) { | ||
errors.push({ message: 'Title is too short' }); | ||
} | ||
if (!validator.isLength(postInput.content, { min: 5 })) { | ||
errors.push({ message: 'Content is too short' }); | ||
} | ||
if (errors.length > 0) { | ||
const error = new Error('Invalid input'); | ||
error.data = errors; | ||
error.code = 422; | ||
throw error; | ||
} | ||
post.title = postInput.title; | ||
post.content = postInput.content; | ||
if (postInput.imageUrl) { | ||
post.imageUrl = postInput.imageUrl; | ||
} else { | ||
postInput.imageUrl = post.imageUrl; | ||
} | ||
const updatedPost = await post.save(); | ||
return { | ||
...updatedPost._doc, | ||
createdAt: updatedPost.createdAt.toDateString(), | ||
}; | ||
}, | ||
deletePost: async function ({ id }, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const post = await Post.findById(id); | ||
if (!post) { | ||
const error = new Error('Post not found'); | ||
error.code = 404; | ||
throw error; | ||
} | ||
if (post.creator.toString() !== req.userId.toString()) { | ||
const error = new Error('Not authorized'); | ||
error.code = 403; | ||
throw error; | ||
} | ||
clearImage(post.imageUrl); | ||
await Post.findByIdAndDelete(id); | ||
const user = await User.findById(req.userId); | ||
user.posts.pull(id); | ||
await user.save(); | ||
return true; | ||
}, | ||
user: async function (args, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const user = await User.findById(req.userId); | ||
if (!user) { | ||
const error = new Error('User not found'); | ||
error.code = 404; | ||
throw error; | ||
} | ||
return user; | ||
}, | ||
newStatus: async function ({ status }, req) { | ||
if (!req.isAuth) { | ||
const error = new Error('Not authenticated'); | ||
error.code = 401; | ||
throw error; | ||
} | ||
const user = await User.findById(req.userId); | ||
if (!user) { | ||
const error = new Error('User not found'); | ||
error.code = 404; | ||
throw error; | ||
} | ||
const errors = []; | ||
if (!validator.isLength(status, { min: 1 })) { | ||
errors.push({ message: 'Status can not be empty' }); | ||
} | ||
if (errors.length > 0) { | ||
const error = new Error('Invalid input'); | ||
error.data = errors; | ||
error.code = 422; | ||
throw error; | ||
} | ||
user.status = status; | ||
await user.save(); | ||
return user; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
const { buildSchema } = require('graphql'); | ||
|
||
module.exports = buildSchema(` | ||
type Post { | ||
_id: ID! | ||
title: String! | ||
content: String! | ||
imageUrl: String! | ||
creator: User! | ||
createdAt: String! | ||
updatedAt: String! | ||
} | ||
type PostData { | ||
posts: [Post!]! | ||
totalPosts: Int! | ||
} | ||
type User { | ||
_id: ID! | ||
name: String! | ||
email: String! | ||
password: String | ||
status: String! | ||
posts: [Post!]! | ||
} | ||
type AuthData { | ||
token: String! | ||
userId: String! | ||
} | ||
input UserInputData { | ||
email: String! | ||
name: String! | ||
password: String! | ||
} | ||
input PostInputData { | ||
title: String! | ||
content: String! | ||
imageUrl: String | ||
} | ||
type RootQuery { | ||
login(email: String!, password: String!): AuthData! | ||
showPosts(page: Int): PostData | ||
post(id: ID!): Post! | ||
user: User! | ||
} | ||
type RootMutation { | ||
createUser(userInput: UserInputData): User! | ||
createPost(postInput: PostInputData): Post! | ||
updatePost(id: ID!, postInput: PostInputData): Post! | ||
deletePost(id: ID!): Boolean | ||
newStatus(status: String!): User! | ||
} | ||
schema { | ||
query: RootQuery | ||
mutation: RootMutation | ||
} | ||
`); |
Binary file added
BIN
+228 KB
images/25184ab8-5b3a-474e-8771-05f010ccb77b-photo_2023-12-01_21-41-46.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added
BIN
+347 KB
images/aed2f8fb-68e3-42e6-a85b-58df7eada558-photo_2022-08-31_20-17-10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed
BIN
-488 KB
...ac0-2871-4629-8fbb-3047ab1b1b1f-Снимок ÑкÑана 2022-12-30 174352.png
Binary file not shown.
Oops, something went wrong.