diff --git a/filemanager/package-lock.json b/filemanager/package-lock.json index 25f2cd9ab..021680fe3 100644 --- a/filemanager/package-lock.json +++ b/filemanager/package-lock.json @@ -11,7 +11,7 @@ "@fontsource/noto-sans": "^5.0.1", "combokeys": "^3.0.1", "filesize": "^10.0.7", - "pankow": "^0.2.2", + "pankow": "^0.2.3", "primeicons": "^6.0.1", "primevue": "^3.29.1", "superagent": "^8.0.9", @@ -858,9 +858,9 @@ } }, "node_modules/pankow": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/pankow/-/pankow-0.2.2.tgz", - "integrity": "sha512-8vD37iQ/4GiCbFVoK5bj9ISn3OQQIpFNu94hmMvVTzZtXBhG4fK3C8YSw53s2fWdL9zrkzl6esqGlJPkOXvsoQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/pankow/-/pankow-0.2.3.tgz", + "integrity": "sha512-g8kZMwAnwUdHCa9VFbPeZkZotXJ3aWssg8mqdfEP2tOY5qQmO1wfeg2RN/0DiqeLdDTsC3+1vX5nagN2qqiNFg==", "dependencies": { "filesize": "^10.0.7", "monaco-editor": "^0.38.0", diff --git a/filemanager/package.json b/filemanager/package.json index afdb23963..cf2b9a943 100644 --- a/filemanager/package.json +++ b/filemanager/package.json @@ -12,7 +12,7 @@ "@fontsource/noto-sans": "^5.0.1", "combokeys": "^3.0.1", "filesize": "^10.0.7", - "pankow": "^0.2.2", + "pankow": "^0.2.3", "primeicons": "^6.0.1", "primevue": "^3.29.1", "superagent": "^8.0.9", diff --git a/filemanager/src/constants.js b/filemanager/src/constants.js new file mode 100644 index 000000000..8334f56b6 --- /dev/null +++ b/filemanager/src/constants.js @@ -0,0 +1,104 @@ + +// keep in sync with box/src/apps.js +const ISTATES = { + PENDING_INSTALL: 'pending_install', + PENDING_CLONE: 'pending_clone', + PENDING_CONFIGURE: 'pending_configure', + PENDING_UNINSTALL: 'pending_uninstall', + PENDING_RESTORE: 'pending_restore', + PENDING_IMPORT: 'pending_import', + PENDING_UPDATE: 'pending_update', + PENDING_BACKUP: 'pending_backup', + PENDING_RECREATE_CONTAINER: 'pending_recreate_container', // env change or addon change + PENDING_LOCATION_CHANGE: 'pending_location_change', + PENDING_DATA_DIR_MIGRATION: 'pending_data_dir_migration', + PENDING_RESIZE: 'pending_resize', + PENDING_DEBUG: 'pending_debug', + PENDING_START: 'pending_start', + PENDING_STOP: 'pending_stop', + PENDING_RESTART: 'pending_restart', + ERROR: 'error', + INSTALLED: 'installed' +}; + +const HSTATES = { + HEALTHY: 'healthy', + UNHEALTHY: 'unhealthy', + ERROR: 'error', + DEAD: 'dead' +}; + +const RSTATES ={ + RUNNING: 'running', + STOPPED: 'stopped' +}; + +const ERROR = { + ACCESS_DENIED: 'Access Denied', + ALREADY_EXISTS: 'Already Exists', + BAD_FIELD: 'Bad Field', + COLLECTD_ERROR: 'Collectd Error', + CONFLICT: 'Conflict', + DATABASE_ERROR: 'Database Error', + DNS_ERROR: 'DNS Error', + DOCKER_ERROR: 'Docker Error', + EXTERNAL_ERROR: 'External Error', + FS_ERROR: 'FileSystem Error', + INTERNAL_ERROR: 'Internal Error', + LOGROTATE_ERROR: 'Logrotate Error', + NETWORK_ERROR: 'Network Error', + NOT_FOUND: 'Not found', + REVERSEPROXY_ERROR: 'ReverseProxy Error', + TASK_ERROR: 'Task Error', + UNKNOWN_ERROR: 'Unknown Error' // only used for portin, +}; + +const ROLES = { + OWNER: 'owner', + ADMIN: 'admin', + MAIL_MANAGER: 'mailmanager', + USER_MANAGER: 'usermanager', + USER: 'user' +}; + +// sync up with tasks.js +const TASK_TYPES = { + TASK_APP: 'app', + TASK_BACKUP: 'backup', + TASK_UPDATE: 'update', + TASK_CHECK_CERTS: 'checkCerts', + TASK_SETUP_DNS_AND_CERT: 'setupDnsAndCert', + TASK_CLEAN_BACKUPS: 'cleanBackups', + TASK_SYNC_EXTERNAL_LDAP: 'syncExternalLdap', + TASK_CHANGE_MAIL_LOCATION: 'changeMailLocation', + TASK_SYNC_DNS_RECORDS: 'syncDnsRecords', + TASK_UPDATE_DISK_USAGE: 'updateDiskUsage', +}; + +const APP_TYPES = { + APP: 'app', //default + LINK: 'link', + PROXIED: 'proxied' +}; + +// named exports +export { + APP_TYPES, + ERROR, + HSTATES, + ISTATES, + RSTATES, + ROLES, + TASK_TYPES +}; + +// default export +export default { + APP_TYPES, + ERROR, + HSTATES, + ISTATES, + RSTATES, + ROLES, + TASK_TYPES +}; diff --git a/filemanager/src/utils.js b/filemanager/src/utils.js deleted file mode 100644 index 03713ed27..000000000 --- a/filemanager/src/utils.js +++ /dev/null @@ -1,196 +0,0 @@ - -import { filesize } from 'filesize'; - -function prettyDate(value) { - var date = new Date(value), - diff = (((new Date()).getTime() - date.getTime()) / 1000), - day_diff = Math.floor(diff / 86400); - - if (isNaN(day_diff) || day_diff < 0) - return; - - return day_diff === 0 && ( - diff < 60 && 'just now' || - diff < 120 && '1 min ago' || - diff < 3600 && Math.floor( diff / 60 ) + ' min ago' || - diff < 7200 && '1 hour ago' || - diff < 86400 && Math.floor( diff / 3600 ) + ' hours ago') || - day_diff === 1 && 'Yesterday' || - day_diff < 7 && day_diff + ' days ago' || - day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago' || - day_diff < 365 && Math.round( day_diff / 30 ) + ' months ago' || - Math.round( day_diff / 365 ) + ' years ago'; -} - -function prettyLongDate(value) { - if (!value) return 'unkown'; - - var date = new Date(value); - return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; -} - -function prettyFileSize(value) { - if (typeof value !== 'number') return 'unkown'; - - return filesize(value); -} - -function sanitize(path) { - path = '/' + path; - return path.replace(/\/+/g, '/'); -} - -function encode(path) { - return path.split('/').map(encodeURIComponent).join('/'); -} - -function decode(path) { - return path.split('/').map(decodeURIComponent).join('/'); -} - -// TODO create share links instead of using access token -function getDirectLink(entry) { - if (entry.share) { - let link = window.location.origin + '/api/v1/shares/' + entry.share.id + '?type=raw&path=' + encodeURIComponent(entry.filePath); - return link; - } else { - return window.location.origin + '/api/v1/files?type=raw&path=' + encodeURIComponent(entry.filePath); - } -} - -// TODO the url might actually return a 412 in which case we have to keep reloading -function getPreviewUrl(entry) { - if (!entry.previewUrl) return ''; - return entry.previewUrl; -} - -function getShareLink(shareId) { - return window.location.origin + '/api/v1/shares/' + shareId + '?type=raw'; -} - -function download(entries, name) { - if (!entries.length) return; - - if (entries.length === 1) { - if (entries[0].share) window.location.href = '/api/v1/shares/' + entries[0].share.id + '?type=download&path=' + encodeURIComponent(entries[0].filePath); - else window.location.href = '/api/v1/files?type=download&path=' + encodeURIComponent(entries[0].filePath); - - return; - } - - const params = new URLSearchParams(); - - // be a bit smart about the archive name and folder tree - const folderPath = entries[0].filePath.slice(0, -entries[0].fileName.length); - const archiveName = name || folderPath.slice(folderPath.slice(0, -1).lastIndexOf('/')+1).slice(0, -1); - params.append('name', archiveName); - params.append('skipPath', folderPath); - - params.append('entries', JSON.stringify(entries.map(function (entry) { - return { - filePath: entry.filePath, - shareId: entry.share ? entry.share.id : undefined - }; - }))); - - window.location.href = '/api/v1/download?' + params.toString(); -} - -function getFileTypeGroup(entry) { - return entry.mimeType.split('/')[0]; -} - -// simple extension detection, does not work with double extension like .tar.gz -function getExtension(entry) { - if (entry.isFile) return entry.fileName.slice(entry.fileName.lastIndexOf('.') + 1); - return ''; -} - -function copyToClipboard(value) { - var elem = document.createElement('input'); - elem.value = value; - document.body.append(elem); - elem.select(); - document.execCommand('copy'); - elem.remove(); -} - -function clearSelection() { - if(document.selection && document.selection.empty) { - document.selection.empty(); - } else if(window.getSelection) { - var sel = window.getSelection(); - sel.removeAllRanges(); - } -} - -function urlSearchQuery() { - return decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {}); -} - -// those paths contain the internal type and path reference eg. shares/:shareId/folder/filename or files/folder/filename -function parseResourcePath(resourcePath) { - var result = { - type: '', - path: '', - shareId: '', - apiPath: '', - resourcePath: '' - }; - - if (resourcePath.indexOf('files/') === 0) { - result.type = 'files'; - result.path = resourcePath.slice('files'.length) || '/'; - result.apiPath = '/api/v1/files'; - result.resourcePath = result.type + result.path; - } else if (resourcePath.indexOf('shares/') === 0) { - result.type = 'shares'; - result.shareId = resourcePath.split('/')[1]; - result.path = resourcePath.slice((result.type + '/' + result.shareId).length) || '/'; - result.apiPath = '/api/v1/shares/' + result.shareId; - // without shareId we show the root (share listing) - result.resourcePath = result.type + '/' + (result.shareId ? (result.shareId + result.path) : ''); - } else { - console.error('Unknown resource path', resourcePath); - } - - return result; -} - -function getEntryIdentifier(entry) { - return (entry.share ? (entry.share.id + '/') : '') + entry.filePath; -} - -function entryListSort(list, prop, desc) { - var tmp = list.sort(function (a, b) { - var av = a[prop]; - var bv = b[prop]; - - if (typeof av === 'string') return (av.toUpperCase() < bv.toUpperCase()) ? -1 : 1; - else return (av < bv) ? -1 : 1; - }); - - if (desc) return tmp; - return tmp.reverse(); -} - -export { - getDirectLink, - getPreviewUrl, - getShareLink, - getFileTypeGroup, - prettyDate, - prettyLongDate, - prettyFileSize, - sanitize, - encode, - decode, - download, - getExtension, - copyToClipboard, - clearSelection, - urlSearchQuery, - parseResourcePath, - getEntryIdentifier, - entryListSort -}; diff --git a/filemanager/src/views/Home.vue b/filemanager/src/views/Home.vue index b535cf38e..3e08f6502 100644 --- a/filemanager/src/views/Home.vue +++ b/filemanager/src/views/Home.vue @@ -42,7 +42,8 @@