UNPKG

gridsome-source-directus

Version:

Gridsome Source Plugin to load data from Directus CMS

302 lines (247 loc) 8.31 kB
const DirectusSDK = require("@directus/sdk-js"); const fs = require('fs'); const path = require('path'); /** * Convert `const http` to variable to change protocol from project options */ let http = require('https'); /** * Default upload image path */ let uploadImagesDir = './.cache-directus/img-cache' const imageTypes = [ 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', ] // TODO ADD CLEANUP OF UNUSED IMAGES / FILES let download = async (url, dest, dir = uploadImagesDir) => { var imgName = dest; if (!fs.existsSync('./.cache-directus')){ fs.mkdirSync('./.cache-directus'); } if (!fs.existsSync(dir)){ fs.mkdirSync(dir); } dest = dir + '/' + dest; var cleanImageName = path.resolve(dest); if (fs.existsSync(dest)) return cleanImageName; console.log(' -- Downloading Resource: ' + imgName); return new Promise((resolve, reject) => { var file = fs.createWriteStream(dest); http.get(url, function(response) { response.pipe(file); file.on('finish', function() { resolve(cleanImageName); }); }).on('error', function(err) { fs.unlink(dest); reject(err.message); }); }); }; function sanitizeFields(fields) { Object.keys(fields).forEach((key) => { if(fields[key] === null || fields[key] === undefined) { delete fields[key]; } }); return fields; } function sanitizeItem(fields) { let { id, title, slug, path, date, content, excerpt } = fields; let _id = id.toString(); delete fields.id; fields._directusID = _id; return sanitizeFields(fields); } /** * Convert nested object to flat object * https://stackoverflow.com/questions/34513964/how-to-convert-this-nested-object-into-a-flat-object * */ function traverseAndFlatten(currentNode, target, flattenedKey) { for (var key in currentNode) { if (currentNode.hasOwnProperty(key)) { var newKey; if (flattenedKey === undefined) { newKey = key; } else { newKey = flattenedKey + '__' + key; } var value = currentNode[key]; if (typeof value === "object") { traverseAndFlatten(value, target, newKey); } else { target[newKey] = value; } } } } function flatten(obj) { var flattenedObject = {}; traverseAndFlatten(obj, flattenedObject); return flattenedObject; } /** * End. https://stackoverflow.com/questions/34513964/how-to-convert-this-nested-object-into-a-flat-object * */ async function checkForImages(item) { for(const itemKey in item) { const itemContent = item[itemKey]; if(itemContent && itemContent.type && imageTypes.includes(itemContent.type)) { item[itemKey].gridsome_image = await download(itemContent.data.full_url, itemContent.filename_disk); } else if(itemContent && itemKey !== 'owner' && typeof itemContent === 'object' && Object.keys(itemContent).length > 0) { item[itemKey] = await checkForImages(itemContent); } } return item; } async function checkForDownloads(item) { for(const itemKey in item) { const itemContent = item[itemKey]; if(itemContent && itemContent.type && itemContent.data) { item[itemKey].gridsome_link = await download(itemContent.data.full_url, itemContent.filename_disk, './.cache-directus/file-cache'); } else if(itemContent && itemKey !== 'owner' && typeof itemContent === 'object' && Object.keys(itemContent).length > 0) { item[itemKey] = await checkForDownloads(itemContent); } } return item; } class DirectusSource { static defaultOptions () { return { typeName: 'Directus', apiUrl: undefined, project: '_', staticToken: undefined, email: undefined, password: undefined, maxRetries: 3, reconnectTimeout: 10000, collections: [] } } constructor (api, options) { this.api = api; this.options = options; /** * Options for setting download protocol && images upload directory */ if( options.global ) { if( options.global.protocol ) { http = require(options.global.protocol) } if( options.global.uploadImagesDir ) { uploadImagesDir = options.global.uploadImagesDir } } api.loadSource(args => this.fetchContent(args)); } async fetchContent (store) { const { addCollection, getContentType, slugify } = store const { apiUrl, project, staticToken, email, password, collections, maxRetries, reconnectTimeout } = this.options const directusOptions = { url: apiUrl, project: project, token: staticToken }; const client = new DirectusSDK(directusOptions); let retries = 0; let connect = async () => { return new Promise(async (resolve, reject) => { try { await client.login(Object.assign({ email, password, persist: false }, directusOptions)); resolve(await client.getCollections()); } catch (e) { console.error("DIRECTUS ERROR: Can not login to Directus", e); if(retries < maxRetries) { retries++; console.log("DIRECTUS - Retrying to connect in 10 seconds..."); setTimeout(async () => { await connect(); }, reconnectTimeout); } else { reject(process.exit(1)) throw new Error("DIRECTUS ERROR: Can not login to Directus"); } } }); } if(email && password) { let data = await connect(); } console.log("DIRECTUS: Loading data from Directus at: " + apiUrl); if(collections.length <= 0) { console.error("DIRECTUS ERROR: No Directus collections specified!"); process.exit(1) throw new Error("DIRECTUS ERROR: No Directus collections specified!"); } for(const collection of collections) { let collectionName; let params; let directusPathName; if(typeof collection === 'object') { collectionName = collection.name; directusPathName = collection.directusPathName || collectionName delete collection.name; params = collection; } else { collectionName = collection; } try { if(!params.limit) { params.limit = -1; } let data = await client.getItems(directusPathName, params); data = data.data; let route; if(params) { if(params.hasRoute) { route = `/${slugify(collectionName)}/:slug`; } else if(params.route) { if(typeof params.route === 'function') { route = params.route(collectionName, collection, slugify); } else { route = params.route; } } } const contentType = addCollection({ typeName: collectionName, // TODO change name creation route: route }) for(let item of data) { if(params.downloadImages) { item = await checkForImages(item); } if(params.downloadFiles) { item = await checkForDownloads(item); } /** * Convert nested object to flat object */ if( params.flat ) { item = flatten(item); } /** * Check if params.sanitizeID === false to sanitize Node ID or not */ if( params.sanitizeID === false ) { contentType.addNode(sanitizeFields(item)) } else { contentType.addNode(sanitizeItem(item)) } } } catch (e) { console.error("DIRECTUS ERROR: Can not load data for collection '", e); process.exit(1) throw "DIRECTUS ERROR: Can not load data for collection '" + collectionName + "'!"; } } console.log("DIRECTUS: Loading done!"); client.logout(); } } module.exports = DirectusSource