UNPKG

profile-plus

Version:

271 lines 13 kB
"use strict"; 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