UNPKG

@wepublish/api-media-karma

Version:
130 lines (107 loc) 3.78 kB
import { MediaAdapter, ImageWithFocalPoint, UploadImage, ImageTransformation, ArrayBufferUpload } from '@wepublish/api' import {FileUpload} from 'graphql-upload' import fetch from 'node-fetch' import FormData from 'form-data' import {URL} from 'url' export class MediaServerError extends Error { constructor(message: string) { super(`Received error from media server. Message: ${message}`) } } export class KarmaMediaAdapter implements MediaAdapter { readonly url: URL readonly token: string readonly internalURL: URL constructor(url: URL, token: string, internalURL: URL = url) { this.url = url this.token = token this.internalURL = internalURL } async _uploadImage(form: FormData): Promise<UploadImage> { // The form-data module reports a known length for the stream returned by createReadStream, // which is wrong, override it and always set it to false. // Related issue: https://github.com/form-data/form-data/issues/394 form.hasKnownLength = () => false const response = await fetch(this.internalURL, { method: 'POST', headers: {authorization: `Bearer ${this.token}`}, body: form }) const json = await response.json() if (response.status !== 200) { throw new MediaServerError(response.statusText) } const {id, filename, fileSize, extension, mimeType, format, width, height} = json return { id, filename, fileSize, extension, mimeType, format, width, height } } async uploadImage(fileUpload: Promise<FileUpload>): Promise<UploadImage> { const form = new FormData() const {filename: inputFilename, mimetype, createReadStream}: FileUpload = await fileUpload form.append('file', createReadStream(), {filename: inputFilename, contentType: mimetype}) return this._uploadImage(form) } async uploadImageFromArrayBuffer( arrayBufferUpload: Promise<ArrayBufferUpload> ): Promise<UploadImage> { const form = new FormData() const { filename: inputFilename, mimetype, arrayBuffer }: ArrayBufferUpload = await arrayBufferUpload form.append('file', arrayBuffer, {filename: inputFilename, contentType: mimetype}) return this._uploadImage(form) } async deleteImage(id: string): Promise<boolean> { const response = await fetch(`${this.internalURL}${id}`, { method: 'DELETE', headers: {authorization: `Bearer ${this.token}`} }) if (response.status !== 204) { throw new MediaServerError(response.statusText) } return true } async getImageURL( {id, filename, extension, focalPoint}: ImageWithFocalPoint, transformation?: ImageTransformation ): Promise<string> { filename = filename || 'untitled' if (transformation) { const {width, height, rotation, output, quality} = transformation const fullFilename = encodeURIComponent(`${filename}${output ? `.${output}` : extension}`) const transformations = [] if (width) transformations.push(`w_${width}`) if (height) transformations.push(`h_${height}`) if (rotation) transformations.push(`r_${rotation}`) if (output) transformations.push(`o_${output}`) if (quality) transformations.push(`q_${quality}`) if (focalPoint && (width || height)) { transformations.push(`f_${focalPoint.x?.toFixed(3)}:${focalPoint.y?.toFixed(3)}`) } if (transformations.length > 0) { return `${this.url}${id}/t/${transformations.join(',')}/${fullFilename}` } else { return `${this.url}${id}/${fullFilename}` } } else { const fullFilename = encodeURIComponent(`${filename}${extension}`) return `${this.url}${id}/${fullFilename}` } } }