resolvers.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { IResolvers } from 'apollo-server-express'
  2. import fs from 'fs'
  3. import randombytes from 'randombytes'
  4. import sharp from 'sharp'
  5. import { tmpDir, uploadDir, thumbnails } from './constants'
  6. import user from '../user'
  7. export const resolvers: IResolvers = {
  8. Query: {
  9. fsFiles: async (parent, { directory }, context, info) => {
  10. user.checkPermission(context, 'ADMIN')
  11. const data = await fsFiles(directory)
  12. return data
  13. },
  14. files: (parent, args, context, info) => {
  15. user.checkPermission(context, 'ADMIN')
  16. return context.db.query.files(args, info)
  17. }
  18. },
  19. Mutation: {
  20. uploadFile: async (parent, { comment, file }, context, info) => {
  21. user.checkPermission(context, 'ADMIN')
  22. const fileInfo = await uploadFile(file)
  23. return context.db.mutation.createFile(
  24. {
  25. data: {
  26. ...fileInfo,
  27. comment,
  28. user: { connect: { id: context.req.userId } }
  29. }
  30. },
  31. info
  32. )
  33. }
  34. }
  35. }
  36. async function fsFiles(directory: string) {
  37. const fileList = await fs.promises.readdir(directory)
  38. return Promise.all(
  39. fileList.map(async filename => {
  40. const path = `${directory}/${filename}`
  41. const { size, ctime, mtime } = await fs.promises.stat(path)
  42. return {
  43. filename,
  44. path,
  45. size,
  46. ctime,
  47. mtime
  48. }
  49. })
  50. )
  51. }
  52. async function uploadFile(file: any) {
  53. const { createReadStream, filename, mimetype, encoding } = await file
  54. const stream = createReadStream()
  55. const fsFilename = randombytes(16).toString('hex')
  56. const tmpPath = `${tmpDir}/${fsFilename}`
  57. const path = `${uploadDir}/${fsFilename}`
  58. const thumbnailPath = `${thumbnails}/${fsFilename}`
  59. await new Promise((resolve, reject) => {
  60. const file = fs.createWriteStream(tmpPath)
  61. file.on('finish', resolve)
  62. file.on('error', error => {
  63. fs.unlink(tmpPath, () => {
  64. reject(error)
  65. })
  66. })
  67. stream.on('error', (error: any) => file.destroy(error))
  68. stream.pipe(file)
  69. })
  70. if (mimetype.startsWith('image/')) {
  71. try {
  72. await processImage(tmpPath, path)
  73. await createThumbnail(tmpPath, thumbnailPath)
  74. await fs.promises.unlink(tmpPath)
  75. } catch (error) {
  76. try {
  77. await fs.promises.unlink(tmpPath)
  78. await fs.promises.unlink(path)
  79. } catch (ignore) {}
  80. throw error
  81. }
  82. }
  83. const { size } = await fs.promises.stat(path)
  84. return {
  85. filename,
  86. path,
  87. mimetype,
  88. encoding,
  89. size
  90. }
  91. }
  92. function processImage(tmpFile: string, outputFile: string) {
  93. return sharp(tmpFile)
  94. .resize(1600, 1600, {
  95. fit: sharp.fit.inside,
  96. withoutEnlargement: true
  97. })
  98. .jpeg()
  99. .toFile(outputFile)
  100. }
  101. function createThumbnail(tmpFile: string, thumbnail: string) {
  102. return sharp(tmpFile)
  103. .resize(200, 200, {
  104. fit: sharp.fit.inside
  105. })
  106. .jpeg()
  107. .toFile(thumbnail)
  108. }