Browse Source

shop on base of current hipsta

Ivan Asmer 5 years ago
parent
commit
68a416d8ee
5 changed files with 412 additions and 592 deletions
  1. 18 217
      README.md
  2. 134 294
      index.js
  3. 71 19
      models.js
  4. 179 55
      package-lock.json
  5. 10 7
      package.json

+ 18 - 217
README.md

@@ -1,223 +1,24 @@
-Shop basic backend
-=====
+hipstagram
+===
 
-```
-        type Query {
-            login(login: String!, password: String!): String
-
-            categories: [Category]
-            category(_id: ID!): Category
-
-            goods: [Good]
-            good(_id: ID!): Good
-
-            myOrders: [Order]
-            orders: [Order]
-        }
-        type Mutation {
-            createUser(login: String!, password: String!): User
-            changePassword(password: String!): User
-
-            setCategory(cat: CategoryInput!):Category
-            setGood(good: GoodInput!):Good
-            setOrder(order: OrderInput):Order
-            setOrderGood(orderGood: OrderGoodInput):OrderGood
-        }
-
-        type User {
-             _id: String
-             login: String
-             nick : String
-             orders: [Order]
-        }
-
-        type Category {
-            _id: ID,
-            name: String!,
-            goods: [Good]
-        }
-
-        type Good {
-            _id: ID,
-            name: String!,
-            description: String
-            price: Float
-            orderGoods: [OrderGood]
-            categories: [Category]
-        }
-
-        type OrderGood {
-            _id: ID,
-            price: Float,
-            count: Float,
-            good: Good,
-            order: Order
-        }
-
-        type Order {
-            _id: ID
-            orderGoods: [OrderGood]
-        }
-
-
-        input UserInput {
-             _id: String
-             login: String
-             nick : String
-        }
-
-        input CategoryInput {
-            _id: ID,
-            name: String!,
-            goods: [ID]
-        }
-
-        input GoodInput {
-            _id: ID,
-            name: String!,
-            description: String
-            price: Float
-            categories: [ID]
-        }
-
-        input OrderGoodInput {
-            _id: ID,
-            count: Int!,
-            good: ID!,
-            order: ID!
-        }
-
-        input OrderInput {
-            _id: ID
-            orderGoods: [ID]
-        }
-```
-
-
-примеры запросов:
-```
-mutation createUser($login:String!, $password:String!){
-  createUser(login:$login, password:$password){
-    _id, login    
-  }  
-}
-
-query login($login:String!, $password:String!){
-  login(login:$login, password:$password)
-}
-
-mutation pwd($password:String!){
-  changePassword(password: $password){
-    _id, login
-	}
-}
-
-mutation cc($cat:CategoryInput!){
-  setCategory(cat:$cat){
-    _id, name
-  }
-}
-
-query cats{
-  categories{
-    _id, name, goods{
-      name, description
-    }
-  }
-}
-
-query cat($_id:ID!){
-  category(_id:$_id){
-    _id, name
-  }
-}
-
-
-mutation cg($good:GoodInput!){
-  setGood(good:$good){
-    _id, name, description, price
-  }
-}
-
-query goods{
-  goods{
-    _id, name, description
-  }
-}
-
-query good($_id:ID!){
-  good(_id:$_id){
-    _id, name, description
-  }
-}
-
-mutation sO($order:OrderInput!){
-  setOrder(order:$order){
-		_id   
-		orderGoods{
-      _id, price, count,
-      good{
-        _id, name
-      }
-    }
-  }
-}
-
-mutation sOG($orderGood:OrderGoodInput!){
-  setOrderGood(orderGood:$orderGood){
-		_id,
-		good{
-      _id, name, price
-    },
-		price,
-		order{
-      _id
-    }
-	}
-}
-
-query mO{
-  myOrders{
-    _id,
-    orderGoods{
-      _id,
-			price,
-      count
-      good{
-        name, price
-      }
-    }
-  }
-}
-```
+**Mock Back for instagram-like service**
 
+Basic relations
+----
 
