kopia lustrzana https://github.com/manuelkasper/sotlas-api
Move photos to DigitalOcean Spaces
rodzic
641b472cf9
commit
1e757e4667
13
config.js
13
config.js
|
@ -40,10 +40,6 @@ config.summitListUrl = 'https://www.sotadata.org.uk/summitslist.csv';
|
||||||
config.sotatrailsUrl = 'https://sotatrails.ch/api.php';
|
config.sotatrailsUrl = 'https://sotatrails.ch/api.php';
|
||||||
|
|
||||||
config.photos = {
|
config.photos = {
|
||||||
paths: {
|
|
||||||
thumb: '/data/images/photos/thumb',
|
|
||||||
large: '/data/images/photos/large'
|
|
||||||
},
|
|
||||||
sizes: {
|
sizes: {
|
||||||
large: {
|
large: {
|
||||||
width: 1600,
|
width: 1600,
|
||||||
|
@ -58,7 +54,14 @@ config.photos = {
|
||||||
originalStorage: {
|
originalStorage: {
|
||||||
endPoint: 's3.eu-central-003.backblazeb2.com',
|
endPoint: 's3.eu-central-003.backblazeb2.com',
|
||||||
accessKey: process.env.B2_ACCESS_KEY,
|
accessKey: process.env.B2_ACCESS_KEY,
|
||||||
secretKey: process.env.B2_SECRET_KEY
|
secretKey: process.env.B2_SECRET_KEY,
|
||||||
|
bucketName: 'sotlas-photos'
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
endPoint: 'fra1.digitaloceanspaces.com',
|
||||||
|
accessKey: process.env.SPACES_ACCESS_KEY,
|
||||||
|
secretKey: process.env.SPACES_SECRET_KEY,
|
||||||
|
bucketName: 'sotlas-photos'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
57
photos.js
57
photos.js
|
@ -22,20 +22,10 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload original photo to Backblaze (don't wait for completion)
|
// Upload original photo to Backblaze (don't wait for completion)
|
||||||
let minioClient = new minio.Client(config.photos.originalStorage)
|
fsPromises.readFile(filename)
|
||||||
minioClient.fPutObject('sotlas-photos', 'original/' + hashFilename, filename, {'Content-Type': 'image/jpeg'}, (err, etag) => {
|
.then(buffer => {
|
||||||
if (err) {
|
uploadToCloud(config.photos.originalStorage, 'original/' + hashFilename, buffer)
|
||||||
console.error(err)
|
})
|
||||||
|
|
||||||
let transporter = nodemailer.createTransport(config.mail)
|
|
||||||
transporter.sendMail({
|
|
||||||
from: 'api@sotl.as',
|
|
||||||
to: 'mk@neon1.net',
|
|
||||||
subject: 'Backblaze upload failed',
|
|
||||||
text: `The file ${filename} could not be uploaded:\n${err}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let photo = {
|
let photo = {
|
||||||
filename: hashFilename,
|
filename: hashFilename,
|
||||||
|
@ -93,16 +83,17 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mkdirTasks = []
|
let tasks = []
|
||||||
let resizeTasks = []
|
|
||||||
Object.keys(config.photos.sizes).forEach(sizeDescr => {
|
Object.keys(config.photos.sizes).forEach(sizeDescr => {
|
||||||
let outPath = config.photos.paths[sizeDescr] + '/' + hashFilename.substr(0, 2) + '/' + hashFilename
|
tasks.push(
|
||||||
mkdirTasks.push(fsPromises.mkdir(path.dirname(outPath), {recursive: true}))
|
makeResized(filename, config.photos.sizes[sizeDescr].width, config.photos.sizes[sizeDescr].height)
|
||||||
resizeTasks.push(makeResized(filename, outPath, config.photos.sizes[sizeDescr].width, config.photos.sizes[sizeDescr].height))
|
.then(buffer => {
|
||||||
|
return uploadToCloud(config.photos.storage, sizeDescr + '/' + hashFilename, buffer)
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await Promise.all(mkdirTasks)
|
await Promise.all(tasks)
|
||||||
await Promise.all(resizeTasks)
|
|
||||||
|
|
||||||
db.getDb().collection('uploads').insertOne({
|
db.getDb().collection('uploads').insertOne({
|
||||||
uploadDate: new Date(),
|
uploadDate: new Date(),
|
||||||
|
@ -115,10 +106,30 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function uploadToCloud(storageConfig, targetPath, buffer) {
|
||||||
|
let minioClient = new minio.Client(storageConfig)
|
||||||
|
let metadata = {
|
||||||
|
'Content-Type': 'image/jpeg',
|
||||||
|
'x-amz-acl': 'public-read'
|
||||||
|
}
|
||||||
|
return minioClient.putObject(storageConfig.bucketName, targetPath, buffer, metadata)
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
|
||||||
|
let transporter = nodemailer.createTransport(config.mail)
|
||||||
|
transporter.sendMail({
|
||||||
|
from: 'api@sotl.as',
|
||||||
|
to: 'mk@neon1.net',
|
||||||
|
subject: 'Cloud photo upload failed',
|
||||||
|
text: `The file ${filename} could not be uploaded to ${storageConfig.endpoint} at path ${targetPath}:\n${err}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function getMetadata(src) {
|
function getMetadata(src) {
|
||||||
return sharp(src).metadata()
|
return sharp(src).metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeResized(src, dst, maxWidth, maxHeight) {
|
function makeResized(src, maxWidth, maxHeight) {
|
||||||
return sharp(src).rotate().resize({ height: maxHeight, width: maxWidth, fit: 'inside' }).toFile(dst)
|
return sharp(src).rotate().resize({ height: maxHeight, width: maxWidth, fit: 'inside' }).toBuffer()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,20 +53,6 @@ router.post('/summits/:association/:code/upload', jwtCallback, upload.array('pho
|
||||||
|
|
||||||
if (dbPhotos.length > 0) {
|
if (dbPhotos.length > 0) {
|
||||||
await db.getDb().collection('summits').updateOne({code: summitCode}, { $push: { photos: { $each: dbPhotos } } })
|
await db.getDb().collection('summits').updateOne({code: summitCode}, { $push: { photos: { $each: dbPhotos } } })
|
||||||
|
|
||||||
let transporter = nodemailer.createTransport(config.mail)
|
|
||||||
transporter.sendMail({
|
|
||||||
from: 'api@sotl.as',
|
|
||||||
to: 'mk@neon1.net',
|
|
||||||
subject: 'New photos added to summit ' + summitCode + ' by ' + req.user.callsign,
|
|
||||||
text: `${dbPhotos.length} new photos have been added. https://sotl.as/summits/${summitCode}\n`,
|
|
||||||
attachments: dbPhotos.map(photo => {
|
|
||||||
return {
|
|
||||||
filename: photo.filename,
|
|
||||||
path: config.photos.paths.thumb + '/' + photo.filename.substr(0, 2) + '/' + photo.filename
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(dbPhotos)
|
res.json(dbPhotos)
|
||||||
|
|
Ładowanie…
Reference in New Issue