sb-mig
Version:
CLI to rule the world. (and handle stuff related to Storyblok CMS)
199 lines (198 loc) • 7.42 kB
JavaScript
import fs from "fs";
import https from "https";
import path from "path";
import FormData from "form-data";
import { createDir, isDirectoryExists } from "../../utils/files.js";
import Logger from "../../utils/logger.js";
import { getFileName, getSizeFromURL } from "../../utils/string-utils.js";
const isStoryblokSize = (size) => Boolean(size && /^\d+x\d+$/i.test(size));
const prepareSignedUploadPayload = (payload) => {
const { filename, asset_folder_id, id, size, validate_upload } = payload;
const inferredSize = isStoryblokSize(size)
? size
: isStoryblokSize(getSizeFromURL(filename))
? getSizeFromURL(filename)
: undefined;
return {
filename: getFileName(filename),
...(asset_folder_id === undefined || asset_folder_id === null
? {}
: { asset_folder_id }),
...(id === undefined ? {} : { id }),
...(inferredSize ? { size: inferredSize } : {}),
...(validate_upload === undefined ? {} : { validate_upload }),
};
};
// GET
export const getAllAssets = async (args, config) => {
const { spaceId, search } = args;
const { sbApi } = config;
return sbApi
.get(`spaces/${spaceId}/assets/`, {
// @ts-ignore TODO: have to submit ISSUE to storyblok-js-client (in documentation it is search, in typescript its search_term STORYBLOK_ISSUE
search: search ? search : "",
per_page: 100, // need to do pagination here probably
})
.then(({ data }) => data)
.catch((err) => {
if (err.response.status === 404) {
Logger.error(`There is no assets in your Storyblok ${spaceId} space.`);
return true;
}
else {
Logger.error(err);
return false;
}
});
};
export const getAssetByName = async ({ spaceId, fileName }, config) => {
const result = await getAllAssets({ spaceId, search: fileName }, config);
if (result.assets.length === 1) {
return result.assets[0];
}
else {
return undefined;
}
};
const requestSignedUploadUrl = ({ spaceId, payload }, config) => {
const { sbApi, debug } = config;
const signedUploadPayload = prepareSignedUploadPayload(payload);
return sbApi
.post(`spaces/${spaceId}/assets/`, signedUploadPayload)
.then((signedResponseObject) => {
if (debug) {
Logger.log(`Signed upload URL has been requested for ${signedUploadPayload.filename}.`);
}
return signedResponseObject.data; // this is very bad... but storyblok-js-client types are pretty broken
})
.catch((err) => {
console.log(err);
});
};
const uploadFile = ({ signedResponseObject, pathToFile }) => {
const file = pathToFile;
const form = new FormData();
// apply all fields from the signed response object to the second request
for (const key in signedResponseObject.fields) {
form.append(key, signedResponseObject.fields[key]);
}
// also append the file read stream
form.append("file", fs.createReadStream(file));
// submit your form
return new Promise((resolve, reject) => {
form.submit(signedResponseObject.post_url, (err, res) => {
if (err) {
reject(err);
return;
}
const statusCode = res?.statusCode;
if (statusCode === 204) {
Logger.upload(`Asset uploaded ${getFileName(file)}`);
resolve();
return;
}
reject(new Error(`Asset upload failed with status code ${statusCode ?? "unknown"}`));
});
});
};
const downloadAsset = async (args, config) => {
const { debug, sbmigWorkingDirectory } = config;
const { payload } = args;
if (!sbmigWorkingDirectory) {
throw Error("sbmigWorkingDirectory is not defined");
}
const fileName = getFileName(payload.filename);
const fileUrl = payload.filename;
const downloadedAssetsFolder = path.join(sbmigWorkingDirectory, "downloadedAssets");
Logger.log(`Downloading ${fileName} asset ${debug ? `from ${fileUrl} to ${downloadedAssetsFolder}` : ""}`);
if (!isDirectoryExists(downloadedAssetsFolder)) {
await createDir(downloadedAssetsFolder);
}
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(path.join(downloadedAssetsFolder, fileName));
https
.get(fileUrl, (response) => {
response.pipe(file);
file.on("finish", () => {
file.close();
Logger.download(`Asset downloaded to ${path.join(downloadedAssetsFolder, fileName)}`);
resolve(path.join(downloadedAssetsFolder, fileName));
});
})
.on("error", (error) => {
Logger.error(`Error downloading image: ${error.message}`);
reject("error");
});
});
};
export const migrateAsset = async ({ migrateTo, payload, syncDirection }, config) => {
const pathToFile = await downloadAsset({ payload }, config);
if (syncDirection === "fromSpaceToSpace") {
const signedResponseObject = await requestSignedUploadUrl({
spaceId: migrateTo,
payload,
}, config);
if (pathToFile) {
await uploadFile({ signedResponseObject, pathToFile });
}
}
return true;
};
export const createAsset = async ({ spaceId, pathToFile, payload = {} }, config) => {
const signedResponseObject = await requestSignedUploadUrl({
spaceId,
payload: {
...payload,
filename: payload.filename ?? pathToFile,
},
}, config);
await uploadFile({ signedResponseObject, pathToFile });
return signedResponseObject;
};
export const updateAsset = async ({ spaceId, assetId, payload }, config) => {
const { sbApi } = config;
Logger.log(`Trying to update asset with id ${assetId}.`);
return sbApi
.put(`spaces/${spaceId}/assets/${assetId}`, payload)
.then((res) => {
Logger.success(`Asset '${assetId}' has been updated.`);
return res.data;
})
.catch((err) => {
Logger.error(`${err.message} in updateAsset function for asset ${assetId}`);
throw err;
});
};
// GET
export const getAssetById = async ({ spaceId, assetId }, config) => {
const { sbApi } = config;
Logger.log(`Trying to get '${assetId}' asset.`);
return sbApi
.get(`spaces/${spaceId}/assets/${assetId}`)
.then(({ data }) => data)
.catch((err) => {
if (err.response.status === 404) {
Logger.error(`There is no assets in your Storyblok ${spaceId} space.`);
return true;
}
else {
Logger.error(err);
return false;
}
});
};
export const getAsset = async (assetName) => {
Logger.log(`Trying to get '${assetName}' asset.`);
// return getAllAssets()
// .then((res) =>
// res.space_roles.filter((role: any) => role.role === assetName)
// )
// .then((res) => {
// if (Array.isArray(res) && res.length === 0) {
// Logger.warning(`There is no role named '${assetName}'`);
// return false;
// }
// return res;
// })
// .catch((err) => Logger.error(err));
};