diff --git a/untangle-vue-ui/source/package.json b/untangle-vue-ui/source/package.json index cf7b9326eb..67e5ca136c 100644 --- a/untangle-vue-ui/source/package.json +++ b/untangle-vue-ui/source/package.json @@ -19,6 +19,7 @@ "highcharts": "^9.1.2", "lodash": "^4.17.21", "nanomatch": "^1.2.13", + "v-code-diff": "^1.13.1", "vee-validate": "^3.4.10", "vue": "2.7.16", "vue-grid-layout": "^2.3.12", @@ -28,7 +29,7 @@ "vuetify": "^2.7.2", "vuex": "^3.6.2", "vuex-persist": "^3.1.3", - "vuntangle": "git+ssh://git@github.com/untangle/vuntangle.git#ngfw-17.5-release" + "vuntangle": "untangle/vuntangle" }, "devDependencies": { "@sentry/webpack-plugin": "^1.15.1", diff --git a/untangle-vue-ui/source/src/App.vue b/untangle-vue-ui/source/src/App.vue index f4b8eb7352..2122626007 100644 --- a/untangle-vue-ui/source/src/App.vue +++ b/untangle-vue-ui/source/src/App.vue @@ -9,14 +9,16 @@ $route.name === 'setup' " /> + + diff --git a/untangle-vue-ui/source/src/components/Apps/AppsLayout.vue b/untangle-vue-ui/source/src/components/Apps/AppsLayout.vue new file mode 100644 index 0000000000..8760ed11a7 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/AppsLayout.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/AppsView.vue b/untangle-vue-ui/source/src/components/Apps/AppsView.vue new file mode 100644 index 0000000000..63102aefd2 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/AppsView.vue @@ -0,0 +1,392 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/ApplicationControlLite.vue b/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/ApplicationControlLite.vue new file mode 100644 index 0000000000..8ab4563ebb --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/ApplicationControlLite.vue @@ -0,0 +1,75 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/index.js b/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/index.js new file mode 100644 index 0000000000..37f59a1441 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/ApplicationControlLite/index.js @@ -0,0 +1 @@ +export { default } from './ApplicationControlLite.vue' diff --git a/untangle-vue-ui/source/src/components/Apps/apps/CaptivePortal/CaptivePortal.vue b/untangle-vue-ui/source/src/components/Apps/apps/CaptivePortal/CaptivePortal.vue new file mode 100644 index 0000000000..b695667749 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/CaptivePortal/CaptivePortal.vue @@ -0,0 +1,197 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/PhishBlocker.vue b/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/PhishBlocker.vue new file mode 100644 index 0000000000..9782d4b72d --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/PhishBlocker.vue @@ -0,0 +1,88 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/index.js b/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/index.js new file mode 100644 index 0000000000..8ea399316c --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/PhishBlocker/index.js @@ -0,0 +1 @@ +export { default } from './PhishBlocker.vue' diff --git a/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/SpamBlocker.vue b/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/SpamBlocker.vue new file mode 100644 index 0000000000..95f65fe717 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/SpamBlocker.vue @@ -0,0 +1,110 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/index.js b/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/index.js new file mode 100644 index 0000000000..724faf594e --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/SpamBlocker/index.js @@ -0,0 +1 @@ +export { default } from './SpamBlocker.vue' diff --git a/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/VirusBlocker.vue b/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/VirusBlocker.vue new file mode 100644 index 0000000000..0e7873f284 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/VirusBlocker.vue @@ -0,0 +1,100 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/index.js b/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/index.js new file mode 100644 index 0000000000..8388519d47 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/VirusBlocker/index.js @@ -0,0 +1 @@ +export { default } from './VirusBlocker.vue' diff --git a/untangle-vue-ui/source/src/components/Apps/apps/WebCache/WebCache.vue b/untangle-vue-ui/source/src/components/Apps/apps/WebCache/WebCache.vue new file mode 100644 index 0000000000..cd1e8c96b3 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/WebCache/WebCache.vue @@ -0,0 +1,95 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/WebFilter.vue b/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/WebFilter.vue new file mode 100644 index 0000000000..f0d013f6dc --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/WebFilter.vue @@ -0,0 +1,190 @@ + + + diff --git a/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/capabilities.js b/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/capabilities.js new file mode 100644 index 0000000000..c78d739fb2 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/WebFilter/capabilities.js @@ -0,0 +1,96 @@ +import { webFilterDefaultCapabilities } from 'vuntangle' + +export const ngfwCapabilities = { + ...webFilterDefaultCapabilities, + main: { + policyManagerAlert: { render: false }, + enableToggle: { render: false }, + appIcon: { render: true }, + powerStatus: { render: true }, + description: { render: true, key: 'app_web_filter_description' }, + }, + categories: { + listGroup: { + mode: 'direct', + actions: [ + { value: 'blocked', text: 'block', forceEnabledFor: [] }, + { value: 'flagged', text: 'flag', forceEnabledFor: [] }, + ], + }, + }, + searchTerms: { + ...webFilterDefaultCapabilities.searchTerms, + importExport: { + render: true, + exportId: 'web-filter-search-terms', + formats: [ + { text: 'comma_delimited', value: 'comma' }, + { text: 'newline_delimited', value: 'newline' }, + { text: 'json_array', value: 'json' }, + ], + defaultActions: [ + { key: 'blocked', label: 'block', default: true }, + { key: 'flagged', label: 'flag', default: true }, + ], + }, + defaultItem: { + javaClass: 'com.untangle.uvm.app.GenericRule', + string: '', + description: '', + blocked: true, + flagged: true, + }, + }, + blockSites: { + ...webFilterDefaultCapabilities.blockSites, + siteName: { key: 'string', label: 'enter_site_domain', validator: null }, + enabled: { render: false }, + blocked: { render: true }, + rejected: { render: false }, + flagged: { render: true, label: 'flag' }, + logged: { render: false }, + exact: { render: false }, + global: { render: true }, + importExport: { render: true, exportId: 'web-filter-block-sites' }, + defaultItem: { + javaClass: 'com.untangle.uvm.app.GenericRule', + string: '', + description: '', + blocked: true, + flagged: true, + isGlobal: false, + }, + }, + passSites: { + ...webFilterDefaultCapabilities.passSites, + siteName: { key: 'string', label: 'enter_site_domain', validator: null }, + flagged: { render: false }, + logged: { render: false }, + exact: { render: false }, + global: { render: true }, + importExport: { render: true, exportId: 'web-filter-pass-sites' }, + defaultItem: { + javaClass: 'com.untangle.uvm.app.GenericRule', + string: '', + description: '', + enabled: true, + isGlobal: false, + }, + }, + passClients: { + ...webFilterDefaultCapabilities.passClients, + defaultItem: { + javaClass: 'com.untangle.uvm.app.GenericRule', + string: '1.2.3.4', + description: '', + enabled: true, + }, + }, + siteLookup: { + ...webFilterDefaultCapabilities.siteLookup, + recategorize: { render: true }, + }, + advanced: { + ...webFilterDefaultCapabilities.advanced, + }, +} diff --git a/untangle-vue-ui/source/src/components/Apps/apps/appMixin.js b/untangle-vue-ui/source/src/components/Apps/apps/appMixin.js new file mode 100644 index 0000000000..fef2640d13 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/appMixin.js @@ -0,0 +1,321 @@ +import { mapGetters } from 'vuex' +import { getReportUrl, getReportIcon } from '@/util/reports' +import util from '@/util/util' +import { EVENT_ACTIONS } from '@/constants/actions' +import { sendEvent } from '@/utils/event' +import Util from '@/util/setupUtil' + +export default { + data() { + return { + appManager: null, // Cached app manager instance for RPC calls + toggling: false, // Local state to track if app is currently toggling (starting/stopping) + saveDisabled: false, // Flag to disable save button on settings error + sessionHistory: this.initializeSessionsData(), // Rolling 7-point history for the sessions chart + } + }, + + provide() { + return { + isReportsInstalled: this.isReportsInstalled, + isRestricted: this.isRestricted, + } + }, + + inject: ['embedded'], + + /** + * Common computed properties and methods related to app management, metrics, and reports integration. + * Can be overridden in individual app components as needed, but provides a shared baseline implementation for all apps + * to reduce code duplication and ensure consistent behavior across app components. + */ + computed: { + ...mapGetters('metrics', ['getFormattedMetrics', 'getLiveSessions', 'lastUpdateTime']), + ...mapGetters('apps', ['getAppPowerState']), + + /** + * App display name for reports filtering and UI labels. + * Falls back to defaultDisplayName data property defined in each component. + */ + appDisplayName: ({ appData, defaultDisplayName }) => + appData?.appProperties?.displayName || defaultDisplayName || '', + + // App Instance ID. + instanceId: ({ appData }) => appData?.instance?.id || null, + + /** + * Check if Reports app is installed + * @returns {Boolean} True if reports is installed, else false + * TODO Remove Util Method Call Once Reports App and its flows are Fully Migrated + */ + isReportsInstalled: ({ $store }) => $store.getters['reports/isReportsInstalled'] || !!util.isReportsInstalled(), + + /** + * Check if the license instance is restricted + * @returns {Boolean} True if restricted, else false + */ + isRestricted: () => !!util.isRestricted(), + + /** + * Power state of the app, derived from Vuex store with local toggling state overlay + * @returns {Object} Power state object with 'on' boolean and 'toggling' boolean + */ + powerState: ({ appData, appManager, getAppPowerState, toggling }) => { + const vuexPowerState = getAppPowerState({ + policyId: appData?.policyId, + appName: appData?.appName, + appManager, + }) + + return { + ...vuexPowerState, + power: toggling, + } + }, + + /** + * Get reports for this app from global store + * Filters by appDisplayName and transforms to UI format + * @returns {Array} - Formatted reports array + */ + appReports: ({ $store, appDisplayName }) => { + if (!appDisplayName) return [] + + const rawReports = $store.getters['reports/getReportsByCategory'](appDisplayName) + + return rawReports.map(report => ({ + key: report.uniqueId, + label: report.title || report.uniqueId, + url: getReportUrl(report.category, report.title), + icon: getReportIcon(report.type), + })) + }, + + /** + * Formatted metrics for this app instance from Vuex store + * @param {String} instanceId - App instance ID for metrics lookup + * @returns {Array} - Formatted metrics array or empty array if no instanceId + */ + formattedMetrics: ({ instanceId, getFormattedMetrics }) => (instanceId ? getFormattedMetrics(instanceId) : []), + + /** + * Sessions chart data — returns the rolling 7-point history maintained by the watcher. + * Falls back to the zero-initialized baseline when the app is off or instanceId is unavailable. + */ + sessionsData() { + if (!this.powerState?.on) return [] + if (!this.instanceId) return this.initializeSessionsData() + return this.sessionHistory + }, + + settings: ({ $store, appData }) => + $store.getters['apps/getSettings'](`${appData?.appName}-${appData?.instance?.id}`)?.settings || {}, + }, + + watch: { + /** + * Fires on every poll tick (lastUpdateTime advances even when sessions value is unchanged), + * ensuring the x-axis timestamp always moves forward. + */ + lastUpdateTime(timestamp) { + if (!this.powerState?.on || !this.instanceId) return + + const sessions = this.getLiveSessions(this.instanceId) + + if (this.sessionHistory.length === 0) { + const init = this.initializeSessionsData() + init[init.length - 1].sessions = sessions + this.sessionHistory = init + return + } + + this.sessionHistory = [...this.sessionHistory.slice(1), { timestamp, sessions }] + }, + + /** + * Clear history when the app is turned off so the chart resets on next start. + */ + 'powerState.on'(isOn) { + if (!isOn) this.sessionHistory = this.initializeSessionsData() + }, + }, + + async created() { + if (!this.appData?.instance?.id) return + this.appManager = await this.$store.dispatch('apps/getAppById', { + appId: this.appData.instance.id, + }) + this.loadAppSettings() + }, + + /** + * Common methods for app components - toggleAppState, removeApp, initializeSessionsData, loadAppSettings, saveSettings + * These methods can be overridden or extended in individual app components as needed, but provide a shared baseline implementation + * for all apps to reduce code duplication and ensure consistent behavior across app components. + */ + methods: { + /** + * Toggle app state. Starts or stops the app based on the 'enabled' parameter. + * @param {boolean} enabled - Target state (true = starting, false = stopping) + */ + async toggleAppState(enabled) { + if (!this.appManager) return + + this.toggling = true + try { + const rpcMethod = enabled ? 'start' : 'stop' + + await new Promise((resolve, reject) => { + this.appManager[rpcMethod]((ex, res) => { + if (ex || res?.code) { + reject(ex || new Error(res?.message || `Failed to ${rpcMethod} app`)) + } else { + resolve(res) + } + }) + }) + this.$store.dispatch('apps/getAppsViews', true) + this.refreshLicenseStatus() + if (this.embedded) { + // Send event to parent window to refresh app status. + // TODO Remove this once all apps are migrated to Vue UI and Parent Layout is changed + this.sendEventToParentWindow(EVENT_ACTIONS.REFRESH_APP_STATUS) + } + } catch (error) { + Util.handleException(error) + } finally { + this.toggling = false + } + }, + + /** + * Remove/Uninstall the app + */ + async removeApp() { + this.$store.commit('SET_LOADER', true) + try { + await this.$store.dispatch('apps/destroyApp', { + instanceId: this.appData?.instance?.id, + policyId: this.appData?.policyId, + }) + if (this.embedded) { + // If embedded, just send event to parent + // TODO Remove this once all apps are migrated to Vue UI and Parent Layout is changed + this.sendEventToParentWindow(EVENT_ACTIONS.REMOVE_APP) + } else { + this.$router.push({ name: 'apps', params: { policyId: this.appData?.policyId } }) + } + } catch (error) { + } finally { + this.$store.commit('SET_LOADER', false) + } + }, + + /** + * Send event to parent window + * @param {String} type - Event type from EVENT_ACTIONS + */ + sendEventToParentWindow(type) { + const eventData = { + appName: this.appData?.appName, + type, + } + + if (type === EVENT_ACTIONS.REFRESH_APP_STATUS) { + eventData.targetState = this.powerState.on ? 'RUNNING' : 'INITIALIZED' + } + + sendEvent(eventData) + }, + + /** + * Builds the base consolidated app data passed to each app component. + * Merges appData and powerState with any app-specific extras. + * @param {Object} extras - Additional app-specific properties to merge + * @returns {Object} Consolidated app data + */ + buildConsolidatedAppData(extras = {}) { + return { + ...this.appData, + powerState: this.powerState || {}, + ...extras, + } + }, + + /** + * Initialize sessions chart with 7 zero points + * Creates baseline data for chart display when no metrics available yet + * @returns {Array} Array of 7 data points with timestamps and zero sessions + */ + initializeSessionsData() { + const data = [] + const now = Date.now() + const roundedNow = Math.round(now / 1000) * 1000 + + for (let i = -6; i <= 0; i++) { + data.push({ + timestamp: roundedNow + i * 10000, + sessions: 0, + }) + } + + return data + }, + + /** + * Load app settings from backend via RPC call to app manager and store in Vuex + */ + loadAppSettings() { + this.$store.commit('SET_LOADER', true) + this.$store + .dispatch('apps/loadAppData', { + appName: this.appData?.appName, + appId: this.appData?.instance?.id, + app: this.appManager, + }) + .finally(() => { + this.$store.commit('SET_LOADER', false) + }) + }, + + /** + * Save app settings by sending updated settings to backend via RPC call to app manager, then refresh app data and license status + * @param {Object} newSettings - Settings object to save + */ + saveSettings(newSettings) { + this.$store.commit('SET_LOADER', true) + this.$store + .dispatch('apps/setAppSettings', { + appName: this.appData?.appName, + settings: newSettings, + appId: this.appData?.instance?.id, + app: this.appManager, + }) + .catch(error => { + this.saveDisabled = true + Util.handleException(error) + }) + .finally(() => { + this.$store.commit('SET_LOADER', false) + }) + }, + + /** + * Refresh app data - reload settings and app view data. + * Triggers computed properties (powerState and appReports) to update with latest data. + * Also refreshes license status in case app state change affects licensing. + */ + refreshData() { + this.refreshLicenseStatus() + this.loadAppSettings() + this.$store.dispatch('apps/getAppsView', this.appData?.policyId) + }, + + /** + * Emit event to refresh license status in parent component + */ + refreshLicenseStatus() { + this.$emit('refreshLicenseStatus') + }, + }, +} diff --git a/untangle-vue-ui/source/src/components/Apps/apps/conditionDataMixin.js b/untangle-vue-ui/source/src/components/Apps/apps/conditionDataMixin.js new file mode 100644 index 0000000000..c8272a7e2b --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/apps/conditionDataMixin.js @@ -0,0 +1,34 @@ +import { usernameOptions, directoryConnectorGroupOptions, directoryConnectorDomainOptions } from 'vuntangle' + +/** + * Factory mixin for app components that need runtime condition dropdown data. + * + * @param {string[]} appNames - Service names whose condition data to fetch (e.g. ['directory-connector']) + * + * Usage: + * import conditionDataMixin from '../conditionDataMixin' + * mixins: [appMixin, conditionDataMixin(['directory-connector'])] + * + * Then include the needed keys in provide() → $remoteData: + * $remoteData: () => ({ ..., directoryGroups: this.directoryGroups, directoryDomains: this.directoryDomains, directoryUsers: this.directoryUsers }) + */ +export default function conditionDataMixin(appNames = []) { + return { + computed: { + conditionData: ({ $store }) => $store.getters['apps/conditionData'], + directoryGroups: ({ conditionData }) => + conditionData?.directoryGroups?.length + ? [...directoryConnectorGroupOptions, ...conditionData.directoryGroups] + : [], + directoryDomains: ({ conditionData }) => + conditionData?.directoryDomains?.length + ? [...directoryConnectorDomainOptions, ...conditionData.directoryDomains] + : [], + directoryUsers: ({ conditionData }) => + conditionData?.directoryUsers?.length ? [...usernameOptions, ...conditionData.directoryUsers] : [], + }, + created() { + this.$store.dispatch('apps/fetchConditionData', appNames) + }, + } +} diff --git a/untangle-vue-ui/source/src/components/Apps/components/AppCard.vue b/untangle-vue-ui/source/src/components/Apps/components/AppCard.vue new file mode 100644 index 0000000000..15db30d628 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/components/AppCard.vue @@ -0,0 +1,318 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/components/AppsToolbar.vue b/untangle-vue-ui/source/src/components/Apps/components/AppsToolbar.vue new file mode 100644 index 0000000000..950d085f80 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/components/AppsToolbar.vue @@ -0,0 +1,330 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/components/PowerButton.vue b/untangle-vue-ui/source/src/components/Apps/components/PowerButton.vue new file mode 100644 index 0000000000..68faca1edc --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/components/PowerButton.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/containers/InstallableApps.vue b/untangle-vue-ui/source/src/components/Apps/containers/InstallableApps.vue new file mode 100644 index 0000000000..d3033d1c98 --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/containers/InstallableApps.vue @@ -0,0 +1,373 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/Apps/containers/InstalledApps.vue b/untangle-vue-ui/source/src/components/Apps/containers/InstalledApps.vue new file mode 100644 index 0000000000..61300bf59d --- /dev/null +++ b/untangle-vue-ui/source/src/components/Apps/containers/InstalledApps.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/untangle-vue-ui/source/src/components/reports/ReportDetails.vue b/untangle-vue-ui/source/src/components/reports/ReportDetails.vue new file mode 100644 index 0000000000..f50b3a80f7 --- /dev/null +++ b/untangle-vue-ui/source/src/components/reports/ReportDetails.vue @@ -0,0 +1,276 @@ + + + diff --git a/untangle-vue-ui/source/src/components/reports/Reports.vue b/untangle-vue-ui/source/src/components/reports/Reports.vue new file mode 100644 index 0000000000..f265a1ef73 --- /dev/null +++ b/untangle-vue-ui/source/src/components/reports/Reports.vue @@ -0,0 +1,60 @@ + + diff --git a/untangle-vue-ui/source/src/components/reports/SettingsDiffDialog.vue b/untangle-vue-ui/source/src/components/reports/SettingsDiffDialog.vue new file mode 100644 index 0000000000..efe14a3139 --- /dev/null +++ b/untangle-vue-ui/source/src/components/reports/SettingsDiffDialog.vue @@ -0,0 +1,156 @@ + + + diff --git a/untangle-vue-ui/source/src/components/reports/reportsMixin.js b/untangle-vue-ui/source/src/components/reports/reportsMixin.js new file mode 100644 index 0000000000..c57b98c8ac --- /dev/null +++ b/untangle-vue-ui/source/src/components/reports/reportsMixin.js @@ -0,0 +1,89 @@ +import { mapGetters } from 'vuex' +import { urlEncode, tableContainsColumns } from '@/util/reports' +import { globalOperatorOptions, globalConditionColumns, protocolNameMap, conditionValueOptions } from '@/constants' + +export default { + provide() { + return { + $conditions: () => this.globalConditions, + $conditionOperators: () => this.globalConditionOperators, + $conditionColumns: () => this.globalConditionColumns, + $conditionFormatValue: () => this.conditionFormatValue, + $conditionValueOptions: () => conditionValueOptions, + $disabledReportIds: () => this.disabledReportIds, + } + }, + + computed: { + ...mapGetters('reports', ['allReports', 'globalConditions']), + + globalConditionOperators() { + return globalOperatorOptions.map(op => ({ value: op.value, text: this.$t(op.text) })) + }, + + globalConditionColumns() { + return globalConditionColumns.map(col => ({ value: col.value, text: this.$t(col.text) })) + }, + + /** Unique column names currently used across all active global conditions. */ + conditionColumns() { + return [...new Set(this.globalConditions.map(c => c.column))] + }, + + /** + * Report uniqueIds that should be disabled because their database table + * does not contain one or more columns required by the active global conditions. + */ + disabledReportIds() { + if (!this.conditionColumns.length) return [] + return this.allReports + .filter(r => r.table && !tableContainsColumns(r.table, this.conditionColumns)) + .map(r => r.uniqueId) + }, + + /** + * Returns a formatter function that resolves raw column values to display names. + * Currently handles protocol numbers → "TCP [6]" style names. + */ + conditionFormatValue() { + return (column, value) => { + if (column === 'protocol') return protocolNameMap[value] || value + return value + } + }, + }, + + methods: { + /** + * Navigates to the report details page for the given uniqueId. + * Resolves the report from the store, then builds cat/rep route params. + * @param {String} uniqueId - the report's uniqueId from the backend + */ + onViewReport(uniqueId) { + const report = this.allReports.find(r => r.uniqueId === uniqueId) + if (!report) return + this.$router.push({ + name: 'report-details', + params: { + cat: urlEncode(report.category), + rep: urlEncode(report.title), + }, + }) + }, + + /** Adds a new global condition to the Vuex store. */ + onAddCondition(condition) { + this.$store.commit('reports/ADD_GLOBAL_CONDITION', condition) + }, + + /** Removes a global condition by index from the Vuex store. */ + onRemoveCondition(index) { + this.$store.commit('reports/REMOVE_GLOBAL_CONDITION', index) + }, + + /** Clears all active global conditions from the Vuex store. */ + onClearConditions() { + this.$store.commit('reports/CLEAR_GLOBAL_CONDITIONS') + }, + }, +} diff --git a/untangle-vue-ui/source/src/components/settings/administration/Admin.vue b/untangle-vue-ui/source/src/components/settings/administration/Admin.vue deleted file mode 100644 index 0f5b88d0d2..0000000000 --- a/untangle-vue-ui/source/src/components/settings/administration/Admin.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - diff --git a/untangle-vue-ui/source/src/components/settings/advanced/Advanced.vue b/untangle-vue-ui/source/src/components/settings/advanced/Advanced.vue index 6f353e79a4..5cdfd9d36b 100644 --- a/untangle-vue-ui/source/src/components/settings/advanced/Advanced.vue +++ b/untangle-vue-ui/source/src/components/settings/advanced/Advanced.vue @@ -38,12 +38,12 @@ computed: { /* network settings from the store */ - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], /* Gets the expert mode status from the settings store */ - isExpertMode: ({ $store }) => $store.getters['settings/isExpertMode'], + isExpertMode: ({ $store }) => $store.getters['config/isExpertMode'], /* Get list of interfaces from the settings store */ - interfaces: ({ $store }) => $store.getters['settings/interfaces'], + interfaces: ({ $store }) => $store.getters['config/interfaces'], /** * returns the interfaces for condition value from network settings * @param {Object} vm.networkSettings @@ -64,7 +64,7 @@ * @param {boolean} refetch - Whether to force a re-fetch of the settings. */ async fetchSettings(refetch) { - await this.$store.dispatch('settings/getNetworkSettings', refetch) + await this.$store.dispatch('config/getNetworkSettings', refetch) }, /** @@ -77,7 +77,7 @@ if (!isValid) return this.$store.commit('SET_LOADER', true) - await Promise.all([this.$store.dispatch('settings/setNetworkSettingV2', advancedSettings)]).finally(() => { + await Promise.all([this.$store.dispatch('config/setNetworkSettingV2', advancedSettings)]).finally(() => { this.$store.commit('SET_LOADER', false) }) }, diff --git a/untangle-vue-ui/source/src/components/settings/firewall/DenialOfService.vue b/untangle-vue-ui/source/src/components/settings/firewall/DenialOfService.vue index 0a74241c51..c425b8300c 100644 --- a/untangle-vue-ui/source/src/components/settings/firewall/DenialOfService.vue +++ b/untangle-vue-ui/source/src/components/settings/firewall/DenialOfService.vue @@ -39,13 +39,13 @@ computed: { // the shield settings from store - shieldSettings: ({ $store }) => $store.getters['settings/shieldSettings'], + shieldSettings: ({ $store }) => $store.getters['config/shieldSettings'], // the network settings from the store - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], /** - * eturns the interfaces for condition value from network settings + * returns the interfaces for condition value from network settings * @param {Object} vm.networkSettings */ interfaces: ({ networkSettings }) => { @@ -68,7 +68,7 @@ this.$store.commit('SET_LOADER', true) await store - .dispatch('settings/setShieldSettings', newSettings) + .dispatch('config/setShieldSettings', newSettings) .finally(() => this.$store.commit('SET_LOADER', false)) }, @@ -88,8 +88,8 @@ this.$store.commit('SET_LOADER', true) try { await Promise.all([ - this.$store.dispatch('settings/getShieldSettings', shieldRefetch), - this.$store.dispatch('settings/getNetworkSettings', networkRefetch), + this.$store.dispatch('config/getShieldSettings', shieldRefetch), + this.$store.dispatch('config/getNetworkSettings', networkRefetch), ]) } finally { this.$store.commit('SET_LOADER', false) diff --git a/untangle-vue-ui/source/src/components/settings/interface/DeleteInterfaceDialog.vue b/untangle-vue-ui/source/src/components/settings/interface/DeleteInterfaceDialog.vue index 6031d548fb..ae92efe149 100644 --- a/untangle-vue-ui/source/src/components/settings/interface/DeleteInterfaceDialog.vue +++ b/untangle-vue-ui/source/src/components/settings/interface/DeleteInterfaceDialog.vue @@ -29,7 +29,7 @@ *deleteInterface will handle for remove selected interface from all the interfaces * Updated Interfaces will be passed to the Network Setting */ - const response = await this.$store.dispatch('settings/deleteInterface', this.intf) + const response = await this.$store.dispatch('config/deleteInterface', this.intf) if (response) { this.$vuntangle.toast.add(this.$t('deleted_successfully', [this.$t('interface')])) // notify consumer of success diff --git a/untangle-vue-ui/source/src/components/settings/interface/Ipv4Aliases.vue b/untangle-vue-ui/source/src/components/settings/interface/Ipv4Aliases.vue index 792ce8c7ed..9f2e47c1d0 100644 --- a/untangle-vue-ui/source/src/components/settings/interface/Ipv4Aliases.vue +++ b/untangle-vue-ui/source/src/components/settings/interface/Ipv4Aliases.vue @@ -90,7 +90,7 @@ */ validateUniqueIpAddress(value) { // check aliases from other network interfaces - for (const networkInterface of this.$store.getters['settings/interfaces']) { + for (const networkInterface of this.$store.getters['config/interfaces']) { // ignore current interface, those will be checked from the current editable data if (networkInterface.interfaceId === this.interfaceId) { continue diff --git a/untangle-vue-ui/source/src/components/settings/network/Dhcp.vue b/untangle-vue-ui/source/src/components/settings/network/Dhcp.vue index 6a135c714d..f52f26106c 100644 --- a/untangle-vue-ui/source/src/components/settings/network/Dhcp.vue +++ b/untangle-vue-ui/source/src/components/settings/network/Dhcp.vue @@ -25,7 +25,7 @@ }, computed: { - dhcpSettings: ({ $store }) => $store.getters['settings/networkSetting'], + dhcpSettings: ({ $store }) => $store.getters['config/networkSetting'], }, created() { @@ -40,7 +40,7 @@ * @param {boolean} refetch - Whether to force a re-fetch of the settings. */ async fetchSettings(refetch) { - await this.$store.dispatch('settings/getNetworkSettings', refetch) + await this.$store.dispatch('config/getNetworkSettings', refetch) }, /** @@ -92,7 +92,7 @@ const isValid = await validate() if (!isValid) return this.$store.commit('SET_LOADER', true) - await Promise.all([this.$store.dispatch('settings/setNetworkSettingV2', newDhcpSettings)]).finally(() => { + await Promise.all([this.$store.dispatch('config/setNetworkSettingV2', newDhcpSettings)]).finally(() => { this.$store.commit('SET_LOADER', false) }) }, diff --git a/untangle-vue-ui/source/src/components/settings/network/Dns.vue b/untangle-vue-ui/source/src/components/settings/network/Dns.vue index 5a5104c6d9..9763ac9ac2 100644 --- a/untangle-vue-ui/source/src/components/settings/network/Dns.vue +++ b/untangle-vue-ui/source/src/components/settings/network/Dns.vue @@ -28,9 +28,9 @@ * @property {Array} dnsSettings.localServers - List of local DNS servers. * @property {Array} dnsSettings.staticEntries - List of static DNS entries. */ - dnsSettings: ({ $store }) => $store.getters['settings/dnsSettings'], + dnsSettings: ({ $store }) => $store.getters['config/dnsSettings'], // the network settings from the store - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], }, created() { @@ -43,14 +43,14 @@ * @param {boolean} refetch - Whether to force a re-fetch of the settings. */ async fetchSettings(refetch) { - await this.$store.dispatch('settings/getNetworkSettings', refetch) + await this.$store.dispatch('config/getNetworkSettings', refetch) }, async onSaveSettings(newDnsSettings) { this.$store.commit('SET_LOADER', true) const networkSettingsCopy = cloneDeep(this.networkSettings) networkSettingsCopy.dnsSettings = newDnsSettings - await Promise.all([this.$store.dispatch('settings/setNetworkSettingV2', networkSettingsCopy)]).finally(() => { + await Promise.all([this.$store.dispatch('config/setNetworkSettingV2', networkSettingsCopy)]).finally(() => { this.$store.commit('SET_LOADER', false) }) }, diff --git a/untangle-vue-ui/source/src/components/settings/network/Interface.vue b/untangle-vue-ui/source/src/components/settings/network/Interface.vue index 1791f8d3f3..31c6559eae 100644 --- a/untangle-vue-ui/source/src/components/settings/network/Interface.vue +++ b/untangle-vue-ui/source/src/components/settings/network/Interface.vue @@ -39,12 +39,12 @@ computed: { // interfaces filered and grouped (by category) interfaces({ $store }) { - return $store.getters['settings/networkSetting'].interfaces || [] + return $store.getters['config/networkSetting'].interfaces || [] }, }, created() { - this.$store.dispatch('settings/getNetworkSettings') + this.$store.dispatch('config/getNetworkSettings') }, mounted() { @@ -132,7 +132,7 @@ async setRemappedInterfaces(interfaces) { this.$store.commit('SET_LOADER', true) const response = await this.$store - .dispatch('settings/setInterfaces', interfaces) + .dispatch('config/setInterfaces', interfaces) .finally(() => this.$store.commit('SET_LOADER', false)) if (response?.success) { this.$vuntangle.toast.add(this.$t('interfaces_remapped_successfully')) @@ -144,7 +144,7 @@ async onRefresh() { this.$store.commit('SET_LOADER', true) - await this.$store.dispatch('settings/getInterfaces').finally(() => this.$store.commit('SET_LOADER', false)) + await this.$store.dispatch('config/getInterfaces').finally(() => this.$store.commit('SET_LOADER', false)) this.getInterfacesStatus() }, @@ -197,7 +197,7 @@ * Fetches updated network settings and updates the store. */ onBrowserRefresh() { - this.$store.dispatch('settings/getNetworkSettings', true) + this.$store.dispatch('config/getNetworkSettings', true) }, }, } diff --git a/untangle-vue-ui/source/src/components/settings/network/InterfaceEdit.vue b/untangle-vue-ui/source/src/components/settings/network/InterfaceEdit.vue index 96a96dfef3..e6c5c2a6a2 100644 --- a/untangle-vue-ui/source/src/components/settings/network/InterfaceEdit.vue +++ b/untangle-vue-ui/source/src/components/settings/network/InterfaceEdit.vue @@ -48,7 +48,7 @@ computed: { device: ({ $route }) => $route.params.device, type: ({ $route }) => $route.params.type, - interfaces: ({ $store }) => $store.getters['settings/interfaces'], + interfaces: ({ $store }) => $store.getters['config/interfaces'], intfSetting: ({ interfaces, device }) => interfaces.find(intf => intf.device === device), }, async mounted() { @@ -60,17 +60,21 @@ methods: { // check if network is available async isNetworkAvailable(network, cb) { - const result = await new Promise((resolve, reject) => { - window.rpc.UvmContext.netspaceManager().isNetworkAvailable( - (res, err) => (err ? reject(err) : resolve(res)), - 'networking', - network, - ) - }) - if (cb) { - cb(result) + try { + const result = await new Promise((resolve, reject) => { + window.rpc.UvmContext.netspaceManager().isNetworkAvailable( + (res, err) => (err ? reject(err) : resolve(res)), + 'networking', + network, + ) + }) + if (cb) { + cb(result) + } + return (this.status = result) + } catch (ex) { + Util.handleException(ex) } - return (this.status = result) }, // get the interface status @@ -100,37 +104,57 @@ /** returns box Wi-Fi channels */ async onGetWifiChannels(countryCode, cb) { - if (countryCode === '') { - countryCode = await window.rpc.networkManager.getWirelessRegulatoryCountryCode(this.intfSetting?.systemDev) + try { + if (countryCode === '') { + countryCode = await window.rpc.networkManager.getWirelessRegulatoryCountryCode(this.intfSetting?.systemDev) + } + const response = (await window.rpc.networkManager.getWirelessChannels( + this.intfSetting?.systemDev, + countryCode, + )) || [{ frequency: this.$t('no_channel_match'), channel: -1 }] + cb(response ?? null) + } catch (ex) { + Util.handleException(ex) } - const response = (await window.rpc.networkManager.getWirelessChannels( - this.intfSetting?.systemDev, - countryCode, - )) || [{ frequency: this.$t('no_channel_match'), channel: -1 }] - cb(response ?? null) }, /** returns country codes */ async onGetCountryItems(systemDev, cb) { - const response = await window.rpc.networkManager.getWirelessValidRegulatoryCountryCodes(systemDev) - cb(response ?? null) + try { + const response = await window.rpc.networkManager.getWirelessValidRegulatoryCountryCodes(systemDev) + cb(response ?? null) + } catch (ex) { + Util.handleException(ex) + } }, /** returns wireless channels */ async onGetWirelessChannels(systemDev, newValue, cb) { - const response = await window.rpc.networkManager.getWirelessChannels(systemDev, newValue) - cb(response ?? null) + try { + const response = await window.rpc.networkManager.getWirelessChannels(systemDev, newValue) + cb(response ?? null) + } catch (ex) { + Util.handleException(ex) + } }, /** returns wireless regulatory compliant */ async onWirelessRegulatoryCompliant(systemDev, cb) { - const response = await window.rpc.networkManager.isWirelessRegulatoryCompliant(systemDev) - cb(response ?? null) + try { + const response = await window.rpc.networkManager.isWirelessRegulatoryCompliant(systemDev) + cb(response ?? null) + } catch (ex) { + Util.handleException(ex) + } }, /** fetches and returns whether the given interface is the VRRP master */ async getVrrpMaster(interfaceId, cb) { - const response = await window.rpc.networkManager.isVrrpMaster(interfaceId) - cb(response ?? null) + try { + const response = await window.rpc.networkManager.isVrrpMaster(interfaceId) + cb(response ?? null) + } catch (ex) { + Util.handleException(ex) + } }, async onSave(newSettings, validate) { @@ -141,7 +165,7 @@ this.$store.commit('SET_LOADER', true) this.isSaving = true // Save interface settings by updating the current/new interface- newSettings - const resultIntf = await this.$store.dispatch('settings/setInterfaces', [newSettings]) + const resultIntf = await this.$store.dispatch('config/setInterfaces', [newSettings]) if (resultIntf?.success) { this.$vuntangle.toast.add(this.$t('network_settings_saved_successfully')) } else { diff --git a/untangle-vue-ui/source/src/components/settings/network/SettingsInterface/index.vue b/untangle-vue-ui/source/src/components/settings/network/SettingsInterface/index.vue index a4f5b27413..b34aad41a0 100644 --- a/untangle-vue-ui/source/src/components/settings/network/SettingsInterface/index.vue +++ b/untangle-vue-ui/source/src/components/settings/network/SettingsInterface/index.vue @@ -60,7 +60,7 @@ }, computed: { device: ({ $route }) => $route.params.device, - // interfaceStatuses: ({ $store }) => $store.getters['settings/interfaceStatuses'], + // interfaceStatuses: ({ $store }) => $store.getters['config/interfaceStatuses'], title() { if (!this.device && this.type === 'VLAN') { return this.$t('add_x_interface', [this.$t('vlan')]) diff --git a/untangle-vue-ui/source/src/components/settings/network/Troubleshooting.vue b/untangle-vue-ui/source/src/components/settings/network/Troubleshooting.vue index d25e37d2ef..267f3304e8 100644 --- a/untangle-vue-ui/source/src/components/settings/network/Troubleshooting.vue +++ b/untangle-vue-ui/source/src/components/settings/network/Troubleshooting.vue @@ -23,10 +23,10 @@ computed: { // uri settings from store - uriSettings: ({ $store }) => $store.getters['settings/uriSettings'], + uriSettings: ({ $store }) => $store.getters['config/uriSettings'], // network settings from the store - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], // dns test host from uri settings for Connectivity test dnsTestHost: ({ uriSettings }) => uriSettings?.dnsTestHost, @@ -46,8 +46,8 @@ }, created() { - this.$store.dispatch('settings/getUriSettings', false) - this.$store.dispatch('settings/getNetworkSettings', false) + this.$store.dispatch('config/getUriSettings', false) + this.$store.dispatch('config/getNetworkSettings', false) }, methods: { @@ -144,7 +144,7 @@ * Optional hook triggered on browser refresh. refetches the settings. */ onBrowserRefresh() { - this.$store.dispatch('settings/getNetworkSettings', true) + this.$store.dispatch('config/getNetworkSettings', true) }, }, } diff --git a/untangle-vue-ui/source/src/components/settings/network/interfaceMixin.js b/untangle-vue-ui/source/src/components/settings/network/interfaceMixin.js index fa65e35a40..4483ddd35b 100644 --- a/untangle-vue-ui/source/src/components/settings/network/interfaceMixin.js +++ b/untangle-vue-ui/source/src/components/settings/network/interfaceMixin.js @@ -16,7 +16,7 @@ export default { // Still added this check to prevent accidental deletion if (intf.type === 'NIC' || intf.type === 'WIFI') return - const interfacesCopy = cloneDeep(this.$store.getters['settings/interfaces']) + const interfacesCopy = cloneDeep(this.$store.getters['config/interfaces']) const affectedInterfaces = this.getAffectedBridgedInterfaces(intf, interfacesCopy) if (affectedInterfaces.length) { this.$vuntangle.dialog.show({ diff --git a/untangle-vue-ui/source/src/components/settings/routing/DynamicRoutes.vue b/untangle-vue-ui/source/src/components/settings/routing/DynamicRoutes.vue index 037da9b069..2a71ba15e8 100644 --- a/untangle-vue-ui/source/src/components/settings/routing/DynamicRoutes.vue +++ b/untangle-vue-ui/source/src/components/settings/routing/DynamicRoutes.vue @@ -34,8 +34,8 @@ }, computed: { - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], - dynamicRoutingSettings: ({ $store }) => $store.getters['settings/dynamicRoutingSettings'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], + dynamicRoutingSettings: ({ $store }) => $store.getters['config/dynamicRoutingSettings'], }, created() { @@ -45,7 +45,7 @@ methods: { async fetchNetworkSettings(refetch) { - await this.$store.dispatch('settings/getNetworkSettings', refetch) + await this.$store.dispatch('config/getNetworkSettings', refetch) }, /** fetch Dynamic Routing Status */ @@ -238,7 +238,7 @@ try { const networkdynamicRoutingSettings = cloneDeep(this.networkSettings) networkdynamicRoutingSettings.dynamicRoutingSettings = updatedSettings - await this.$store.dispatch('settings/setNetworkSettingV2', networkdynamicRoutingSettings) + await this.$store.dispatch('config/setNetworkSettingV2', networkdynamicRoutingSettings) await this.getDynamicRoutingOverview() } catch (ex) { Util.handleException(ex) diff --git a/untangle-vue-ui/source/src/components/settings/routing/StaticRoutes.vue b/untangle-vue-ui/source/src/components/settings/routing/StaticRoutes.vue index f5a2e0e7a2..3d4d9d3239 100644 --- a/untangle-vue-ui/source/src/components/settings/routing/StaticRoutes.vue +++ b/untangle-vue-ui/source/src/components/settings/routing/StaticRoutes.vue @@ -36,11 +36,11 @@ computed: { // Routes is coming from the networkSettings from store - routes: ({ $store }) => $store.getters['settings/staticRoutes'], + routes: ({ $store }) => $store.getters['config/staticRoutes'], // Interfaces from store which is need to be shown in the Next Hop - interfaces: ({ $store }) => $store.getters['settings/interfaces'], + interfaces: ({ $store }) => $store.getters['config/interfaces'], // the network settings from the store - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], }, created() { @@ -72,7 +72,7 @@ * @param {boolean} refetch - Whether to force a re-fetch of the settings. */ async fetchSettings(refetch) { - await this.$store.dispatch('settings/getNetworkSettings', refetch) + await this.$store.dispatch('config/getNetworkSettings', refetch) }, onRefresh() { @@ -97,7 +97,7 @@ }) const networkSettingsCopy = cloneDeep(this.networkSettings) networkSettingsCopy.staticRoutes = routes - await Promise.all([this.$store.dispatch('settings/setNetworkSettingV2', networkSettingsCopy)]).finally(() => { + await Promise.all([this.$store.dispatch('config/setNetworkSettingV2', networkSettingsCopy)]).finally(() => { this.$store.commit('SET_LOADER', false) }) }, diff --git a/untangle-vue-ui/source/src/components/settings/rules/EventRulesList.vue b/untangle-vue-ui/source/src/components/settings/rules/EventRulesList.vue new file mode 100644 index 0000000000..e5516d5c99 --- /dev/null +++ b/untangle-vue-ui/source/src/components/settings/rules/EventRulesList.vue @@ -0,0 +1,289 @@ + + diff --git a/untangle-vue-ui/source/src/components/settings/rules/RulesList.vue b/untangle-vue-ui/source/src/components/settings/rules/RulesList.vue index ad7021415a..01aba2b048 100644 --- a/untangle-vue-ui/source/src/components/settings/rules/RulesList.vue +++ b/untangle-vue-ui/source/src/components/settings/rules/RulesList.vue @@ -109,10 +109,10 @@ }, // the network settings from the store - networkSettings: ({ $store }) => $store.getters['settings/networkSetting'], + networkSettings: ({ $store }) => $store.getters['config/networkSetting'], // the system settings from the store - systemSettings: ({ $store }) => $store.getters['settings/systemSetting'], + systemSettings: ({ $store }) => $store.getters['config/systemSetting'], // rule configuration names associated with a given rule type ruleConfigs: ({ rulesMap, ruleType }) => rulesMap[ruleType], @@ -198,10 +198,10 @@ this.ruleConfigs.map(async confName => { // For Network Settings Rules Refresh Network Settings if (this.networkRules.includes(confName)) { - await store.dispatch('settings/getNetworkSettings', refetch) + await store.dispatch('config/getNetworkSettings', refetch) } if (confName === 'port-forward-rules') { - await store.dispatch('settings/getSystemSettings', false) + await store.dispatch('config/getSystemSettings', false) } }), ).finally(() => { @@ -233,7 +233,7 @@ networkSettingsCopy[key.replace(/-/g, '_')] = rules }) - await store.dispatch('settings/setNetworkSettingV2', networkSettingsCopy) + await store.dispatch('config/setNetworkSettingV2', networkSettingsCopy) } }), ).finally(() => { @@ -370,8 +370,8 @@ const accessRules = updatedRules['access-rules'] const isBlockAllEnabled = util.getRuleEnabledStatus(accessRules, 'Block All') const isSshEnabled = util.getRuleEnabledStatus(accessRules, 'Allow SSH') - const prevSshEnabled = store.getters['settings/accessRuleSshEnabled'] - const prevAccessRules = store.getters['settings/networkSetting']?.access_rules || [] + const prevSshEnabled = store.getters['config/accessRuleSshEnabled'] + const prevAccessRules = store.getters['config/networkSetting']?.access_rules || [] // Case 1: Block All rule cannot be disabled if (!isBlockAllEnabled) { diff --git a/untangle-vue-ui/source/src/components/settings/services/BrandingManager.vue b/untangle-vue-ui/source/src/components/settings/services/BrandingManager.vue new file mode 100644 index 0000000000..055bfae997 --- /dev/null +++ b/untangle-vue-ui/source/src/components/settings/services/BrandingManager.vue @@ -0,0 +1,123 @@ + + + diff --git a/untangle-vue-ui/source/src/components/settings/services/ConfigurationBackup.vue b/untangle-vue-ui/source/src/components/settings/services/ConfigurationBackup.vue new file mode 100644 index 0000000000..bf76cedd8a --- /dev/null +++ b/untangle-vue-ui/source/src/components/settings/services/ConfigurationBackup.vue @@ -0,0 +1,116 @@ + + + diff --git a/untangle-vue-ui/source/src/components/settings/services/DirectoryConnector.vue b/untangle-vue-ui/source/src/components/settings/services/DirectoryConnector.vue new file mode 100644 index 0000000000..0a64e1f479 --- /dev/null +++ b/untangle-vue-ui/source/src/components/settings/services/DirectoryConnector.vue @@ -0,0 +1,161 @@ + + + diff --git a/untangle-vue-ui/source/src/components/settings/services/DynamicBlockLists.vue b/untangle-vue-ui/source/src/components/settings/services/DynamicBlockLists.vue index b9cf877dce..bf900dc002 100644 --- a/untangle-vue-ui/source/src/components/settings/services/DynamicBlockLists.vue +++ b/untangle-vue-ui/source/src/components/settings/services/DynamicBlockLists.vue @@ -1,6 +1,6 @@