index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. const ObjectID = require("mongodb").ObjectID;
  2. const jwt = require('jsonwebtoken')
  3. const jwtSecret = 'CbymrfGfnB'
  4. const express = require('express');
  5. const express_graphql = require('express-graphql');
  6. const { buildSchema, GraphQLObjectType, GraphQLString, GraphQLList, GraphQLSchema} = require('graphql');
  7. const { buildASTSchema, parseSchemaIntoAST } = require('graphql/utilities');
  8. const anonResolvers = ['login', 'createUser'];
  9. function mmExpandSchema(gqlSchema){
  10. const types = {}
  11. const _typeMap = gqlSchema.getTypeMap()
  12. const buildInTypes = ['Query', 'Mutation', 'ID', 'Float', "String", 'Int', 'Boolean',
  13. 'Query!', 'Mutation!', 'ID!', 'Float!', "String!", 'Int!', 'Boolean!' ]
  14. async function argToSavables(arg, outputTypeName, Savable){
  15. console.log('argToSavables', arg)
  16. const entity = arg._id ? await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)}) :
  17. new Savable.classes[outputTypeName]({})
  18. const {_id, ...data} = arg;
  19. const type = _typeMap[outputTypeName + 'Input']
  20. const fields = type.getFields()
  21. for(let [fieldName, value] of Object.entries(data)){
  22. let typeName = fields[fieldName].type.toString()
  23. if (!buildInTypes.includes(typeName)){
  24. console.log('recursive', arg[fieldName], typeName)
  25. if (typeName[0] === '['){
  26. const nestedTypeName = typeName.slice(1,-6)
  27. console.log('array',nestedTypeName)
  28. entity[fieldName] = []
  29. for (let nestedArg of value){
  30. const nestedEntity = await argToSavables(nestedArg, nestedTypeName, Savable)
  31. entity[fieldName].push(nestedEntity)
  32. }
  33. }
  34. else {
  35. const nestedTypeName = typeName.slice(0,-6)
  36. console.log('one', nestedTypeName)
  37. entity[fieldName] = await argToSavables(value, nestedTypeName, Savable)
  38. }
  39. }
  40. else {
  41. entity[fieldName] = value
  42. }
  43. }
  44. return await entity.save()
  45. }
  46. let queryFields = _typeMap.Query ? _typeMap.Query.getFields() : {}
  47. let mutationFields = _typeMap.Mutation ? _typeMap.Mutation.getFields() : {}
  48. for (let [typeName, type] of Object.entries(_typeMap))
  49. if (!buildInTypes.includes(typeName) &&
  50. !typeName.startsWith('__')){
  51. if (typeName.endsWith('Input')){
  52. let outputTypeName = typeName.substr(0, typeName.length - 'Input'.length)
  53. if (outputTypeName in _typeMap){
  54. types[outputTypeName] = type
  55. const find = {
  56. type: GraphQLList(_typeMap[outputTypeName]),
  57. args: {query: {type: GraphQLString}},
  58. async resolve(root, args, context, info){
  59. //console.log(root, args, context, info)
  60. const Savable = context.models.SlicedSavable || context.models.Savable
  61. args = JSON.parse(args.query)
  62. let results = []
  63. for (let result of Savable.m[outputTypeName].find(...args)){
  64. try {result = await result} catch (e) { break }
  65. results.push(result)
  66. }
  67. return results;
  68. }
  69. }
  70. queryFields[`${outputTypeName}Find`] = find
  71. const findOne = {
  72. type: _typeMap[outputTypeName],
  73. args: {query: {type: GraphQLString}},
  74. async resolve(root, args, context, info){
  75. //console.log(root, args, context, info)
  76. const Savable = context.models.SlicedSavable || context.models.Savable
  77. args = JSON.parse(args.query)
  78. let [query] = args
  79. if (query._id && typeof query._id === 'string'){
  80. query._id = ObjectID(query._id)
  81. }
  82. let record = Savable.m[outputTypeName].findOne(query, ...args.slice(1))
  83. return record;
  84. }
  85. }
  86. queryFields[`${outputTypeName}FindOne`] = findOne
  87. const lowerCaseName = outputTypeName[0].toLowerCase() + outputTypeName.slice(1)
  88. const del = {
  89. type: _typeMap[outputTypeName],
  90. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  91. async resolve(root, args, context, info){
  92. //console.log(root, args, context, info)
  93. const Savable = context.models.SlicedSavable || context.models.Savable
  94. const arg = args[lowerCaseName]
  95. if (! ('_id' in arg)){
  96. return null;
  97. }
  98. let entity = await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)})
  99. if (entity){
  100. let copy = {...record}
  101. await entity.delete()
  102. return copy;
  103. }
  104. return entity;
  105. }
  106. }
  107. mutationFields[`${outputTypeName}Delete`] = del
  108. const upsert = {
  109. type: _typeMap[outputTypeName],
  110. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  111. async resolve(root, args, context, info){
  112. //console.log(root, args, context, info)
  113. const Savable = context.models.SlicedSavable || context.models.Savable
  114. const arg = args[lowerCaseName]
  115. const entity = argToSavables(args[lowerCaseName], outputTypeName, Savable)
  116. return entity;
  117. }
  118. }
  119. mutationFields[`${outputTypeName}Upsert`] = upsert
  120. }
  121. }
  122. }
  123. let newQuery = new GraphQLObjectType({name: 'Query', fields: queryFields})
  124. let newMutation = new GraphQLObjectType({name: 'Mutation', fields: mutationFields})
  125. let newSchema = new GraphQLSchema({query: newQuery, mutation: newMutation})
  126. return newSchema;
  127. //console.log(types)
  128. //console.log(queryFields)
  129. //console.log(_typeMap.Query.getFields())
  130. }
  131. ;(async () => {
  132. const {Savable, slice, getModels} = await require('./models.js')()
  133. class User extends Savable {
  134. static get relations(){
  135. return {
  136. }
  137. }
  138. }
  139. Savable.addClass(User)
  140. //type Query {
  141. //login(login: String!, password: String!): String
  142. //categories: [Category]
  143. //category(_id: ID!): Category
  144. //goods: [Good]
  145. //good(_id: ID!): Good
  146. //myOrders: [Order]
  147. //orders: [Order]
  148. //}
  149. //type Mutation {
  150. //createUser(login: String!, password: String!): User
  151. //changePassword(password: String!): User
  152. //setCategory(cat: CategoryInput!):Category
  153. //setGood(good: GoodInput!):Good
  154. //setOrder(order: OrderInput):Order
  155. //setOrderGood(orderGood: OrderGoodInput):OrderGood
  156. //}
  157. let schema = buildSchema(`
  158. type User {
  159. _id: String
  160. createdAt: String
  161. login: String
  162. nick : String
  163. orders: [Order]
  164. }
  165. input UserInput {
  166. _id: String
  167. login: String
  168. nick : String
  169. }
  170. type Category {
  171. _id: ID,
  172. createdAt: String
  173. name: String!,
  174. goods: [Good]
  175. }
  176. input CategoryInput {
  177. _id: ID,
  178. name: String,
  179. goods: [GoodInput]
  180. }
  181. type Good {
  182. _id: ID,
  183. createdAt: String
  184. name: String!,
  185. description: String
  186. price: Float
  187. imgUrls: [String]
  188. orderGoods: [OrderGood]
  189. categories: [Category]
  190. }
  191. input GoodInput {
  192. _id: ID,
  193. name: String,
  194. description: String
  195. imgUrls: [String]
  196. price: Float
  197. categories: [CategoryInput]
  198. }
  199. type OrderGood {
  200. _id: ID,
  201. createdAt: String
  202. price: Float,
  203. count: Float,
  204. good: Good,
  205. order: Order
  206. total: Float
  207. }
  208. input OrderGoodInput {
  209. _id: ID,
  210. count: Int,
  211. good: GoodInput,
  212. order: OrderInput
  213. }
  214. type Order {
  215. _id: ID
  216. createdAt: String
  217. orderGoods: [OrderGood]
  218. total: Float
  219. }
  220. input OrderInput {
  221. _id: ID
  222. orderGoods: [OrderGoodInput]
  223. }
  224. `);
  225. schema = mmExpandSchema(schema)
  226. //console.log(schema._typeMap.User.__proto__)
  227. //console.log(schema._typeMap.OrderInput.getFields())
  228. var app = express();
  229. app.use(express.static('public'));
  230. const rootResolvers = {
  231. createUser:async function ({login, password}){
  232. let user = await Savable.m.User.findOne({login, password})
  233. if (user)
  234. return null;
  235. user = await (new User({login, password})).save()
  236. user.___owner = user._id.toString()
  237. user.___permissions = {
  238. read: ["owner", "user"]
  239. }
  240. return await user.save()
  241. },
  242. login: async function({login, password}){
  243. console.log(Savable.classes)
  244. const user = await Savable.m.User.findOne({login, password})
  245. if (!user)
  246. return null;
  247. const token = jwt.sign({ sub: {id: user._id, login}}, jwtSecret); //подписывам токен нашим ключем
  248. return token
  249. },
  250. changePassword:async function ({password}, {jwt: {id}, models: {SlicedSavable, User}} ){
  251. id = new ObjectID(id)
  252. const user = await SlicedSavable.m.User.findOne({_id: id})
  253. if (!user)
  254. return null;
  255. user.password = password;
  256. return await user.save()
  257. },
  258. async setCategory({cat}, {jwt: {id}, models: {SlicedSavable, Category}}){
  259. if ('_id' in cat){
  260. let entity = await SlicedSavable.m.Category.findOne({_id: ObjectID(cat._id)})
  261. console.log(entity)
  262. if (entity){
  263. entity.name = cat.name
  264. if (cat.goods){
  265. entity.goods = []
  266. for (goodId of cat.goods){
  267. let good = await SlicedSavable.m.Good.findOne({_id: ObjectID(goodId)});
  268. good && entity.goods.push(good)
  269. }
  270. }
  271. return await entity.save()
  272. }
  273. }
  274. return await (new Category(cat)).save()
  275. },
  276. async categories({}, {jwt: {id}, models: {SlicedSavable, Category}}){
  277. let categories = []
  278. for (let category of SlicedSavable.m.Category.find({})){
  279. try {category = await category} catch (e) { break }
  280. categories.push(category)
  281. }
  282. return categories;
  283. },
  284. async category({_id}, {jwt: {id}, models: {SlicedSavable, Category}}){
  285. return await SlicedSavable.m.Category.findOne({_id: ObjectID(_id)});
  286. },
  287. async setGood({good}, {jwt: {id}, models: {SlicedSavable, Good}}){
  288. let entity;
  289. if ('_id' in good){
  290. entity = await SlicedSavable.m.Good.findOne({_id: ObjectID(good._id)})
  291. if (entity){
  292. entity.name = good.name
  293. entity.description = good.description
  294. entity.price = good.price
  295. }
  296. }
  297. entity = entity || new Good(good)
  298. if (good.categories){
  299. console.log(good.categories)
  300. entity.categories = []
  301. for (catId of good.categories){
  302. let cat = await SlicedSavable.m.Category.findOne({_id: ObjectID(catId)});
  303. cat && entity.categories.push(cat)
  304. }
  305. }
  306. return await entity.save()
  307. },
  308. async goods({}, {jwt: {id}, models: {SlicedSavable, Good}}){
  309. goods = []
  310. for (let good of SlicedSavable.m.Good.find({})){
  311. try {good = await good} catch (e) { break }
  312. goods.push(good)
  313. }
  314. return goods;
  315. },
  316. async good({_id}, {jwt: {id}, models: {SlicedSavable, Good}}){
  317. return await SlicedSavable.m.Good.findOne({_id: ObjectID(_id)});
  318. },
  319. async setOrder({order}, {jwt: {id}, models: {SlicedSavable, Order, thisUser}}){
  320. let entity;
  321. if ('_id' in order){
  322. entity = await SlicedSavable.m.Order.findOne({_id: ObjectID(order._id)})
  323. }
  324. entity = entity || new Order(order)
  325. if (order.orderGoods){
  326. entity.orderGoods = []
  327. for (orderGoodId of order.orderGoods){
  328. let orderGood = await SlicedSavable.m.OrderGood.findOne({_id: ObjectID(orderGoodId)});
  329. orderGood && entity.orderGoods.push(orderGood)
  330. }
  331. }
  332. console.log(entity.orderGoods)
  333. entity.user = thisUser
  334. return await entity.save()
  335. },
  336. async orders({}, {jwt: {id}, models: {SlicedSavable}}){
  337. orders = []
  338. for (let order of SlicedSavable.m.Order.find({})){
  339. try {order = await order} catch (e) { break }
  340. orders.push(order)
  341. }
  342. return order;
  343. },
  344. async myOrders({}, {jwt: {id}, models: {SlicedSavable}}){
  345. orders = []
  346. for (let order of SlicedSavable.m.Order.find({___owner: id.toString(id)})){
  347. try {order = await order} catch (e) { break }
  348. orders.push(order)
  349. }
  350. return orders;
  351. },
  352. async order({_id}, {jwt: {id}, models: {SlicedSavable, Good}}){
  353. return await SlicedSavable.m.Order.findOne({_id: ObjectID(_id)});
  354. },
  355. async setOrderGood({orderGood}, {jwt: {id}, models: {SlicedSavable, OrderGood, thisUser}}){
  356. let order = await SlicedSavable.m.Order.findOne({'_id': ObjectID(orderGood.order)})
  357. let good = await SlicedSavable.m.Good.findOne ({'_id': ObjectID(orderGood.good)})
  358. if (order && good){
  359. let entity = await SlicedSavable.m.OrderGood.findOne({'order._id': order._id,
  360. 'good._id': good._id})
  361. if (!entity){
  362. console.log('wtf')
  363. entity = new OrderGood({})
  364. }
  365. entity.price = good.price
  366. entity.count = orderGood.count
  367. entity.order = order
  368. entity.good = good
  369. await entity.save()
  370. console.log(entity)
  371. return entity
  372. }
  373. return null;
  374. },
  375. }
  376. app.use('/graphql', express_graphql(async (req, res, gql) => {
  377. if (!gql.query){
  378. return {
  379. schema: schema,
  380. rootValue: (...params) => (console.log(params), rootResolvers),
  381. graphiql: true,
  382. }
  383. }
  384. const operationMatch = gql.query.match(/\{\s*([a-zA-Z]+)\s*/)
  385. const operationName = gql.operationName || operationMatch[1]
  386. console.log('before oper', operationName)
  387. if ((!operationName) || anonResolvers.includes(operationName)){
  388. return {
  389. schema: schema,
  390. rootValue: rootResolvers,
  391. graphiql: true,
  392. }
  393. }
  394. const authorization = req.headers.authorization
  395. console.log(authorization)
  396. if (authorization && authorization.startsWith('Bearer ')){
  397. console.log('token provided')
  398. const token = authorization.substr("Bearer ".length)
  399. const decoded = jwt.verify(token, jwtSecret)
  400. if (decoded){
  401. console.log('token verified', decoded)
  402. let slicedModels = await getModels(decoded.sub.id)
  403. return {
  404. schema: schema,
  405. rootValue: rootResolvers,
  406. graphiql: true,
  407. context: {jwt: decoded.sub,
  408. models: slicedModels}
  409. }
  410. }
  411. }
  412. console.log('bad end')
  413. }))
  414. app.listen(4000, () => console.log('Express GraphQL Server Now Running On localhost:4000/graphql'));
  415. })()