index.js 11 KB

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