resolvers.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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. console.log('image')
  72. try {
  73. await processImage(tmpPath, path)
  74. await createThumbnail(tmpPath, thumbnailPath)
  75. await fs.promises.unlink(tmpPath)
  76. } catch (error) {
  77. try {
  78. await fs.promises.unlink(tmpPath)
  79. await fs.promises.unlink(path)
  80. } catch (ignore) {}
  81. throw error
  82. }
  83. } else {
  84. console.log('no image')
  85. try {
  86. console.log(tmpPath)
  87. await fs.promises.rename(tmpPath, path)
  88. } catch (error) {
  89. try {
  90. await fs.promises.unlink(tmpPath)
  91. await fs.promises.unlink(path)
  92. } catch (ignore) {}
  93. throw error
  94. }
  95. }
  96. const { size } = await fs.promises.stat(path)
  97. return {
  98. filename,
  99. path,
  100. mimetype,
  101. encoding,
  102. size,
  103. }
  104. }
  105. function processImage(tmpFile: string, outputFile: string) {
  106. return sharp(tmpFile)
  107. .resize(1600, 1600, {
  108. fit: sharp.fit.inside,
  109. withoutEnlargement: true,
  110. })
  111. .jpeg()
  112. .toFile(outputFile)
  113. }
  114. function createThumbnail(tmpFile: string, thumbnail: string) {
  115. return sharp(tmpFile)
  116. .resize(200, 200, {
  117. fit: sharp.fit.inside,
  118. })
  119. .jpeg()
  120. .toFile(thumbnail)
  121. }