-переменные:
 
 ```
-{
-  "login": "foo",
-  "password": "bar",
-	"cat": {
-		"_id": "5d3868a23aa7106a3a4369e6",
-		"name": "Cars",
-    "goods":["5d3874e8271cb784ad1d65a7"]
-  },
-	"_id": "5d3874e8271cb784ad1d65a7",
-	"good": {
-	  "_id": "5d3874e8271cb784ad1d65a7",
-    "name": "iPhone",
-	   "description": "apple",
-		"price": 500,
-		"categories": ["5d3869cc216a156eba15b8db"]
-  },
-  "order":{
-  
-  },
-	"orderGood": {
-    "good": "5d3874e8271cb784ad1d65a7",
-		"order": "5d388f0786f70ff605a78700",
-		"count": 2
-  }
-}
+User: Owner, Following, Followers, Like, Direct, Image(avatar)
+Post: Images, Like, Comments, Directs, Collections
+Images: Avatar, Posts, Direct
+Comments: Post, Comment (tree), Like
+Like: Post, Comment, User, Direct
+Direct: User, Owner, Post?, Image?, Like
+Collection: Posts
+Storis: видосики
+
+
+Пермишены:
+User: public, private (Follow Approve by user)
+Post: public, private
 ```

+ 134 - 294
index.js

@@ -1,348 +1,188 @@
-const ObjectID    = require("mongodb").ObjectID;
-const jwt         = require('jsonwebtoken')
-const jwtSecret   = 'CbymrfGfnB'
+const jwtSecret   = 'AjnjLhjxM'
 
-const anonResolvers = ['login', 'createUser'];
+const express           = require('express');
+const express_graphql   = require('express-graphql');
 
+const { buildSchema, printSchema } = require('graphql');
+const expand = require('mm-graphql/expand')
+const fs     = require('fs')
+const uploadPath = `${__dirname}/public/images/`;
+const upload  = require('multer')({ dest: uploadPath })
 
