portainer-backup
Version:
Utility for scripting or scheduling scheduled backups for Portainer
209 lines (176 loc) • 9.23 kB
JavaScript
/**
* -------------------------------------------------------------------
* ___ ___ ___ ___ ___
* / __| /_\ \ / /_\ / __| __|
* \__ \/ _ \ V / _ \ (_ | _|
* |___/_/_\_\_/_/_\_\___|___| ___ ___ ___
* / __|/ _ \| __|_ _\ \ / /_\ | _ \ __|
* \__ \ (_) | _| | | \ \/\/ / _ \| / _|
* |___/\___/|_| |_| \_/\_/_/ \_\_|_\___|
*
* -------------------------------------------------------------------
* COPYRIGHT SAVAGESOFTWARE,LLC, @ 2022, ALL RIGHTS RESERVED
* https://github.com/SavageSoftware/portainer-backup
* -------------------------------------------------------------------
*/
import { DateTime } from "luxon";
import sanitize from 'sanitize-filename';
/**
* This class is used to expose commonly used utility methods.
*/
export default class Util {
constructor() { /* nothing */ }
/**
* Used to determine if an unknown object value results in a TRUE|FALSE.
* @param {*} arg any boolean, number or string
* @returns true|false
*/
static evalBool(arg) {
// object
if(!arg) return false;
if(arg === undefined) return false;
if(arg === null) return false;
// bool
if(arg === false) return false;
if(arg === true) return true;
// int
if(arg === 0) return false;
if(arg === 1) return true;
// string
let argLower = arg.toString().toLowerCase();
if(argLower == "") return false;
if(argLower == "false") return false;
if(argLower == "no") return false;
if(argLower == "0") return false;
return true;
}
/**
* Wrap a filename string so that it can fix
* inside a fixed width container.
*
* @param {*} input file name (not path)
* @param {*} width maximum character width to wrap within
* @returns
*/
static wrapFilePath(input, width) {
let leadingSlash = input.startsWith('/');
width = parseInt(width) || 80;
let res = []
, cLine = ""
, words = input.split("/")
;
for (let i = 0; i < words.length; ++i) {
let cWord = words[i];
if ((cLine + cWord).length <= width) {
cLine += (cLine ? "/" : "") + cWord;
} else {
res.push(cLine);
cLine = "/" + cWord;
}
}
if (cLine) {
res.push(cLine);
}
return (leadingSlash ? "/" : "") + res.join("\n");
};
/**
* Process file or directory string for named or tokenized substitutions
* Sanitize the name for any invalid filesystem characters.
* @param {*} name
*/
static hasSubstitutions(name){
// find any substitution replacements in file|directory name string
const substitutions = name.match(/\{\{.*?\}\}/g);
if(substitutions) return true;
return false;
}
/**
* Process file or directory string for named or tokenized substitutions
* Sanitize the name for any invalid filesystem characters.
* @param {*} name
*/
static processSubstitutions(name){
// copy name to working results variable
let results = name;
// find any substitution replacements in file|directory name string
const substitutions = results.match(/\{\{.*?\}\}/g);
if(substitutions){
// get current date & time
let now = DateTime.now();
// iterate over substitutions
for (let substitution of substitutions) {
// remove "{{" and "}}" from substitution string
let token = substitution.slice(2, -2);
// handle "UTC_" prefix
if(token.startsWith("UTC_")){
now = now.toUTC();
token = token.slice(4);
}
// process the token string and replace value with either a known named format or tokenized replacement
let value = substitution;
switch(token){
// simple, commonly used formats
case "DATETIME": { value = now.toFormat("yyyy-MM-dd'T'HHmmss"); break; }
case "TIMESTAMP": { value = now.toFormat("yyyyMMdd'T'HHmmss.SSSZZZ"); break; }
case "DATE": { value = now.toFormat("yyyy-MM-dd"); break; }
case "TIME": { value = now.toFormat("HHmmss"); break; }
// ISO standard formats
case "ISO8601": { value = now.toISO(); break; }
case "ISO": { value = now.toISO(); break; }
case "ISO_BASIC": { value = now.toISO({format: 'basic'}); break; }
case "ISO_NO_OFFSET": { value = now.toISO({includeOffset: false}); break; }
case "ISO_DATE": { value = now.toISODate(); break; }
case "ISO_WEEKDATE": { value = now.toISOWeekDate(); break; }
case "ISO_TIME": { value = now.toISOTime(); break; }
// other standards-based formats
case "RFC2822": { value = now.toRFC2822(); break; }
case "HTTP": { value = now.toHTTP(); break; }
case "MILLIS": { value = now.toMillis(); break; }
case "SECONDS": { value = now.toSeconds(); break; }
case "UNIX": { value = now.toSeconds(); break; }
case "EPOCH": { value = now.toSeconds(); break; }
// locale based formats
case "LOCALE": { value = now.toLocaleString(); break; }
case "LOCALE_DATE": { value = now.dt.toLocaleString(DateTime.DATE_SHORT); break; }
case "LOCALE_TIME": { value = now.dt.toLocaleString(DateTime.TIME_24_SIMPLE); break; }
// named date preset formats
case "DATE_SHORT": { value = now.toLocaleString(DateTime.DATE_SHORT); break; }
case "DATE_MED": { value = now.toLocaleString(DateTime.DATE_MED); break; }
case "DATE_FULL": { value = now.toLocaleString(DateTime.DATE_FULL); break; }
case "DATE_HUGE": { value = now.toLocaleString(DateTime.DATE_HUGE); break; }
case "DATE_MED_WITH_WEEKDAY": { value = now.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY); break; }
// named time preset formats
case "TIME_SIMPLE": { value = now.toLocaleString(DateTime.TIME_SIMPLE); break; }
case "TIME_WITH_SECONDS": { value = now.toLocaleString(DateTime.TIME_WITH_SECONDS); break; }
case "TIME_WITH_SHORT_OFFSET": { value = now.toLocaleString(DateTime.TIME_WITH_SHORT_OFFSET); break; }
case "TIME_WITH_LONG_OFFSET": { value = now.toLocaleString(DateTime.TIME_WITH_LONG_OFFSET); break; }
case "TIME_24_SIMPLE": { value = now.toLocaleString(DateTime.TIME_24_SIMPLE); break; }
case "TIME_24_WITH_SECONDS": { value = now.toLocaleString(DateTime.TIME_24_WITH_SECONDS); break; }
case "TIME_24_WITH_SHORT_OFFSET": { value = now.toLocaleString(DateTime.TIME_24_WITH_SHORT_OFFSET); break; }
case "TIME_24_WITH_LONG_OFFSET": { value = now.toLocaleString(DateTime.TIME_24_WITH_LONG_OFFSET); break; }
// nam,ed date/time preset formats
case "DATETIME_SHORT": { value = now.toLocaleString(DateTime.DATETIME_SHORT); break; }
case "DATETIME_MED": { value = now.toLocaleString(DateTime.DATETIME_MED); break; }
case "DATETIME_FULL": { value = now.toLocaleString(DateTime.DATETIME_FULL); break; }
case "DATETIME_HUGE": { value = now.toLocaleString(DateTime.DATETIME_HUGE); break; }
case "DATETIME_SHORT_WITH_SECONDS": { value = now.toLocaleString(DateTime.DATETIME_SHORT_WITH_SECONDS); break; }
case "DATETIME_MED_WITH_SECONDS": { value = now.toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS); break; }
case "DATETIME_FULL_WITH_SECONDS": { value = now.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS); break; }
case "DATETIME_HUGE_WITH_SECONDS": { value = now.toLocaleString(DateTime.DATETIME_HUGE_WITH_SECONDS); break; }
// tokenized string
default: { value = now.toFormat(token); break; }
}
results = results.replace(substitution, value);
}
//console.log("UNSANITIZED :: ", results);
results = sanitize(results, { replacement: (offender)=>{
if(offender === '/') return "-";
if(offender === ':') return "_"; // "꞉"; // U+FF1A
return "";
}});
//console.log("SANITIZED :: ", results);
}
// return resulting substituted string
return results;
}
}