profile-plus
Version:
271 lines • 13 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
//import all ShapeProviders here or define a generic BackendProvider, see documentation on https://docs.lincd.org
const BackendProvider_1 = require("lincd-server-utils/utils/BackendProvider");
const formidable_1 = require("formidable");
const ProfilePicture_1 = require("profile-pics/shapes/ProfilePicture");
const path_1 = __importDefault(require("path"));
const ImageObject_1 = require("lincd-schema/shapes/ImageObject");
const sharp_1 = __importDefault(require("sharp"));
const node_fetch_1 = __importDefault(require("node-fetch"));
const Upload_1 = require("lincd-server-utils/utils/Upload");
const Person_js_1 = require("./shapes/Person.js");
const events_1 = require("lincd-auth/utils/events");
class ProfilePlusBackendProvider extends BackendProvider_1.BackendProvider {
setupBeforeControllers() {
(0, events_1.onAccountWillBeRemoved)((account) => {
var _a, _b, _c, _d, _e;
if (account.accountOfNode) {
let p = new Person_js_1.Person(account.accountOfNode);
(_a = p.profilePicture) === null || _a === void 0 ? void 0 : _a.remove();
(_b = p.profilePicture2) === null || _b === void 0 ? void 0 : _b.remove();
(_c = p.profilePicture3) === null || _c === void 0 ? void 0 : _c.remove();
(_d = p.profilePicture4) === null || _d === void 0 ? void 0 : _d.remove();
(_e = p.profilePicture5) === null || _e === void 0 ? void 0 : _e.remove();
if (p.address) {
p.address.remove();
}
if (p.homeLocation) {
p.homeLocation.forEach((l) => l.remove());
}
if (p.birthPlace) {
p.birthPlace.remove();
}
}
});
}
loadProfilePicture(property) {
var _a, _b, _c;
//TODO: replace uploader with a lincd component
const auth = (_a = this.request.linkedAuth) === null || _a === void 0 ? void 0 : _a.userAccount;
if (!auth) {
console.warn('loadProfilePicture: No user authenticated.');
return;
}
const user = auth.accountOf;
return user && ((_b = user[property]) === null || _b === void 0 ? void 0 : _b.cropped) ? (_c = user[property]) === null || _c === void 0 ? void 0 : _c.cropped : null;
}
// upload image
uploadImage() {
const form = (0, formidable_1.formidable)({});
return new Promise((resolve, reject) => {
var _a;
// get property query
const property = this.request.query.property;
// check if user is authenticated
const auth = (_a = this.request.linkedAuth) === null || _a === void 0 ? void 0 : _a.userAccount;
if (!auth) {
console.warn('No user authenticated and no subject provided.');
return;
}
// get person
const user = auth.accountOf;
form.parse(this.request, (err, fields, file, files, body) => __awaiter(this, void 0, void 0, function* () {
const { file: upload } = file;
if (err) {
console.warn('Error parsing uploaded file:' + err.stack);
reject(err);
return;
}
try {
if (!file) {
reject('No file uploaded');
return;
}
// Upload the file and get the public URL
//TODO: camera plugin maybe allows for multiple images? that's why we're getting an array with 1 item? See if that can be changed
const singleFileUpload = Array.isArray(upload) ? upload[0] : upload;
(0, Upload_1.uploadSingleFileFromFormData)({
file: singleFileUpload,
allowedExtensions: ProfilePlusBackendProvider.ALLOWED_EXTENSIONS,
processDataFn: this.compressImage,
})
.then((publicUrl) => {
const image = new ImageObject_1.ImageObject();
image.contentUrl = publicUrl;
image.save();
// Check user has profile picture and save image to user profile picture and image object
if (!user.profilePicture || !user[property]) {
const profilePicture = new ProfilePicture_1.ProfilePicture();
if (property) {
user[property] = profilePicture;
}
else {
user.profilePicture = profilePicture;
}
profilePicture.save();
}
// Update profile picture's image reference
if (property) {
const propertyId = user[property];
if (propertyId) {
propertyId.image = image;
}
else {
throw new Error(`Property '${property}' does not exist on user.`);
}
}
else {
user.profilePicture.image = image;
}
resolve(publicUrl);
})
.catch((error) => {
console.warn('Error during file upload', error);
resolve(null);
});
}
catch (error) {
console.error('Error uploading file:', error);
reject(error);
}
}));
});
}
cropImage(imageUrl, croppedArea, property, fileName) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
// check if user is authenticated
const auth = (_a = this.request.linkedAuth) === null || _a === void 0 ? void 0 : _a.userAccount;
if (!auth) {
console.warn('cropImage: No user authenticated and no subject provided.');
return;
}
const user = auth.accountOf;
if (!imageUrl) {
console.warn('No imageUrl provided.');
return;
}
let imageData;
// Check if imageUrl is a URL or a base64 string
if (imageUrl.startsWith('data:')) {
// Decode base64 image data
const base64Data = imageUrl.replace(/^data:image\/\w+;base64,/, '');
imageData = Buffer.from(base64Data, 'base64');
}
else {
// Fetch the image data from the provided URL using Fetch API
const response = yield (0, node_fetch_1.default)(imageUrl);
if (!response.ok) {
throw new Error('Failed to fetch image from URL');
}
imageData = yield response.buffer();
}
const originalImage = yield (0, sharp_1.default)(imageData);
const originalImageMeta = yield originalImage.metadata();
// console.log('Original: ' + originalImageMeta.width + ' x ' + originalImageMeta.height);
// Extract the cropping parameters from the croppedArea object
const { width, height, x, y } = croppedArea;
// Crop the image using sharp's .extract() method
// console.log(`Cropping: left: ${x}, top: ${y}, width: ${width}, height: ${height}`);
if (width > originalImageMeta.width ||
height > originalImageMeta.height) {
//tilt the image
console.log('Tilting image');
imageData = yield (0, sharp_1.default)(imageData)
.rotate(90)
.toBuffer()
.catch((err) => {
console.warn('Error during sharp extract: ' + err.toString());
return null;
});
}
const croppedImageData = yield (0, sharp_1.default)(imageData)
.extract({ left: x, top: y, width: width, height: height })
.toBuffer()
.catch((err) => {
console.warn('Error during sharp extract: ' + err.toString());
return null;
});
// let fileName;
// if (filename) {
// fileName = filename;
// } else {
if (!fileName) {
// get the original filename and extension from the URL
fileName = path_1.default.basename(imageUrl);
}
try {
const publicUrl = yield (0, Upload_1.uploadSingleFileFromBuffer)({
buffer: croppedImageData,
fileName: fileName,
allowedExtensions: ProfilePlusBackendProvider.ALLOWED_EXTENSIONS,
addSuffix: 'cropped', // add suffix to filename
});
// console.log('publicUrl', publicUrl);
if (!publicUrl) {
throw new Error('Upload failed');
}
const image = new ImageObject_1.ImageObject();
image.contentUrl = publicUrl;
image.save();
let profilePicture = user[property] || new ProfilePicture_1.ProfilePicture();
// update the cropped image
profilePicture.cropped = image;
// save profile picture will user doesn't have profilePicture property
if (!user[property]) {
profilePicture.save();
user[property] = profilePicture;
}
// Return the image url of the saved cropped image
// also return the user, so that the updated data will be sent to the frontend
return [user, publicUrl];
}
catch (err) {
console.error('Error during file cropping', err);
}
}
catch (error) {
throw new Error('Error cropping the image: ' + error.message);
}
});
}
// Compress the image
compressImage(imageData) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Create a single sharp pipeline to handle resizing and compression
const pipeline = (0, sharp_1.default)(imageData).toFormat('jpeg').jpeg({ quality: 90 });
// Resize the image to a maximum width of 1024 pixels if needed
// const imageInfo = await sharp(imageData).metadata();
// if (imageInfo.width > 1024) {
// pipeline.resize({ width: 1024 });
// }
// Execute the pipeline and get the compressed image data
const compressedData = yield pipeline.toBuffer();
return compressedData;
}
catch (error) {
throw new Error('Error compressing the image: ' + error);
}
});
}
}
ProfilePlusBackendProvider.ALLOWED_EXTENSIONS = [
'jpg',
'png',
'gif',
'webp',
'tiff',
'psd',
'raw',
'bmp',
'heif',
'indd',
'jpeg',
];
exports.default = ProfilePlusBackendProvider;
//# sourceMappingURL=backend.js.map