import { IResolvers } from 'apollo-server-express' import fs from 'fs' import randombytes from 'randombytes' import sharp from 'sharp' import { tmpDir, uploadDir, thumbnails } from './constants' import user from '../user' export const resolvers: IResolvers = { Query: { fsFiles: async (parent, { directory }, context, info) => { user.checkPermission(context, 'ADMIN') const data = await fsFiles(directory) return data }, files: (parent, args, context, info) => { user.checkPermission(context, 'ADMIN') return context.db.query.files(args, info) } }, Mutation: { uploadFile: async (parent, { comment, file }, context, info) => { user.checkPermission(context, 'ADMIN') const fileInfo = await uploadFile(file) return context.db.mutation.createFile( { data: { ...fileInfo, comment, user: { connect: { id: context.req.userId } } } }, info ) } } } async function fsFiles(directory: string) { const fileList = await fs.promises.readdir(directory) return Promise.all( fileList.map(async filename => { const path = `${directory}/${filename}` const { size, ctime, mtime } = await fs.promises.stat(path) return { filename, path, size, ctime, mtime } }) ) } async function uploadFile(file: any) { const { createReadStream, filename, mimetype, encoding } = await file const stream = createReadStream() const fsFilename = randombytes(16).toString('hex') const tmpPath = `${tmpDir}/${fsFilename}` const path = `${uploadDir}/${fsFilename}` const thumbnailPath = `${thumbnails}/${fsFilename}` await new Promise((resolve, reject) => { const file = fs.createWriteStream(tmpPath) file.on('finish', resolve) file.on('error', error => { fs.unlink(tmpPath, () => { reject(error) }) }) stream.on('error', (error: any) => file.destroy(error)) stream.pipe(file) }) if (mimetype.startsWith('image/')) { try { await processImage(tmpPath, path) await createThumbnail(tmpPath, thumbnailPath) await fs.promises.unlink(tmpPath) } catch (error) { try { await fs.promises.unlink(tmpPath) await fs.promises.unlink(path) } catch (ignore) {} throw error } } const { size } = await fs.promises.stat(path) return { filename, path, mimetype, encoding, size } } function processImage(tmpFile: string, outputFile: string) { return sharp(tmpFile) .resize(1600, 1600, { fit: sharp.fit.inside, withoutEnlargement: true }) .jpeg() .toFile(outputFile) } function createThumbnail(tmpFile: string, thumbnail: string) { return sharp(tmpFile) .resize(200, 200, { fit: sharp.fit.inside }) .jpeg() .toFile(thumbnail) }