-;(async () => {
-    const {Savable, slice, getModels} = await require('./models.js')()
-
-    class User extends Savable {
-        static get relations(){
-            return {
-            }
-        }
-    }
-    Savable.addClass(User)
-
-    const express = require('express');
-    const express_graphql = require('express-graphql');
-    const { buildSchema } = require('graphql');
-
-
-    const schema = buildSchema(`
-        type Query {
-            login(login: String!, password: String!): String
-
-            categories: [Category]
-            category(_id: ID!): Category
 
-            goods: [Good]
-            good(_id: ID!): Good
+;(async () => {
 
-            myOrders: [Order]
-            orders: [Order]
-        }
-        type Mutation {
-            createUser(login: String!, password: String!): User
-            changePassword(password: String!): User
+    const {Savable, slice, getModels} = await require('./models.js')()
+    const { jwtGQL, jwtCheck } = require('mm-graphql/jwt')
 
-            setCategory(cat: CategoryInput!):Category
-            setGood(good: GoodInput!):Good
-            setOrder(order: OrderInput):Order
-            setOrderGood(orderGood: OrderGoodInput):OrderGood
-        }
+    const {anonSchema, anonResolvers} = require('./anon')({Savable, secret: jwtSecret})
 
+    let schema = buildSchema(`
         type User {
              _id: String
              createdAt: String
              login: String
              nick : String
-             orders: [Order]
+             avatar: Image
+             likes: [Like]
+             likesCount: Int
+             incomings: [Direct]
+             followers: [User]
+             following: [User]
         }
 
-        type Category {
-            _id: ID,
-            createdAt: String
-            name: String!,
-            goods: [Good]
+        input UserInput {
+             _id: String
+             login: String
+             nick : String
+             avatar: ImageInput
+             following: [UserInput]
         }
 
-        type Good {
+        type Like {
             _id: ID,
-            createdAt: String
-            name: String!,
-            description: String
-            price: Float
-            imgUrls: [String]
-            orderGoods: [OrderGood]
-            categories: [Category]
+            post: Post,
+            comment: Comment,
+            direct: Direct,
+            owner: User
         }
 
-        type OrderGood {
+        input LikeInput {
             _id: ID,
-            createdAt: String
-            price: Float,
-            count: Float,
-            good: Good,
-            order: Order
-            total: Float
+            post: PostInput,
+            comment: CommentInput,
+            direct: DirectInput,
+            user: UserInput,
         }
 
-        type Order {
-            _id: ID
+        type Post {
+            _id: ID,
             createdAt: String
-            orderGoods: [OrderGood]
-            total: Float
+            title: String
+            text: String,
+            images: [Image],
+            comments: [Comment],
+            directs: [Direct],
+            collections: [Collection]
+            likes: [Like]
+            likesCount: Int
+            owner: User
+        }
+
+        input PostInput {
+            _id: ID,
+            title: String,
+            text: String,
+            images: [ImageInput],
+            comments: [CommentInput],
+            directs: [DirectInput],
+            collections: [CollectionInput]
         }
 
+        type Image {
+            _id: ID,
+            text: String,
+            url: String,
+            originalFileName: String,
+            userAvatar: User,
+            posts: [Post],
+            directs: [Direct]
+            owner: User
+        }
 
+        input ImageInput {
+            _id: ID,
+            text: String,
+            userAvatar: UserInput,
+            posts: [PostInput],
+            directs: [DirectInput]
+        }
 
+        type Comment {
+            _id: ID,
+            createdAt: String,
+            text: String,
+            post: Post,
+            answers: [Comment],
+            answerTo: Comment
+            likes: [Like]
+            likesCount: Int
+            owner: User
+        }
 
-
-        input UserInput {
-             _id: String
-             login: String
-             nick : String
+        input CommentInput {
+            _id: ID,
+            text: String,
+            post: PostInput,
+            answers: [CommentInput],
+            answerTo: CommentInput
         }
 
-        input CategoryInput {
+        type Direct {
             _id: ID,
-            name: String!,
-            goods: [ID]
+            createdAt: String,
+            text: String,
+            post: Post,
+            image: Image,
+            likes: [Like]
+            likesCount: Int
+            to: User
+            owner: User
         }
 
-        input GoodInput {
+        input DirectInput {
             _id: ID,
-            name: String!,
-            description: String
-            imgUrls: [String]
-            price: Float
-            categories: [ID]
+            text: String,
+            post: PostInput,
+            image: ImageInput,
+            likes: [LikeInput]
+            to: UserInput
         }
 
-        input OrderGoodInput {
+        type Collection {
             _id: ID,
-            count: Int!,
-            good: ID!,
-            order: ID!
+            text: String,
+            posts: [Post]
+            owner: User
         }
 
-        input OrderInput {
-            _id: ID
-            orderGoods: [ID]
+        input CollectionInput {
+            _id: ID,
+            text: String,
+            posts: [PostInput]
         }
     `);
 
+    schema = expand(schema)
+    console.log(printSchema(schema))
 
-
-    var app = express();
+    const app = express();
     app.use(express.static('public'));
-
-    const rootResolvers = {
-        createUser:async function ({login, password}){
-            let user =  await Savable.m.User.findOne({login, password})
-            if (user)
-                return null;
-            user = await (new User({login, password})).save()
-
-            user.___owner = user._id.toString()
-            user.___permissions = {
-                read: ["owner", "user"]
-            }
-
-            return await user.save()
-        },
-
-        login: async function({login, password}){
-            console.log(Savable.classes)
-            const user =  await Savable.m.User.findOne({login, password})
-            if (!user)
-                return null;
-
-            const token = jwt.sign({ sub: {id: user._id, login}}, jwtSecret); //подписывам токен нашим ключем
-            return token
-        },
-
-        changePassword:async function ({password}, {jwt: {id}, models: {SlicedSavable, User}} ){
-            id = new ObjectID(id)
-
-            const user =  await SlicedSavable.m.User.findOne({_id: id})
-            if (!user)
-                return null;
-            user.password = password;
-            return await user.save()
-        },
-
-        async setCategory({cat}, {jwt: {id}, models: {SlicedSavable, Category}}){
-            if ('_id' in cat){
-                let entity = await SlicedSavable.m.Category.findOne({_id: ObjectID(cat._id)})
-                console.log(entity)
-                if (entity){
-                    entity.name = cat.name
-                    if (cat.goods){
-                        entity.goods = []
-                        for (goodId of cat.goods){
-                            let good = await SlicedSavable.m.Good.findOne({_id: ObjectID(goodId)});
-                            good && entity.goods.push(good)
-                        }
-                    }
-                    return await entity.save()
-                }
-            }
-            return await (new Category(cat)).save()
-        },
-
-        async categories({}, {jwt: {id}, models: {SlicedSavable, Category}}){
-            categories = []
-            for (let category of SlicedSavable.m.Category.find({})){
-                try {category = await category} catch (e) { break }
-                categories.push(category)
-            }
-            return categories;
-        },
-
-        async category({_id}, {jwt: {id}, models: {SlicedSavable, Category}}){
-            return await SlicedSavable.m.Category.findOne({_id: ObjectID(_id)});
-        },
+    app.use('/graphql', express_graphql(jwtGQL({anonSchema, anonResolvers, schema, createContext: getModels, graphiql: true, secret: jwtSecret})))
 
 
-        async setGood({good}, {jwt: {id}, models: {SlicedSavable, Good}}){
-            let entity;
-            if ('_id' in good){
-                entity = await SlicedSavable.m.Good.findOne({_id: ObjectID(good._id)})
-                if (entity){
-                    entity.name         = good.name
-                    entity.description  = good.description
-                    entity.price        = good.price      
-                }
-            }
-            entity = entity || new Good(good)
-            if (good.categories){
-                console.log(good.categories)
-                entity.categories = []
-                for (catId of good.categories){
-                    let cat = await SlicedSavable.m.Category.findOne({_id: ObjectID(catId)});
-                    cat && entity.categories.push(cat)
-                }
-            }
-            return await entity.save()
-        },
+    app.post('/upload', upload.single('photo'), async (req, res, next) => {
+        let decoded;
+        console.log('wtf')
+        if (decoded = jwtCheck(req, jwtSecret)){
+            console.log('SOME UPLOAD', decoded, req.file)
 
-        async goods({}, {jwt: {id}, models: {SlicedSavable, Good}}){
-            goods = []
-            for (let good of SlicedSavable.m.Good.find({})){
-                try {good = await good} catch (e) { break }
-                goods.push(good)
-            }
-            return goods;
-        },
-
-        async good({_id}, {jwt: {id}, models: {SlicedSavable, Good}}){
-            return await SlicedSavable.m.Good.findOne({_id: ObjectID(_id)});
-        },
-
-
-
-        async setOrder({order}, {jwt: {id}, models: {SlicedSavable, Order, thisUser}}){
-            let entity;
-            if ('_id' in order){
-                entity = await SlicedSavable.m.Order.findOne({_id: ObjectID(order._id)})
-            }
-            entity = entity || new Order(order)
-            if (order.orderGoods){
-                entity.orderGoods = []
-                for (orderGoodId of order.orderGoods){
-                    let orderGood = await SlicedSavable.m.OrderGood.findOne({_id: ObjectID(orderGoodId)});
-                    orderGood && entity.orderGoods.push(orderGood)
-                }
-            }
-            console.log(entity.orderGoods)
-            entity.user = thisUser
-            return await entity.save()
-        },
-
-        async orders({}, {jwt: {id}, models: {SlicedSavable}}){
-            orders = []
-            for (let order of SlicedSavable.m.Order.find({})){
-                try {order = await order} catch (e) { break }
-                orders.push(order)
-            }
-            return order;
-        },
-
-        async myOrders({}, {jwt: {id}, models: {SlicedSavable}}){
-            orders = []
-            for (let order of SlicedSavable.m.Order.find({___owner: id.toString(id)})){
-                try {order = await order} catch (e) { break }
-                orders.push(order)
-            }
-            return orders;
-        },
-
-        async order({_id}, {jwt: {id}, models: {SlicedSavable, Good}}){
-            return await SlicedSavable.m.Order.findOne({_id: ObjectID(_id)});
-        },
-
-
-
-        async setOrderGood({orderGood}, {jwt: {id}, models: {SlicedSavable, OrderGood, thisUser}}){
-            let order  = await SlicedSavable.m.Order.findOne({'_id': ObjectID(orderGood.order)})
-            let good   = await SlicedSavable.m.Good.findOne ({'_id': ObjectID(orderGood.good)})
-            if (order && good){
-                let entity = await SlicedSavable.m.OrderGood.findOne({'order._id': order._id,
-                                                                         'good._id':  good._id})
-                if (!entity){
-                    console.log('wtf')
-                    entity = new OrderGood({})
-                }
-
-                entity.price = good.price
-                entity.count = orderGood.count
-                entity.order = order
-                entity.good  = good
-                await entity.save()
-                console.log(entity)
-
-                return entity
-            }
-            return null;
-        },
-    }
-
-    app.use('/graphql', express_graphql(async (req, res, gql) => { 
-        if (!gql.query){
-            return {
-                schema: schema,
-                rootValue: rootResolvers,
-                graphiql: true, 
-            }
+            let {models: {Image }} = await getModels(decoded.sub)
+            let image = await Image.fromFileData(req.file)
+            res.end(JSON.stringify({_id: image._id, url: image.url}))
         }
-        const operationMatch = gql.query.match(/\{\s*([a-zA-Z]+)\s*/)
-        const operationName  = gql.operationName || operationMatch[1]
-        console.log('before oper', operationName)
-        if ((!operationName) || anonResolvers.includes(operationName)){
-            return {
-                schema: schema,
-                rootValue: rootResolvers,
-                graphiql: true, 
-            }
+        else {
+            res.status(503).send('permission denied')
         }
-        const authorization = req.headers.authorization 
-        console.log(authorization)
-        
-        if (authorization && authorization.startsWith('Bearer ')){
-            console.log('token provided')
-            const token = authorization.substr("Bearer ".length)
-            const decoded = jwt.verify(token, jwtSecret)
-            if (decoded){
-                console.log('token verified', decoded)
+    })
 
-                let slicedModels  = await getModels(decoded.sub.id)
+    app.use(express.static('public'));
 
-                return {
-                    schema: schema,
-                    rootValue: rootResolvers,
-                    graphiql: true, 
-                    context: {jwt: decoded.sub,
-                              models: slicedModels}
-                }
-            }
-        }
-        console.log('bad end')
-    }))
 
-    app.listen(4000, () => console.log('Express GraphQL Server Now Running On localhost:4000/graphql'));
+    let socketPath = "/home/asmer/node_hosts/shop"
+    app.listen(socketPath, () => {
+        console.log('Express GraphQL Server Now Running On localhost:4000/graphql');
+        fs.chmodSync(socketPath, '777');
+    });
 })()
 

+ 71 - 19
models.js

@@ -1,37 +1,82 @@
-const MongoClient = require("mongodb").MongoClient;
 const ObjectID    = require("mongodb").ObjectID;
-const {connect}          = require('mm')
+const {connect}   = require('mm')
 
 module.exports = async (dbName='shop') => {
     const {Savable, slice} = await connect(dbName)
 
-    async function getModels(id){
-        const SlicedSavable = slice([id, 'user', 'admin'])
+    async function getModels({id}){
+        const SlicedSavable = slice([id, 'user'])
 
         class User extends SlicedSavable {
             constructor(...params){
                 super(...params)
-                this.orders = this.orders instanceof Array ? this.orders : (this.orders ? [this.orders] : []) 
+                //TODO: calc likes count by getter (no two-way relation for this to avoid overflow on many Kilos of likes
+                //cached like count, which incremented and decremented
+                //
+                //following and followers array
+
             }
 
-            static get relations(){
+
+            static get relations(){ //don't needed due to ___owner in most cases
                 return {
-                    orders: "user"
+                    avatar : "userAvatar",
                 }
             }
         }
         SlicedSavable.addClass(User)
 
+        class OwnerSlicedSavable extends SlicedSavable {
+            get owner(){
+                if (!this.___owner) return this.___owner
+
+                return SlicedSavable.m.User.findOne({_id: ObjectID(this.___owner)})
+            }
+        }
+
+
+        class Image extends OwnerSlicedSavable {
+            constructor(...params){
+                super(...params)
+            }
+
+
+            static async fromFileData(fileData){
+                let image  = new Image({})
+                image.fileData = fileData
+                image.url      = `images/${fileData.filename}`
+                image.originalFileName = fileData.originalname
+                await image.save()
+                return image;
+            }
+
+            async save(...params){
+                if (this.userAvatar){
+                    if (this.userAvatar._id.toString() !== id){
+                        throw new ReferenceError(`You can't set ava for other user`)
+                    }
+                }
+
+                return await super.save(...params)
+            }
+
+            static get relations(){
+                return {
+                    userAvatar: "avatar", //if it is ava
+                }
+            }
+
+        }
+        SlicedSavable.addClass(Image)
+
         class Good extends SlicedSavable {
             constructor(...params){
                 super(...params)
-                this.categories = this.category instanceof Array ? this.category : (this.category ? [this.category] : []) 
-                this.orderGoods = this.orderGoods instanceof Array ? this.orderGoods : (this.orderGoods ? [this.orderGoods] : []) 
             }
 
             static get relations(){
                 return {
-                    categories: "goods",
+                    categories: ["goods"],
                     orderGoods: "good",
                 }
             }
@@ -41,12 +86,11 @@ module.exports = async (dbName='shop') => {
         class Category extends SlicedSavable {
             constructor(...params){
                 super(...params)
-                this.goods = this.goods instanceof Array ? this.goods : (this.goods ? [this.goods] : []) 
             }
 
             static get relations(){
                 return {
-                    goods: "categories",
+                    goods: ["categories"],
                 }
             }
         }
@@ -55,7 +99,6 @@ module.exports = async (dbName='shop') => {
         class Order extends SlicedSavable {
             constructor(...params){
                 super(...params)
-                this.orderGoods = this.orderGoods instanceof Array ? this.orderGoods : (this.orderGoods ? [this.orderGoods] : []) 
             }
 
             get total(){
@@ -64,7 +107,7 @@ module.exports = async (dbName='shop') => {
 
             static get relations(){
                 return {
-                    user: "orders",
+                    user: ["orders"],
                     orderGoods: "order"
                 }
             }
@@ -82,18 +125,26 @@ module.exports = async (dbName='shop') => {
 
             static get relations(){
                 return {
-                    good: "orderGoods",
-                    order: "orderGoods"
+                    good: ["orderGoods"],
+                    order: ["orderGoods"]
                 }
             }
         }
         SlicedSavable.addClass(OrderGood)
 
+
+
+
+
+
+
+
         const thisUser = await Savable.m.User.findOne({_id: ObjectID(id)})
 
-        return {
-            SlicedSavable, User, Good, Category, Order, OrderGood
-        }
+        return {models: {
+                            SlicedSavable, ...SlicedSavable.classes
+                        }, 
+                thisUser}
     }
 
     return {
@@ -103,3 +154,4 @@ module.exports = async (dbName='shop') => {
     }
 }
 
+

+ 179 - 55
package-lock.json

@@ -1,5 +1,5 @@
 {
-  "name": "shop",
+  "name": "hipstagram",
   "version": "1.0.0",
   "lockfileVersion": 1,
   "requires": true,
@@ -13,6 +13,11 @@
         "negotiator": "0.6.2"
       }
     },
+    "append-field": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+      "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+    },
     "array-flatten": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -45,24 +50,71 @@
       "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
       "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
     },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+    },
+    "busboy": {
+      "version": "0.2.14",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+      "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+      "requires": {
+        "dicer": "0.2.5",
+        "readable-stream": "1.1.x"
+      }
+    },
     "bytes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
       "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
     },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.6",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
     "content-disposition": {
       "version": "0.5.3",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
       "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
       "requires": {
         "safe-buffer": "5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
-        }
       }
     },
     "content-type": {
@@ -80,6 +132,11 @@
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
     },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
     "debug": {
       "version": "2.6.9",
       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -98,6 +155,15 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "dicer": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+      "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+      "requires": {
+        "readable-stream": "1.1.x",
+        "streamsearch": "0.1.2"
+      }
+    },
     "ecdsa-sig-formatter": {
       "version": "1.0.11",
       "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -161,13 +227,6 @@
         "type-is": "~1.6.18",
         "utils-merge": "1.0.1",
         "vary": "~1.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
-        }
       }
     },
     "express-graphql": {
@@ -273,6 +332,11 @@
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
       "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
     },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+    },
     "iterall": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz",
@@ -361,12 +425,6 @@
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
     },
-    "memory-pager": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
-      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
-      "optional": true
-    },
     "merge-descriptors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -395,31 +453,54 @@
         "mime-db": "1.40.0"
       }
     },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
     "mm": {
-      "version": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git#dbedfd651d76b2f118c11e7f9a259f6b01245e69",
+      "version": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git#f4aad46fcaf18d6f7cc79fc6335d5f9a68cda087",
       "from": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git",
       "requires": {
         "mongodb": "^3.2.2"
       }
     },
