UNPKG

@filerobot-strapi/content-plugin

Version:

Scaleflex DAM normalizes, resizes, optimizes and distributes your images rocket fast around the world.

589 lines (588 loc) 25.6 kB
import { jsxs, jsx, Fragment } from "react/jsx-runtime"; import { useFetchClient, Page } from "@strapi/strapi/admin"; import { Routes, Route } from "react-router-dom"; import { useIntl } from "react-intl"; import { Box, Alert, Field, Button, Typography, ProgressBar, Table, Thead, Tr, Th, Tbody, Td, Flex, Main, Tabs } from "@strapi/design-system"; import { useState, useRef, useEffect } from "react"; import { P as PLUGIN_ID } from "./index-BStz19aB.mjs"; import "@filerobot/core/dist/style.min.css"; import "@filerobot/explorer/dist/style.min.css"; import Filerobot from "@filerobot/core"; import Explorer from "@filerobot/explorer"; import XHRUpload from "@filerobot/xhr-upload"; import ProgressPanel from "@filerobot/progress-panel"; var sprintf = {}; (function(exports$1) { !function() { var re = { not_type: /[^T]/, not_primitive: /[^v]/, number: /[diefg]/, numeric_arg: /[bcdiefguxX]/, json: /[j]/, text: /^[^\x25]+/, modulo: /^\x25{2}/, placeholder: /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/, key: /^([a-z_][a-z_\d]*)/i, key_access: /^\.([a-z_][a-z_\d]*)/i, index_access: /^\[(\d+)\]/, sign: /^[+-]/ }; function sprintf2(key) { return sprintf_format(sprintf_parse(key), arguments); } function vsprintf(fmt, argv) { return sprintf2.apply(null, [fmt].concat(argv || [])); } function sprintf_format(parse_tree, argv) { var cursor = 1, tree_length = parse_tree.length, arg, output = "", i, k, ph, pad, pad_character, pad_length, is_positive, sign; for (i = 0; i < tree_length; i++) { if (typeof parse_tree[i] === "string") { output += parse_tree[i]; } else if (typeof parse_tree[i] === "object") { ph = parse_tree[i]; if (ph.keys) { arg = argv[cursor]; for (k = 0; k < ph.keys.length; k++) { if (arg == void 0) { throw new Error(sprintf2('[sprintf] Cannot access property "%s" of undefined value "%s"', ph.keys[k], ph.keys[k - 1])); } arg = arg[ph.keys[k]]; } } else if (ph.param_no) { arg = argv[ph.param_no]; } else { arg = argv[cursor++]; } if (re.not_type.test(ph.type) && re.not_primitive.test(ph.type) && arg instanceof Function) { arg = arg(); } if (re.numeric_arg.test(ph.type) && (typeof arg !== "number" && isNaN(arg))) { throw new TypeError(sprintf2("[sprintf] expecting number but found %T", arg)); } if (re.number.test(ph.type)) { is_positive = arg >= 0; } switch (ph.type) { case "b": arg = parseInt(arg, 10).toString(2); break; case "c": arg = String.fromCharCode(parseInt(arg, 10)); break; case "d": case "i": arg = parseInt(arg, 10); break; case "j": arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0); break; case "e": arg = ph.precision ? parseFloat(arg).toExponential(ph.precision) : parseFloat(arg).toExponential(); break; case "f": arg = ph.precision ? parseFloat(arg).toFixed(ph.precision) : parseFloat(arg); break; case "g": arg = ph.precision ? String(Number(arg.toPrecision(ph.precision))) : parseFloat(arg); break; case "o": arg = (parseInt(arg, 10) >>> 0).toString(8); break; case "s": arg = String(arg); arg = ph.precision ? arg.substring(0, ph.precision) : arg; break; case "t": arg = String(!!arg); arg = ph.precision ? arg.substring(0, ph.precision) : arg; break; case "T": arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase(); arg = ph.precision ? arg.substring(0, ph.precision) : arg; break; case "u": arg = parseInt(arg, 10) >>> 0; break; case "v": arg = arg.valueOf(); arg = ph.precision ? arg.substring(0, ph.precision) : arg; break; case "x": arg = (parseInt(arg, 10) >>> 0).toString(16); break; case "X": arg = (parseInt(arg, 10) >>> 0).toString(16).toUpperCase(); break; } if (re.json.test(ph.type)) { output += arg; } else { if (re.number.test(ph.type) && (!is_positive || ph.sign)) { sign = is_positive ? "+" : "-"; arg = arg.toString().replace(re.sign, ""); } else { sign = ""; } pad_character = ph.pad_char ? ph.pad_char === "0" ? "0" : ph.pad_char.charAt(1) : " "; pad_length = ph.width - (sign + arg).length; pad = ph.width ? pad_length > 0 ? pad_character.repeat(pad_length) : "" : ""; output += ph.align ? sign + arg + pad : pad_character === "0" ? sign + pad + arg : pad + sign + arg; } } } return output; } var sprintf_cache = /* @__PURE__ */ Object.create(null); function sprintf_parse(fmt) { if (sprintf_cache[fmt]) { return sprintf_cache[fmt]; } var _fmt = fmt, match, parse_tree = [], arg_names = 0; while (_fmt) { if ((match = re.text.exec(_fmt)) !== null) { parse_tree.push(match[0]); } else if ((match = re.modulo.exec(_fmt)) !== null) { parse_tree.push("%"); } else if ((match = re.placeholder.exec(_fmt)) !== null) { if (match[2]) { arg_names |= 1; var field_list = [], replacement_field = match[2], field_match = []; if ((field_match = re.key.exec(replacement_field)) !== null) { field_list.push(field_match[1]); while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") { if ((field_match = re.key_access.exec(replacement_field)) !== null) { field_list.push(field_match[1]); } else if ((field_match = re.index_access.exec(replacement_field)) !== null) { field_list.push(field_match[1]); } else { throw new SyntaxError("[sprintf] failed to parse named argument key"); } } } else { throw new SyntaxError("[sprintf] failed to parse named argument key"); } match[2] = field_list; } else { arg_names |= 2; } if (arg_names === 3) { throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported"); } parse_tree.push( { placeholder: match[0], param_no: match[1], keys: match[2], sign: match[3], pad_char: match[4], align: match[5], width: match[6], precision: match[7], type: match[8] } ); } else { throw new SyntaxError("[sprintf] unexpected placeholder"); } _fmt = _fmt.substring(match[0].length); } return sprintf_cache[fmt] = parse_tree; } { exports$1["sprintf"] = sprintf2; exports$1["vsprintf"] = vsprintf; } if (typeof window !== "undefined") { window["sprintf"] = sprintf2; window["vsprintf"] = vsprintf; } }(); })(sprintf); const Configurations = (props) => { const filerobotApiDomain = "https://api.filerobot.com"; const intl = useIntl(); const config = props.config; const { get, post, put } = useFetchClient(); const [cname, setCname] = useState(config.cname ? config.cname : ""); const [token, setToken] = useState(config.token ? config.token : ""); const [secTemp, setSecTemp] = useState(config.sec_temp ? config.sec_temp : ""); const [folder, setFolder] = useState(config.folder ? config.folder : ""); const [success, setSuccess] = useState(false); const [error, setError] = useState(false); const [syncMessage, setSyncMessage] = useState(false); const [disabledAllButtons, setDisabledAllButtons] = useState(false); const [up, setUp] = useState(0); const [down, setDown] = useState(0); const saveConfiguration = async () => { const config2 = { cname, token, sec_temp: secTemp, folder }; setDisabledAllButtons(true); await fetch(filerobotApiDomain + "/" + token + "/key/" + secTemp).then(async (response) => { if (response.status === 200) { await put(`/${PLUGIN_ID}/update-config`, config2).then((data) => { setSuccess(true); }); } else { setError(true); } setDisabledAllButtons(false); setTimeout(() => { setError(false); setSuccess(false); }, 4e3); }); }; const getNewSassKey = async (config2) => { let sassReqHeaders = new Headers(); sassReqHeaders.append("Content-Type", "application/json"); const sassReqOpt = { method: "GET", headers: sassReqHeaders }; const sassRes = await fetch(`${filerobotApiDomain}/${config2.token}/key/${config2.sec_temp}`, sassReqOpt); if (sassRes.status !== 200) { return false; } const sassInfo = await sassRes.json(); if (sassInfo.status !== "success") { return false; } const sass = sassInfo.key; if (typeof Storage !== "undefined") { sessionStorage.setItem("sassKey", sass); } return sass; }; const getSass = async (config2) => { let sass; sass = await getNewSassKey(config2); return sass; }; const getSyncStatus = async () => { const localMedia = await get(`/${PLUGIN_ID}/db-files`); const configsData = await get(`/${PLUGIN_ID}/config`); const configs = configsData.data; const sass = await getSass(configs); if (!sass) { setSyncMessage(intl.formatMessage({ id: `${PLUGIN_ID}.notification.error.check_sectmp_issue` })); return { localMedia: false, filerobotMedia: false }; } let headers = new Headers(); headers.append("Content-Type", "application/json"); headers.append("X-Filerobot-Key", sass); const requestOptions = { method: "GET", headers }; const filerobotDirectory = configs.folder.charAt(0) === "/" ? configs.folder : `/${configs.folder}`; const filerobotResponse = await fetch(`${filerobotApiDomain}/${configs.token}/v5/files?folder=${filerobotDirectory}`, requestOptions); if (filerobotResponse.status !== 200) { setSyncMessage(intl.formatMessage({ id: `${PLUGIN_ID}.notification.error.sync_status` })); return { localMedia: false, filerobotMedia: false }; } const filerobotResponseJson = await filerobotResponse.json(); const filerobotMedia = filerobotResponseJson.files; return { localMedia, filerobotMedia }; }; const syncStatus = async () => { setDisabledAllButtons(true); const { localMedia, filerobotMedia } = await getSyncStatus(); if (localMedia === false && filerobotMedia === false) { setSyncMessage(intl.formatMessage({ id: `${PLUGIN_ID}.notification.error.wrong_sectmp` })); } const toSyncUp = localMedia.data.nonFilerobot; const alreadyDown = localMedia.data.filerobot; const alreadyDownHashs = alreadyDown.map((x) => x["hash"]); const filerobotMediaHashs = filerobotMedia.map((x) => x["hash"]["sha1"]); const toSyncDown = filerobotMediaHashs.filter((x) => !alreadyDownHashs.includes(x)); setSyncMessage(sprintf.sprintf(intl.formatMessage({ id: `${PLUGIN_ID}.notification.success.sync_status` }), toSyncUp.length, toSyncDown.length)); setDisabledAllButtons(false); setTimeout(() => { setSyncMessage(false); }, 5e3); }; const triggerSync = async () => { setDisabledAllButtons(true); const { localMedia, filerobotMedia } = await getSyncStatus(); const toSyncUp = localMedia.data.nonFilerobot; const alreadyDown = localMedia.data.filerobot; const downResult = await syncDown(filerobotMedia, alreadyDown); const upResult = await syncUp(toSyncUp); setSyncMessage(sprintf.sprintf(intl.formatMessage({ id: `${PLUGIN_ID}.notification.success.sync_results` }), downResult, upResult)); setDisabledAllButtons(false); }; const syncDown = async (filerobotMedia, alreadyDown) => { const alreadyDownHashs = alreadyDown.map((x) => x["hash"]); const filerobotMediaHashs = filerobotMedia.map((x) => x["hash"]["sha1"]); const toSyncDown = filerobotMediaHashs.filter((x) => !alreadyDownHashs.includes(x)); let count = 0; await Promise.all(filerobotMedia.map(async (index) => { if (!alreadyDownHashs.includes(index.hash.sha1)) { const result = await post(`/${PLUGIN_ID}/record-file`, { file: index, action: "sync-down", config }); if (result) { count++; } const percentage = toSyncDown.length === 0 ? 100 : Math.ceil(count / toSyncDown.length * 100); setDown(percentage); } })); return `${count} / ${toSyncDown.length}`; }; const syncUp = async (toSyncUp) => { let count = 0; await Promise.all(toSyncUp.map(async (index) => { const result = await post(`/${PLUGIN_ID}/sync-up`, { file: index, config }); if (result) { count++; } const percentage = toSyncUp.length === 0 ? 100 : Math.ceil(count / toSyncUp.length * 100); setUp(percentage); })); return `${count} / ${toSyncUp.length}`; }; return /* @__PURE__ */ jsxs(Box, { spacing: 4, padding: 4, children: [ success && /* @__PURE__ */ jsx(Alert, { title: "Successfully", onClose: () => setSuccess(false), closeLabel: "Close alert", variant: "success", children: "Configuration updated" }), error && /* @__PURE__ */ jsx(Alert, { title: "Failed", onClose: () => setError(false), closeLabel: "Close alert", variant: "danger", children: "Please check your configuration setting" }), syncMessage && /* @__PURE__ */ jsx( Alert, { title: "Synchronization", onClose: () => setSyncMessage(false), closeLabel: "Close alert", variant: "default", children: syncMessage } ), /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsxs(Field.Root, { children: [ /* @__PURE__ */ jsx(Field.Label, { children: "CNAME" }), /* @__PURE__ */ jsx(Field.Input, { type: "text", placeholder: "CNAME", value: cname, onChange: (e) => { setCname(e.target.value); } }) ] }) }), /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsxs(Field.Root, { children: [ /* @__PURE__ */ jsx(Field.Label, { children: "Token" }), /* @__PURE__ */ jsx(Field.Input, { type: "text", placeholder: "Token", value: token, onChange: (e) => { setToken(e.target.value); } }) ] }) }), /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsxs(Field.Root, { children: [ /* @__PURE__ */ jsx(Field.Label, { children: "Security Template" }), /* @__PURE__ */ jsx(Field.Input, { type: "text", placeholder: "Security Template", value: secTemp, onChange: (e) => { setSecTemp(e.target.value); } }) ] }) }), /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsxs(Field.Root, { children: [ /* @__PURE__ */ jsx(Field.Label, { children: "Folder" }), /* @__PURE__ */ jsx(Field.Input, { type: "text", placeholder: "Folder", value: folder, onChange: (e) => { setFolder(e.target.value); } }) ] }) }), /* @__PURE__ */ jsx(Box, { padding: 2, textAlign: "center", children: /* @__PURE__ */ jsx(Button, { disabled: disabledAllButtons, onClick: () => saveConfiguration(), children: "Save configuration" }) }), /* @__PURE__ */ jsxs(Box, { spacing: 4, textAlign: "center", children: [ /* @__PURE__ */ jsx(Button, { disabled: disabledAllButtons, margin: 2, onClick: () => syncStatus(), variant: "secondary", children: "Synchronization Status" }), /* @__PURE__ */ jsx(Button, { disabled: disabledAllButtons, margin: 2, onClick: () => triggerSync(), variant: "secondary", children: "Trigger Synchronization" }) ] }), up !== 0 && /* @__PURE__ */ jsxs(Box, { spacing: 2, children: [ /* @__PURE__ */ jsx(Typography, { children: "Sync up" }), /* @__PURE__ */ jsx(ProgressBar, { value: up, children: syncMessage }) ] }), down !== 0 && /* @__PURE__ */ jsxs(Box, { spacing: 2, children: [ /* @__PURE__ */ jsx(Typography, { children: "Sync down" }), /* @__PURE__ */ jsx(ProgressBar, { value: down, children: syncMessage }) ] }) ] }); }; const FMAW = (props) => { window.process = { env: { REACT_APP_GITLAB_REVIEW_ENV: false } }; const { post } = useFetchClient(); const intl = useIntl(); const config = props.config; const filerobot = useRef(null); const [success, setSuccess] = useState(false); useEffect(() => { if (config.token === "" || config.sec_temp === "") { return; } filerobot.current = Filerobot({ securityTemplateID: config.sec_temp, container: config.token }).use(ProgressPanel, { target: "#sfx-widget-progress-panel" }).use(Explorer, { config: { rootFolderPath: config.folder }, target: "#scaleflex-dam-widget", inline: true, width: "100%", height: "700px", disableExportButton: true, hideExportButtonIcon: true, dismissUrlPathQueryUpdate: true, disableDownloadButton: false, preventExportDefaultBehavior: true, hideDownloadButtonIcon: true, preventDownloadDefaultBehavior: true, showProgressDetails: true, locale: { strings: { mutualizedExportButtonLabel: intl.formatMessage({ id: `${PLUGIN_ID}.label.button.fmaw.export` }), mutualizedDownloadButton: intl.formatMessage({ id: `${PLUGIN_ID}.label.button.fmaw.export` }) } } }).use(XHRUpload).on("export", async (files, popupExportSuccessMsgFn, downloadFilesPackagedFn, downloadFileFn) => { console.log(files); await recordMedia(files, "export", config); }).on("complete", async ({ failed, uploadID, successful }) => { if (successful) { await recordMedia(successful, "complete", config); } }); return () => { filerobot.current.close(); }; }, [config]); const recordMedia = async (files, action, config2) => { let isSuccess = false; for (const file of files) { const index = files.indexOf(file); await post(`/${PLUGIN_ID}/record-file`, { method: "POST", file, action, config: config2 }); isSuccess = files.length - 1 === index; } setSuccess(isSuccess); setTimeout(() => { setSuccess(false); }, 4e3); }; return /* @__PURE__ */ jsxs(Fragment, { children: [ success && /* @__PURE__ */ jsx(Box, { marginTop: 2, children: /* @__PURE__ */ jsx(Alert, { title: "Successfully", onClose: () => setSuccess(false), closeLabel: "Close alert", variant: "success", children: "Item(s) added to Strapi Media" }) }), /* @__PURE__ */ jsx(Box, { marginTop: 2, id: "scaleflex-dam-widget" }), /* @__PURE__ */ jsx(Box, { id: "sfx-widget-progress-panel" }), /* @__PURE__ */ jsx(Box, { paddingTop: 10 }) ] }); }; const Media = () => { useIntl(); const [media, setMedia] = useState([]); const [totalRecords, setTotalRecords] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [pageCount, setPageCount] = useState(0); const [loadingPage, setLoadingPage] = useState(true); const recordPerPage = 10; const { get } = useFetchClient(); const handlePageChange = async (pageNumber) => { setCurrentPage(pageNumber); let listMedia = await get(`/${PLUGIN_ID}/media?limit=${recordPerPage}&offset=${pageNumber - 1}`); setMedia(listMedia.data); }; useEffect(() => { const countMedia = async () => { try { const itemCounts = await get(`/${PLUGIN_ID}/media-count`); setTotalRecords(itemCounts.data); setPageCount(Math.ceil(itemCounts.data / recordPerPage)); } catch (err) { console.error(err); } }; countMedia(); const fetchMedia = async () => { try { const response = await get(`/${PLUGIN_ID}/media?limit=${recordPerPage}&offset=${currentPage - 1}`); setMedia(response.data); } catch (err) { console.error(err); } finally { setLoadingPage(false); } }; fetchMedia(); }, [get]); console.log(media); return /* @__PURE__ */ jsxs(Fragment, { children: [ loadingPage && /* @__PURE__ */ jsx(Box, { paddingTop: 10, children: "fetching data..." }), !loadingPage && /* @__PURE__ */ jsxs(Box, { padding: 4, children: [ /* @__PURE__ */ jsxs(Table, { colCount: 5, rowCount: recordPerPage, children: [ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "ID" }) }), /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Image" }) }), /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Name" }) }), /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Provider" }) }) ] }) }), /* @__PURE__ */ jsx(Tbody, { children: media.map((entry, index) => /* @__PURE__ */ jsxs(Tr, { children: [ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: entry.id }) }), /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx("img", { src: entry.url, alt: "", style: { width: "80px", borderRadius: "6px", height: "80px", objectFit: "cover" } }) }), /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: entry.name }) }), /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: entry.provider }) }) ] }, index)) }) ] }), /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [ currentPage > 1 && currentPage <= pageCount && /* @__PURE__ */ jsx(Button, { onClick: () => handlePageChange(currentPage - 1), variant: "default", children: "Prev Page" }), currentPage < pageCount && /* @__PURE__ */ jsxs(Fragment, { children: [ currentPage === 1 && /* @__PURE__ */ jsx(Box, {}), /* @__PURE__ */ jsx(Button, { onClick: () => handlePageChange(currentPage + 1), variant: "default", children: "Next Page" }) ] }) ] }) }) ] }) ] }); }; const HomePage = () => { const { get } = useFetchClient(); const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { const response = await get(`/${PLUGIN_ID}/config`); setConfig(response.data); } catch (err) { console.error(err); } finally { setLoading(false); } }; fetchData(); }, [get]); if (loading) return /* @__PURE__ */ jsx(Typography, { children: "fetching configuration..." }); return /* @__PURE__ */ jsxs(Main, { children: [ /* @__PURE__ */ jsx(Box, { paddingLeft: 8, paddingTop: 5, paddingRight: 8, children: /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Scaleflex DAM" }) }), /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsxs(Tabs.Root, { defaultValue: "configurations", children: [ /* @__PURE__ */ jsxs(Tabs.List, { "aria-label": "Scaleflex DAM Managemement", children: [ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "configurations", children: "Configurations" }), /* @__PURE__ */ jsx(Tabs.Trigger, { value: "asset_manager", children: "Asset Manager" }), /* @__PURE__ */ jsx(Tabs.Trigger, { value: "media", children: "Media" }) ] }), /* @__PURE__ */ jsx(Tabs.Content, { value: "configurations", children: /* @__PURE__ */ jsx(Box, { color: "neutral800", padding: 4, background: "neutral0", children: /* @__PURE__ */ jsx(Configurations, { config }) }) }), /* @__PURE__ */ jsx(Tabs.Content, { value: "asset_manager", children: /* @__PURE__ */ jsx(Box, { color: "neutral800", padding: 4, background: "neutral0", children: /* @__PURE__ */ jsx(FMAW, { config }) }) }), /* @__PURE__ */ jsx(Tabs.Content, { value: "media", children: /* @__PURE__ */ jsx(Box, { color: "neutral800", padding: 4, background: "neutral0", children: /* @__PURE__ */ jsx(Media, {}) }) }) ] }) }) ] }); }; const App = () => { return /* @__PURE__ */ jsxs(Routes, { children: [ /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }), /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) }) ] }); }; export { App };