index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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, printSchema } = require('graphql');
  7. const anonResolvers = ['login', 'createUser'];
  8. function mmExpandSchema(gqlSchema){
  9. const types = {}
  10. const _typeMap = gqlSchema.getTypeMap()
  11. const buildInTypes = ['Query', 'Mutation', 'ID', 'Float', "String", 'Int', 'Boolean',
  12. 'Query!', 'Mutation!', 'ID!', 'Float!', "String!", 'Int!', 'Boolean!' ]
  13. async function argToSavables(arg, outputTypeName, Savable){
  14. console.log('argToSavables', arg)
  15. const entity = arg._id ? await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)}) :
  16. new Savable.classes[outputTypeName]({})
  17. const {_id, ...data} = arg;
  18. const type = _typeMap[outputTypeName + 'Input']
  19. const fields = type.getFields()
  20. for(let [fieldName, value] of Object.entries(data)){
  21. let typeName = fields[fieldName].type.toString()
  22. if (!buildInTypes.includes(typeName)){
  23. console.log('recursive', arg[fieldName], typeName)
  24. if (typeName[0] === '['){
  25. const nestedTypeName = typeName.slice(1,-6)
  26. console.log('array',nestedTypeName)
  27. entity[fieldName] = []
  28. for (let nestedArg of value){
  29. const nestedEntity = await argToSavables(nestedArg, nestedTypeName, Savable)
  30. entity[fieldName].push(nestedEntity)
  31. }
  32. }
  33. else {
  34. const nestedTypeName = typeName.slice(0,-5)
  35. console.log('one', nestedTypeName)
  36. entity[fieldName] = await argToSavables(value, nestedTypeName, Savable)
  37. }
  38. }
  39. else {
  40. entity[fieldName] = value
  41. }
  42. }
  43. return await entity.save()
  44. }
  45. let queryFields = _typeMap.Query ? _typeMap.Query.getFields() : {}
  46. let mutationFields = _typeMap.Mutation ? _typeMap.Mutation.getFields() : {}
  47. for (let [typeName, type] of Object.entries(_typeMap))
  48. if (!buildInTypes.includes(typeName) &&
  49. !typeName.startsWith('__')){
  50. if (typeName.endsWith('Input')){
  51. let outputTypeName = typeName.substr(0, typeName.length - 'Input'.length)
  52. if (outputTypeName in _typeMap){
  53. types[outputTypeName] = type
  54. const find = {
  55. type: GraphQLList(_typeMap[outputTypeName]),
  56. args: {query: {type: GraphQLString}},
  57. async resolve(root, args, context, info){
  58. //console.log(root, args, context, info)
  59. const Savable = context.models.SlicedSavable || context.models.Savable
  60. args = JSON.parse(args.query)
  61. let results = []
  62. for (let result of Savable.m[outputTypeName].find(...args)){
  63. try {result = await result} catch (e) { break }
  64. results.push(result)
  65. }
  66. return results;
  67. }
  68. }
  69. queryFields[`${outputTypeName}Find`] = find
  70. const findOne = {
  71. type: _typeMap[outputTypeName],
  72. args: {query: {type: GraphQLString}},
  73. async resolve(root, args, context, info){
  74. //console.log(root, args, context, info)
  75. const Savable = context.models.SlicedSavable || context.models.Savable
  76. args = JSON.parse(args.query)
  77. let [query] = args
  78. if (query._id && typeof query._id === 'string'){
  79. query._id = ObjectID(query._id)
  80. }
  81. let record = Savable.m[outputTypeName].findOne(query, ...args.slice(1))
  82. return record;
  83. }
  84. }
  85. queryFields[`${outputTypeName}FindOne`] = findOne
  86. const lowerCaseName = outputTypeName[0].toLowerCase() + outputTypeName.slice(1)
  87. const del = {
  88. type: _typeMap[outputTypeName],
  89. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  90. async resolve(root, args, context, info){
  91. //console.log(root, args, context, info)
  92. const Savable = context.models.SlicedSavable || context.models.Savable
  93. const arg = args[lowerCaseName]
  94. if (! ('_id' in arg)){
  95. return null;
  96. }
  97. let entity = await Savable.m[outputTypeName].findOne({_id: ObjectID(arg._id)})
  98. if (entity){
  99. let copy = {...record}
  100. await entity.delete()
  101. return copy;
  102. }
  103. return entity;
  104. }
  105. }
  106. mutationFields[`${outputTypeName}Delete`] = del
  107. const upsert = {
  108. type: _typeMap[outputTypeName],
  109. args: {[lowerCaseName]: {type: _typeMap[typeName]}},
  110. async resolve(root, args, context, info){
  111. //console.log(root, args, context, info)
  112. const Savable = context.models.SlicedSavable || context.models.Savable
  113. const arg = args[lowerCaseName]
  114. const entity = argToSavables(args[lowerCaseName], outputTypeName, Savable)
  115. return entity;
  116. }
  117. }
  118. mutationFields[`${outputTypeName}Upsert`] = upsert
  119. }
  120. }
  121. }
  122. let newQuery = new GraphQLObjectType({name: 'Query', fields: queryFields})
  123. let newMutation = new GraphQLObjectType({name: 'Mutation', fields: mutationFields})
  124. let newSchema = new GraphQLSchema({query: newQuery, mutation: newMutation})
  125. return newSchema;
  126. }
  127. ;(async () => {
  128. const {Savable, slice, getModels} = await require('./models.js')()
  129. class User extends Savable {
  130. static get relations(){
  131. return {
  132. }
  133. }
  134. }
  135. Savable.addClass(User)
  136. let schema = buildSchema(`
  137. type User {
  138. _id: String
  139. createdAt: String
  140. login: String
  141. nick : String
  142. orders: [Order]
  143. }
  144. input UserInput {
  145. _id: String
  146. login: String
  147. nick : String
  148. }
  149. type Category {
  150. _id: ID,
  151. createdAt: String
  152. name: String!,
  153. goods: [Good]
  154. }
  155. input CategoryInput {
  156. _id: ID,
  157. name: String,
  158. goods: [GoodInput]
  159. }
  160. type Good {
  161. _id: ID,
  162. createdAt: String
  163. name: String!,
  164. description: String
  165. price: Float
  166. imgUrls: [String]
  167. orderGoods: [OrderGood]
  168. categories: [Category]
  169. }
  170. input GoodInput {
  171. _id: ID,
  172. name: String,
  173. description: String
  174. imgUrls: [String]
  175. price: Float
  176. categories: [CategoryInput]
  177. }
  178. type OrderGood {
  179. _id: ID,
  180. createdAt: String
  181. price: Float,
  182. count: Float,
  183. good: Good,
  184. order: Order
  185. total: Float
  186. }
  187. input OrderGoodInput {
  188. _id: ID,
  189. count: Int,
  190. good: GoodInput,
  191. order: OrderInput
  192. }
  193. type Order {
  194. _id: ID
  195. createdAt: String
  196. orderGoods: [OrderGood]
  197. total: Float
  198. }
  199. input OrderInput {
  200. _id: ID
  201. orderGoods: [OrderGoodInput]
  202. }
  203. `);
  204. schema = mmExpandSchema(schema)
  205. console.log(printSchema(schema))
  206. //console.log(schema._typeMap.User.__proto__)
  207. //console.log(schema._typeMap.OrderInput.getFields())
  208. var app = express();
  209. app.use(express.static('public'));
  210. const rootResolvers = {
  211. createUser:async function ({login, password}){
  212. let user = await Savable.m.User.findOne({login, password})
  213. if (user)
  214. return null;
  215. user = await (new User({login, password})).save()
  216. user.___owner = user._id.toString()
  217. user.___permissions = {
  218. read: ["owner", "user"]
  219. }
  220. return await user.save()
  221. },
  222. login: async function({login, password}){
  223. console.log(Savable.classes)
  224. const user = await Savable.m.User.findOne({login, password})
  225. if (!user)
  226. return null;
  227. const token = jwt.sign({ sub: {id: user._id, login}}, jwtSecret); //подписывам токен нашим ключем
  228. return token
  229. },
  230. changePassword:async function ({password}, {jwt: {id}, models: {SlicedSavable, User}} ){
  231. id = new ObjectID(id)
  232. const user = await SlicedSavable.m.User.findOne({_id: id})
  233. if (!user)
  234. return null;
  235. user.password = password;
  236. return await user.save()
  237. },
  238. }
  239. app.use('/graphql', express_graphql(async (req, res, gql) => {
  240. if (!gql.query){
  241. return {
  242. schema: schema,
  243. rootValue: (...params) => (console.log(params), rootResolvers),
  244. graphiql: true,
  245. }
  246. }
  247. const operationMatch = gql.query.match(/\{\s*([a-zA-Z]+)\s*/)
  248. const operationName = gql.operationName || operationMatch[1]
  249. console.log('before oper', operationName)
  250. if ((!operationName) || anonResolvers.includes(operationName)){
  251. return {
  252. schema: schema,
  253. rootValue: rootResolvers,
  254. graphiql: true,
  255. }
  256. }
  257. const authorization = req.headers.authorization
  258. console.log(authorization)
  259. if (authorization && authorization.startsWith('Bearer ')){
  260. console.log('token provided')
  261. const token = authorization.substr("Bearer ".length)
  262. const decoded = jwt.verify(token, jwtSecret)
  263. if (decoded){
  264. console.log('token verified', decoded)
  265. let slicedModels = await getModels(decoded.sub.id)
  266. return {
  267. schema: schema,
  268. rootValue: rootResolvers,
  269. graphiql: true,
  270. context: {jwt: decoded.sub,
  271. models: slicedModels}
  272. }
  273. }
  274. }
  275. console.log('bad end')
  276. }))
  277. app.listen(4000, () => console.log('Express GraphQL Server Now Running On localhost:4000/graphql'));
  278. })()