-    "mongodb": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz",
-      "integrity": "sha512-2YdWrdf1PJgxcCrT1tWoL6nHuk6hCxhddAAaEh8QJL231ci4+P9FLyqopbTm2Z2sAU6mhCri+wd9r1hOcHdoMw==",
+    "mm-graphql": {
+      "version": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm-graphql.git#56ee6226ac481eaa7869c6997006707c467d9f40",
+      "from": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm-graphql.git",
       "requires": {
-        "mongodb-core": "3.2.7",
-        "safe-buffer": "^5.1.2"
+        "express": "^4.17.1",
+        "express-graphql": "^0.9.0",
+        "graphql": "^14.4.2",
+        "jsonwebtoken": "^8.5.1",
+        "mm": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git#f792da15d7a00091cd98840b352b6b526e2b57a1"
+      },
+      "dependencies": {
+        "mm": {
+          "version": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git#f792da15d7a00091cd98840b352b6b526e2b57a1",
+          "from": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git",
+          "requires": {
+            "mongodb": "^3.2.2"
+          }
+        }
       }
     },
-    "mongodb-core": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz",
-      "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==",
+    "mongodb": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.2.tgz",
+      "integrity": "sha512-fqJt3iywelk4yKu/lfwQg163Bjpo5zDKhXiohycvon4iQHbrfflSAz9AIlRE6496Pm/dQKQK5bMigdVo2s6gBg==",
       "requires": {
         "bson": "^1.1.1",
         "require_optional": "^1.0.1",
-        "safe-buffer": "^5.1.2",
-        "saslprep": "^1.0.0"
+        "safe-buffer": "^5.1.2"
       }
     },
     "ms": {
@@ -427,11 +508,31 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
+    "multer": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz",
+      "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==",
+      "requires": {
+        "append-field": "^1.0.0",
+        "busboy": "^0.2.11",
+        "concat-stream": "^1.5.2",
+        "mkdirp": "^0.5.1",
+        "object-assign": "^4.1.1",
+        "on-finished": "^2.3.0",
+        "type-is": "^1.6.4",
+        "xtend": "^4.0.0"
+      }
+    },
     "negotiator": {
       "version": "0.6.2",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
       "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
     },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
     "on-finished": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -450,6 +551,11 @@
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
       "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
     },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "proxy-addr": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
