This repository has been archived by the owner on Sep 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
main.js
150 lines (130 loc) · 4.57 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* eslint-disable node/no-missing-import */
import { Meteor } from "meteor/meteor";
import { Mongo, MongoInternals } from "meteor/mongo";
import { Random } from "meteor/random";
import { WebApp } from "meteor/webapp";
import { check } from "meteor/check";
import fetch from "node-fetch";
import {
FileDownloadManager,
FileRecord,
RemoteUrlWorker,
MongoFileCollection,
TempFileStore,
TempFileStoreWorker
} from "@reactioncommerce/file-collections";
import GridFSStore from "@reactioncommerce/file-collections-sa-gridfs";
// handle any unhandled Promise rejections
process.on("unhandledRejection", (err) => {
console.error("UNHANDLED REJECTION", err); // eslint-disable-line no-console
});
// lazy loading sharp package
let sharp;
/**
* @returns {void} null
*/
async function lazyLoadSharp() {
if (sharp) return;
const mod = await import("sharp");
sharp = mod.default;
}
const chunkSize = 1 * 1024 * 1024;
const mongodb = MongoInternals.NpmModules.mongodb.module;
const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo;
const stores = [
new GridFSStore({
chunkSize,
db,
mongodb,
name: "images",
async transformWrite(fileRecord) {
await lazyLoadSharp();
// Need to update the content type and extension of the file info, too.
// The new size gets set correctly automatically.
fileRecord.type("image/jpeg", { store: "images" });
fileRecord.extension("jpg", { store: "images" });
// Resize keeping aspect ratio so that largest side is max 1600px, and convert to JPEG if necessary
return sharp().resize(1600, 1600, { fit: "inside", withoutEnlargement: true }).toFormat("jpeg");
}
}),
new GridFSStore({
chunkSize,
db,
mongodb,
name: "thumbs",
async transformWrite(fileRecord) {
await lazyLoadSharp();
// Need to update the content type and extension of the file info, too.
// The new size gets set correctly automatically.
fileRecord.type("image/png", { store: "thumbs" });
fileRecord.extension("png", { store: "thumbs" });
// Resize to 100x100 square, cropping to fit, and convert to PNG if necessary
return sharp().resize(100, 100, { fit: "cover" }).toFormat("png");
}
})
];
const tempStore = new TempFileStore({
shouldAllowRequest(req) {
const { type } = req.uploadMetadata;
if (typeof type !== "string" || !type.startsWith("image/")) {
console.info(`shouldAllowRequest received request to upload file of type "${type}" and denied it`); // eslint-disable-line no-console
return false;
}
return true;
}
});
const ImagesCollection = new Mongo.Collection("ImagesFileCollection");
const Images = new MongoFileCollection("Images", {
// add more security here if the files should not be public
allowGet: () => true,
collection: ImagesCollection.rawCollection(),
makeNewStringID: () => Random.id(),
stores,
tempStore
});
Meteor.methods({
async insertRemoteImage(url) {
check(url, String);
const fileRecord = await FileRecord.fromUrl(url, { fetch });
return Images.insert(fileRecord, { raw: true });
},
async insertUploadedImage(fileRecordDocument) {
check(fileRecordDocument, Object);
return Images._insert(fileRecordDocument);
},
async removeImage(id) {
const fileRecord = await Images.findOne(id);
if (!fileRecord) throw new Meteor.Error("not-found", `No FileRecord has ID ${id}`);
const result = await Images.remove(fileRecord);
return result;
},
async removeAllImages() {
const images = await Images.find();
const result = await Promise.all(images.map((fileRecord) => Images.remove(fileRecord)));
return result;
},
async cloneImage(id) {
const fileRecord = await Images.findOne(id);
if (!fileRecord) throw new Meteor.Error("not-found", `No FileRecord has ID ${id}`);
// The side effect of this call should be that a new file record now
// exists with data in both stores, and will be autopublished to the client.
await fileRecord.fullClone();
}
});
const downloadManager = new FileDownloadManager({
collections: [Images],
headers: {
get: {
"Cache-Control": "public, max-age=31536000"
}
}
});
const remoteUrlWorker = new RemoteUrlWorker({ fetch, fileCollections: [Images] });
remoteUrlWorker.start();
const uploadWorker = new TempFileStoreWorker({ fileCollections: [Images] });
uploadWorker.start();
WebApp.connectHandlers.use("/juicy/uploads", (req, res) => {
req.baseUrl = "/juicy/uploads"; // tus relies on this being set
tempStore.connectHandler(req, res);
});
WebApp.connectHandlers.use("/files", downloadManager.connectHandler);