aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2020-02-16 00:50:23 -0500
committerSam Wilkins <samwilkins333@gmail.com>2020-02-16 00:50:23 -0500
commitf33ad290f1de3a01f2c4536f03f040e09771f82e (patch)
treee69ccddbfe5d7bacd70d30483903084e5fed2a3e /src
parent03b33592cf7f244094bb594c8251ab9c3e0ee48f (diff)
improved file upload api
Diffstat (limited to 'src')
-rw-r--r--src/client/Network.ts14
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx17
-rw-r--r--src/client/views/collections/CollectionSubView.tsx56
-rw-r--r--src/new_fields/Doc.ts6
-rw-r--r--src/scraping/buxton/final/BuxtonImporter.ts7
-rw-r--r--src/server/ApiManagers/GooglePhotosManager.ts5
-rw-r--r--src/server/ApiManagers/UploadManager.ts8
-rw-r--r--src/server/DashUploadUtils.ts107
-rw-r--r--src/server/SharedMediaTypes.ts39
-rw-r--r--src/server/database.ts6
10 files changed, 137 insertions, 128 deletions
diff --git a/src/client/Network.ts b/src/client/Network.ts
index ccf60f199..6982ecf19 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -1,5 +1,6 @@
import { Utils } from "../Utils";
import requestPromise = require('request-promise');
+import { Upload } from "../server/SharedMediaTypes";
export namespace Networking {
@@ -17,12 +18,21 @@ export namespace Networking {
return requestPromise.post(options);
}
- export async function PostFormDataToServer(relativeRoute: string, formData: FormData) {
+ export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
+ const formData = new FormData();
+ if (Array.isArray(files)) {
+ if (!files.length) {
+ return [];
+ }
+ files.forEach(file => formData.append(Utils.GenerateGuid(), file));
+ } else {
+ formData.append(Utils.GenerateGuid(), files);
+ }
const parameters = {
method: 'POST',
body: formData
};
- const response = await fetch(relativeRoute, parameters);
+ const response = await fetch("/uploadFormData", parameters);
return response.json();
}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index d04f56e57..3d8bcbab7 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -22,7 +22,7 @@ import "./DirectoryImportBox.scss";
import { Networking } from "../../Network";
import { BatchedArray } from "array-batcher";
import * as path from 'path';
-import { AcceptibleMedia } from "../../../server/SharedMediaTypes";
+import { AcceptibleMedia, Upload } from "../../../server/SharedMediaTypes";
const unsupported = ["text/html", "text/plain"];
@@ -107,20 +107,21 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
runInAction(() => this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`);
const batched = BatchedArray.from(validated, { batchSize: 15 });
- const uploads = await batched.batchedMapAsync<any>(async (batch, collector) => {
- const formData = new FormData();
-
+ const uploads = await batched.batchedMapAsync<Upload.FileResponse<Upload.ImageInformation>>(async (batch, collector) => {
batch.forEach(file => {
sizes.push(file.size);
modifiedDates.push(file.lastModified);
- formData.append(Utils.GenerateGuid(), file);
});
-
- collector.push(...(await Networking.PostFormDataToServer("/uploadFormData", formData)));
+ collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch)));
runInAction(() => this.completed += batch.length);
});
- await Promise.all(uploads.map(async ({ name, type, accessPaths, exifData }) => {
+ await Promise.all(uploads.map(async response => {
+ const { source: { type }, result } = response;
+ if (result instanceof Error) {
+ return;
+ }
+ const { accessPaths, exifData } = result;
const path = Utils.prepend(accessPaths.agnostic.client);
const document = await Docs.Get.DocumentFromType(type, path, { _width: 300, title: name });
const { data, error } = exifData;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0963e1ea6..e0b2d524b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, trace } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
import * as rp from 'request-promise';
import CursorField from "../../../new_fields/CursorField";
import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
@@ -25,6 +25,7 @@ import { ImageUtils } from "../../util/Import & Export/ImageUtils";
import { Networking } from "../../Network";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
import { InteractionUtils } from "../../util/InteractionUtils";
+import { Upload } from "../../../server/SharedMediaTypes";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc) => boolean;
@@ -288,6 +289,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
const { items } = e.dataTransfer;
const { length } = items;
+ const files: File[] = [];
if (length) {
const batch = UndoManager.StartBatch("collection view drop");
const promises: Promise<void>[] = [];
@@ -307,41 +309,31 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
});
promises.push(prom);
}
- const type = item.type;
if (item.kind === "file") {
const file = item.getAsFile();
- const formData = new FormData();
-
- if (!file || !file.type) {
- continue;
- }
-
- formData.append('file', file);
- const dropFileName = file ? file.name : "-empty-";
- promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => {
- results.map(action((result: any) => {
- const { accessPaths, nativeWidth, nativeHeight, contentSize } = result;
- if (Object.keys(accessPaths).length) {
- const full = { ...options, _width: 300, title: dropFileName };
- const pathname = Utils.prepend(accessPaths.agnostic.client);
- Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
- if (doc) {
- const proto = Doc.GetProto(doc);
- proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
- nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
- nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
- contentSize && (proto.contentSize = contentSize);
- this.props?.addDocument(doc);
- }
- });
- } else {
- alert("Upload failed...");
- }
- }));
- }));
+ file && file.type && files.push(file);
}
}
-
+ (await Networking.UploadFilesToServer(files)).forEach(({ source: { name, type }, result }) => {
+ if (result instanceof Error) {
+ alert(`Upload failed: ${result.message}`);
+ return;
+ }
+ const full = { ...options, _width: 300, title: name };
+ const pathname = Utils.prepend(result.accessPaths.agnostic.client);
+ Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
+ if (doc) {
+ const proto = Doc.GetProto(doc);
+ proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ if (Upload.isImageInformation(result)) {
+ proto["data-nativeWidth"] = result.nativeWidth;
+ proto["data-nativeHeight"] = result.nativeHeight;
+ proto.contentSize = result.contentSize;
+ }
+ this.props?.addDocument(doc);
+ }
+ });
+ });
if (promises.length) {
Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
} else {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 55c0660c0..a722f552e 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -112,10 +112,10 @@ export class Doc extends RefField {
// getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
has: (target, key) => key in target.__fields,
ownKeys: target => {
- let obj = {} as any;
+ const obj = {} as any;
Object.assign(obj, target.___fields);
runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
- return Object.keys(obj)
+ return Object.keys(obj);
},
getOwnPropertyDescriptor: (target, prop) => {
if (prop.toString() === "__LAYOUT__") {
@@ -864,7 +864,7 @@ Scripting.addGlobal(function redo() { return UndoManager.Redo(); });
Scripting.addGlobal(function curPresentationItem() {
const curPres = Doc.UserDoc().curPresentation as Doc;
return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
-})
+});
Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().SelectedDocs = new List([doc]); });
Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null)));
diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts
index 47d6bbe83..8041343fd 100644
--- a/src/scraping/buxton/final/BuxtonImporter.ts
+++ b/src/scraping/buxton/final/BuxtonImporter.ts
@@ -8,6 +8,7 @@ const StreamZip = require('node-stream-zip');
const createImageSizeStream = require("image-size-stream");
import { parseXml } from "libxmljs";
import { strictEqual } from "assert";
+import { Readable, PassThrough } from "stream";
interface DocumentContents {
body: string;
@@ -293,15 +294,15 @@ async function writeImages(zip: any): Promise<ImageData[]> {
const imageUrls: ImageData[] = [];
for (const mediaPath of imageEntries) {
- const streamImage = () => new Promise<any>((resolve, reject) => {
+ const streamImage = () => new Promise<Readable>((resolve, reject) => {
zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream));
});
const { width, height, type } = await new Promise<Dimensions>(async resolve => {
- const sizeStream = createImageSizeStream().on('size', (dimensions: Dimensions) => {
+ const sizeStream = (createImageSizeStream() as PassThrough).on('size', (dimensions: Dimensions) => {
readStream.destroy();
resolve(dimensions);
- });
+ }).on("error", () => readStream.destroy());
const readStream = await streamImage();
readStream.pipe(sizeStream);
});
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 04b724f4b..25c54ee2e 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -8,6 +8,7 @@ import { Opt } from "../../new_fields/Doc";
import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils";
import { Database } from "../database";
import { red } from "colors";
+import { Upload } from "../SharedMediaTypes";
const prefix = "google_photos_";
const remoteUploadError = "None of the preliminary uploads to Google's servers was successful.";
@@ -139,7 +140,7 @@ export default class GooglePhotosManager extends ApiManager {
return;
}
let failed = 0;
- const completed: Opt<DashUploadUtils.ImageUploadInformation>[] = [];
+ const completed: Opt<Upload.ImageInformation>[] = [];
for (const { baseUrl } of mediaItems) {
// start by getting the content size of the remote image
const results = await DashUploadUtils.InspectImage(baseUrl);
@@ -151,7 +152,7 @@ export default class GooglePhotosManager extends ApiManager {
const { contentSize, ...attributes } = results;
// check to see if we have uploaded a Google user content image *specifically via this route* already
// that has this exact content size
- const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize);
+ const found: Opt<Upload.ImageInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize);
if (!found) {
// if we haven't, then upload it locally to Dash's server
const upload = await DashUploadUtils.UploadInspectedImage({ contentSize, ...attributes }, undefined, prefix, false).catch(error => _error(res, downloadError, error));
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 4d09528f4..8f2a5ea3e 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -4,12 +4,12 @@ import * as formidable from 'formidable';
import v4 = require('uuid/v4');
const AdmZip = require('adm-zip');
import { extname, basename, dirname } from 'path';
-import { createReadStream, createWriteStream, unlink, readFileSync } from "fs";
+import { createReadStream, createWriteStream, unlink } from "fs";
import { publicDirectory, filesDirectory } from "..";
import { Database } from "../database";
-import { DashUploadUtils, SizeSuffix } from "../DashUploadUtils";
+import { DashUploadUtils } from "../DashUploadUtils";
import * as sharp from 'sharp';
-import { AcceptibleMedia } from "../SharedMediaTypes";
+import { AcceptibleMedia, Upload } from "../SharedMediaTypes";
import { normalize } from "path";
const imageDataUri = require('image-data-uri');
@@ -47,7 +47,7 @@ export default class UploadManager extends ApiManager {
form.keepExtensions = true;
return new Promise<void>(resolve => {
form.parse(req, async (_err, _fields, files) => {
- const results: any[] = [];
+ const results: Upload.FileResponse[] = [];
for (const key in files) {
const result = await DashUploadUtils.upload(files[key]);
result && results.push(result);
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 913ddc1c3..b66651ef2 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -3,9 +3,9 @@ import { Utils } from '../Utils';
import * as path from 'path';
import * as sharp from 'sharp';
import request = require('request-promise');
-import { ExifData, ExifImage } from 'exif';
+import { ExifImage } from 'exif';
import { Opt } from '../new_fields/Doc';
-import { AcceptibleMedia } from './SharedMediaTypes';
+import { AcceptibleMedia, Upload } from './SharedMediaTypes';
import { filesDirectory } from '.';
import { File } from 'formidable';
import { basename } from "path";
@@ -14,7 +14,7 @@ import { ParsedPDF } from "../server/PdfTypes";
const parse = require('pdf-parse');
import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './ApiManagers/UploadManager';
import { red } from 'colors';
-import { Writable } from 'stream';
+import { Stream } from 'stream';
const requestImageSize = require("../client/util/request-image-size");
export enum SizeSuffix {
@@ -40,13 +40,6 @@ export namespace DashUploadUtils {
suffix: SizeSuffix;
}
- export interface ImageFileResponse {
- name: string;
- path: string;
- type: string;
- exif: Opt<DashUploadUtils.EnrichedExifData>;
- }
-
export const Sizes: { [size: string]: Size } = {
SMALL: { width: 100, suffix: SizeSuffix.Small },
MEDIUM: { width: 400, suffix: SizeSuffix.Medium },
@@ -60,20 +53,9 @@ export namespace DashUploadUtils {
const size = "content-length";
const type = "content-type";
- export interface ImageUploadInformation {
- accessPaths: AccessPathInfo;
- exifData: EnrichedExifData;
- contentSize?: number;
- contentType?: string;
- }
-
- export interface AccessPathInfo {
- [suffix: string]: { client: string, server: string };
- }
-
const { imageFormats, videoFormats, applicationFormats } = AcceptibleMedia;
- export async function upload(file: File): Promise<any> {
+ export async function upload(file: File): Promise<Upload.FileResponse> {
const { type, path, name } = file;
const types = type.split("/");
@@ -83,33 +65,33 @@ export namespace DashUploadUtils {
switch (category) {
case "image":
if (imageFormats.includes(format)) {
- const results = await UploadImage(path, basename(path));
- return { ...results, name, type };
+ const result = await UploadImage(path, basename(path));
+ return { source: file, result };
}
case "video":
if (videoFormats.includes(format)) {
- return MoveParsedFile(path, Directory.videos);
+ return MoveParsedFile(file, Directory.videos);
}
case "application":
if (applicationFormats.includes(format)) {
- return UploadPdf(path);
+ return UploadPdf(file);
}
}
console.log(red(`Ignoring unsupported file (${name}) with upload type (${type}).`));
- return { accessPaths: {} };
+ return { source: file, result: new Error(`Could not upload unsupported file (${name}) with upload type (${type}).`) };
}
- async function UploadPdf(absolutePath: string) {
- const dataBuffer = readFileSync(absolutePath);
+ async function UploadPdf(file: File) {
+ const { path, name } = file;
+ const dataBuffer = readFileSync(path);
const result: ParsedPDF = await parse(dataBuffer);
- const parsedName = basename(absolutePath);
await new Promise<void>((resolve, reject) => {
- const textFilename = `${parsedName.substring(0, parsedName.length - 4)}.txt`;
+ const textFilename = `${name.substring(0, name.length - 4)}.txt`;
const writeStream = createWriteStream(serverPathToFile(Directory.text, textFilename));
writeStream.write(result.text, error => error ? reject(error) : resolve());
});
- return MoveParsedFile(absolutePath, Directory.pdfs);
+ return MoveParsedFile(file, Directory.pdfs);
}
/**
@@ -123,13 +105,13 @@ export namespace DashUploadUtils {
* @param {string} prefix is a string prepended to the generated image name in the
* event that @param filename is not specified
*
- * @returns {ImageUploadInformation} This method returns
+ * @returns {ImageUploadInformation | Error} This method returns
* 1) the paths to the uploaded images (plural due to resizing)
- * 2) the file name of each of the resized images
+ * 2) the exif data embedded in the image, or the error explaining why exif couldn't be parsed
* 3) the size of the image, in bytes (4432130)
* 4) the content type of the image, i.e. image/(jpeg | png | ...)
*/
- export const UploadImage = async (source: string, filename?: string, prefix: string = ""): Promise<ImageUploadInformation | Error> => {
+ export const UploadImage = async (source: string, filename?: string, prefix: string = ""): Promise<Upload.ImageInformation | Error> => {
const metadata = await InspectImage(source);
if (metadata instanceof Error) {
return metadata;
@@ -137,22 +119,6 @@ export namespace DashUploadUtils {
return UploadInspectedImage(metadata, filename || metadata.filename, prefix);
};
- export interface InspectionResults {
- source: string;
- requestable: string;
- exifData: EnrichedExifData;
- contentSize: number;
- contentType: string;
- nativeWidth: number;
- nativeHeight: number;
- filename?: string;
- }
-
- export interface EnrichedExifData {
- data: ExifData;
- error?: string;
- }
-
export async function buildFileDirectories() {
const pending = Object.keys(Directory).map(sub => createIfNotExists(`${filesDirectory}/${sub}`));
return Promise.all(pending);
@@ -175,7 +141,7 @@ export namespace DashUploadUtils {
*
* @param source is the path or url to the image in question
*/
- export const InspectImage = async (source: string): Promise<InspectionResults | Error> => {
+ export const InspectImage = async (source: string): Promise<Upload.InspectionResults | Error> => {
let rawMatches: RegExpExecArray | null;
let filename: string | undefined;
if ((rawMatches = /^data:image\/([a-z]+);base64,(.*)/.exec(source)) !== null) {
@@ -216,14 +182,17 @@ export namespace DashUploadUtils {
};
};
- export async function MoveParsedFile(absolutePath: string, destination: Directory): Promise<Opt<{ accessPaths: AccessPathInfo }>> {
+ export async function MoveParsedFile(file: File, destination: Directory): Promise<Upload.FileResponse> {
+ const { name, path: sourcePath } = file;
return new Promise(resolve => {
- const filename = basename(absolutePath);
- const destinationPath = serverPathToFile(destination, filename);
- rename(absolutePath, destinationPath, error => {
- resolve(error ? undefined : {
- accessPaths: {
- agnostic: getAccessPaths(destination, filename)
+ const destinationPath = serverPathToFile(destination, name);
+ rename(sourcePath, destinationPath, error => {
+ resolve({
+ source: file,
+ result: error ? error : {
+ accessPaths: {
+ agnostic: getAccessPaths(destination, name)
+ }
}
});
});
@@ -237,16 +206,16 @@ export namespace DashUploadUtils {
};
}
- export const UploadInspectedImage = async (metadata: InspectionResults, filename?: string, prefix = "", cleanUp = true): Promise<ImageUploadInformation> => {
+ export const UploadInspectedImage = async (metadata: Upload.InspectionResults, filename?: string, prefix = "", cleanUp = true): Promise<Upload.ImageInformation> => {
const { requestable, source, ...remaining } = metadata;
const extension = `.${remaining.contentType.split("/")[1].toLowerCase()}`;
const resolved = filename || `${prefix}upload_${Utils.GenerateGuid()}${extension}`;
const { images } = Directory;
- const information: ImageUploadInformation = {
+ const information: Upload.ImageInformation = {
accessPaths: {
agnostic: getAccessPaths(images, resolved)
},
- ...remaining
+ ...metadata
};
const outputPath = pathToDirectory(Directory.images);
const writtenFiles = await outputResizedImages(() => request(requestable), outputPath, resolved, extension);
@@ -259,9 +228,9 @@ export namespace DashUploadUtils {
return information;
};
- const parseExifData = async (source: string): Promise<EnrichedExifData> => {
+ const parseExifData = async (source: string): Promise<Upload.EnrichedExifData> => {
const image = await request.get(source, { encoding: null });
- return new Promise<EnrichedExifData>(resolve => {
+ return new Promise(resolve => {
new ExifImage({ image }, (error, data) => {
let reason: Opt<string> = undefined;
if (error) {
@@ -279,18 +248,14 @@ export namespace DashUploadUtils {
force: true
};
- export interface ReadStreamLike {
- pipe: (dest: Writable) => Writable;
- }
-
- export async function outputResizedImages(readStreamSource: () => ReadStreamLike | Promise<ReadStreamLike>, outputPath: string, fileName: string, ext: string) {
+ export async function outputResizedImages(readStreamSource: () => Stream | Promise<Stream>, outputPath: string, fileName: string, ext: string) {
const writtenFiles: { [suffix: string]: string } = {};
for (const { resizer, suffix } of resizers(ext)) {
const resolved = writtenFiles[suffix] = InjectSize(fileName, suffix);
await new Promise<void>(async (resolve, reject) => {
const writeStream = createWriteStream(path.resolve(outputPath, resolved));
- let readStream: ReadStreamLike;
const source = readStreamSource();
+ let readStream: Stream;
if (source instanceof Promise) {
readStream = await source;
} else {
@@ -320,7 +285,7 @@ export namespace DashUploadUtils {
initial = initial.webp();
} else if (tiffs.includes(ext)) {
initial = initial.tiff();
- } else {
+ } else if (ext === ".gif") {
initial = undefined;
}
return {
diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts
index 274b4f01e..185e787cc 100644
--- a/src/server/SharedMediaTypes.ts
+++ b/src/server/SharedMediaTypes.ts
@@ -1,3 +1,6 @@
+import { ExifData } from 'exif';
+import { File } from 'formidable';
+
export namespace AcceptibleMedia {
export const gifs = [".gif"];
export const pngs = [".png"];
@@ -7,4 +10,40 @@ export namespace AcceptibleMedia {
export const imageFormats = [...pngs, ...jpgs, ...gifs, ...webps, ...tiffs];
export const videoFormats = [".mov", ".mp4"];
export const applicationFormats = [".pdf"];
+}
+
+export namespace Upload {
+
+ export function isImageInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation {
+ return "nativeWidth" in uploadResponse;
+ }
+
+ export interface FileInformation {
+ accessPaths: AccessPathInfo;
+ }
+
+ export type FileResponse<T extends FileInformation = FileInformation> = { source: File, result: T | Error };
+
+ export type ImageInformation = FileInformation & InspectionResults;
+
+ export interface AccessPathInfo {
+ [suffix: string]: { client: string, server: string };
+ }
+
+ export interface InspectionResults {
+ source: string;
+ requestable: string;
+ exifData: EnrichedExifData;
+ contentSize: number;
+ contentType: string;
+ nativeWidth: number;
+ nativeHeight: number;
+ filename?: string;
+ }
+
+ export interface EnrichedExifData {
+ data: ExifData;
+ error?: string;
+ }
+
} \ No newline at end of file
diff --git a/src/server/database.ts b/src/server/database.ts
index 83ce865c6..055f04c49 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -2,12 +2,12 @@ import * as mongodb from 'mongodb';
import { Transferable } from './Message';
import { Opt } from '../new_fields/Doc';
import { Utils, emptyFunction } from '../Utils';
-import { DashUploadUtils } from './DashUploadUtils';
import { Credentials } from 'google-auth-library';
import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
import { IDatabase } from './IDatabase';
import { MemoryDatabase } from './MemoryDatabase';
import * as mongoose from 'mongoose';
+import { Upload } from './SharedMediaTypes';
export namespace Database {
@@ -297,7 +297,7 @@ export namespace Database {
};
export const QueryUploadHistory = async (contentSize: number) => {
- return SanitizedSingletonQuery<DashUploadUtils.ImageUploadInformation>({ contentSize }, AuxiliaryCollections.GooglePhotosUploadHistory);
+ return SanitizedSingletonQuery<Upload.ImageInformation>({ contentSize }, AuxiliaryCollections.GooglePhotosUploadHistory);
};
export namespace GoogleAuthenticationToken {
@@ -326,7 +326,7 @@ export namespace Database {
}
- export const LogUpload = async (information: DashUploadUtils.ImageUploadInformation) => {
+ export const LogUpload = async (information: Upload.ImageInformation) => {
const bundle = {
_id: Utils.GenerateDeterministicGuid(String(information.contentSize!)),
...information