@@ -480,6 +586,17 @@
         "unpipe": "1.0.0"
       }
     },
+    "readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
     "require_optional": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
@@ -495,24 +612,15 @@
       "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
     },
     "safe-buffer": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-      "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
-    "saslprep": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
-      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
-      "optional": true,
-      "requires": {
-        "sparse-bitfield": "^3.0.3"
-      }
-    },
     "semver": {
       "version": "5.7.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
@@ -561,20 +669,21 @@
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
       "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
     },
-    "sparse-bitfield": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-      "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
-      "optional": true,
-      "requires": {
-        "memory-pager": "^1.0.2"
-      }
-    },
     "statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
       "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
     },
+    "streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+    },
+    "string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+    },
     "toidentifier": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@@ -589,11 +698,21 @@
         "mime-types": "~2.1.24"
       }
     },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
       "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
     },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
     "utils-merge": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -603,6 +722,11 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
     }
   }
 }

+ 10 - 7
package.json

@@ -1,29 +1,32 @@
 {
-  "name": "shop",
+  "name": "hipstagram",
   "version": "1.0.0",
-  "description": "Mock back for frontend students",
+  "description": "Mock Back for instagram-like service",
   "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "repository": {
     "type": "git",
-    "url": "git@gitlab.a-level.com.ua:gitgod/shop.git"
+    "url": "git@gitlab.a-level.com.ua:gitgod/hipstagram.git"
   },
   "keywords": [
     "node",
-    "mm",
+    "memmongo",
     "mongo",
     "graphql",
-    "shop"
+    "jwt",
+    "instagram"
   ],
   "author": "Ivan Grynkin",
-  "license": "ISC",
+  "license": "MIT",
   "dependencies": {
     "express": "^4.17.1",
     "express-graphql": "^0.9.0",
     "graphql": "^14.4.2",
     "jsonwebtoken": "^8.5.1",
-    "mm": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git"
+    "mm": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm.git",
+    "mm-graphql": "git+ssh://git@gitlab.a-level.com.ua:gitgod/mm-graphql.git",
+    "multer": "^1.4.2"
   }
 }