diff options
Diffstat (limited to 'solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers')
23 files changed, 4626 insertions, 0 deletions
diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js new file mode 100644 index 000000000..6fbca386f --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js @@ -0,0 +1,27 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('AliasOverviewController', +function($scope, $routeParams, Collections, Constants) { + $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + $scope.selectedCollection = $scope.currentCollection; + }; + + $scope.refresh(); +}); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js new file mode 100644 index 000000000..5fff59caa --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js @@ -0,0 +1,201 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('AnalysisController', + function($scope, $location, $routeParams, Luke, Analysis, Constants) { + $scope.resetMenu("analysis", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + Luke.schema({core: $routeParams.core}, function(data) { + $scope.fieldsAndTypes = []; + for (var field in data.schema.fields) { + $scope.fieldsAndTypes.push({ + group: "Fields", + value: "fieldname=" + field, + label: field}); + } + for (var type in data.schema.types) { + $scope.fieldsAndTypes.push({ + group: "Types", + value: "fieldtype=" + type, + label: type}); + } + $scope.core = $routeParams.core; + }); + + $scope.parseQueryString(); + // @todo - set defaultSearchField either to context["analysis.fieldname"] or context["analysis.fieldtype"] + + }; + $scope.verbose = true; + + var getShortComponentName = function(longname) { + var short = -1 !== longname.indexOf( '$' ) + ? longname.split( '$' )[1] + : longname.split( '.' ).pop(); + return short.match( /[A-Z]/g ).join( '' ); + }; + + var getCaptionsForComponent = function(data) { + var captions = []; + for (var key in data[0]) { + key = key.replace(/.*#/,''); + if (key != "match" && key!="positionHistory") { + captions.push(key.replace(/.*#/,'')); + } + } + return captions; + }; + + var getTokensForComponent = function(data) { + var tokens = []; + var previousPosition = 0; + var index=0; + for (var i in data) { + var tokenhash = data[i]; + var positionDifference = tokenhash.position - previousPosition; + for (var j=positionDifference; j>1; j--) { + tokens.push({position: tokenhash.position - j+1, blank:true, index:index++}); + } + + var token = {position: tokenhash.position, keys:[], index:index++}; + + for (key in tokenhash) { + if (key == "match" || key=="positionHistory") { + //skip, to not display these keys in the UI + } else { + var tokenInfo = new Object(); + tokenInfo.name = key; + tokenInfo.value = tokenhash[key]; + if ('text' === key || 'raw_bytes' === key ) { + if (tokenhash.match) { + tokenInfo.extraclass = 'match'; //to highlight matching text strings + } + } + token.keys.push(tokenInfo); + } + } + tokens.push(token); + previousPosition = tokenhash.position; + } + return tokens; + }; + + var extractComponents = function(data, result, name) { + if (data) { + result[name] = []; + for (var i = 0; i < data.length; i += 2) { + var component = { + name: data[i], + short: getShortComponentName(data[i]), + captions: getCaptionsForComponent(data[i + 1]), + tokens: getTokensForComponent(data[i + 1]) + }; + result[name].push(component); + } + } + }; + + var processAnalysisData = function(analysis, fieldOrType) { + var fieldname; + for (fieldname in analysis[fieldOrType]) {console.log(fieldname);break;} + var response = {}; + extractComponents(analysis[fieldOrType][fieldname].index, response, "index"); + extractComponents(analysis[fieldOrType][fieldname].query, response, "query"); + return response; + }; + + $scope.updateQueryString = function() { + + var parts = $scope.fieldOrType.split("="); + var fieldOrType = parts[0]; + var name = parts[1]; + + if ($scope.indexText) { + $location.search("analysis.fieldvalue", $scope.indexText); + } else if ($location.search()["analysis.fieldvalue"]) { + $location.search("analysis.fieldvalue", null); + } + if ($scope.queryText) { + $location.search("analysis.query", $scope.queryText); + } else if ($location.search()["analysis.query"]) { + $location.search("analysis.query", null); + } + + if (fieldOrType == "fieldname") { + $location.search("analysis.fieldname", name); + $location.search("analysis.fieldtype", null); + } else { + $location.search("analysis.fieldtype", name); + $location.search("analysis.fieldname", null); + } + $location.search("verbose_output", $scope.verbose ? "1" : "0"); + }; + + $scope.parseQueryString = function () { + var params = {}; + var search = $location.search(); + + if (Object.keys(search).length == 0) { + return; + } + for (var key in search) { + params[key]=search[key]; + } + $scope.indexText = search["analysis.fieldvalue"]; + $scope.queryText = search["analysis.query"]; + if (search["analysis.fieldname"]) { + $scope.fieldOrType = "fieldname=" + search["analysis.fieldname"]; + $scope.schemaBrowserUrl = "field=" + search["analysis.fieldname"]; + } else { + $scope.fieldOrType = "fieldtype=" + search["analysis.fieldtype"]; + $scope.schemaBrowserUrl = "type=" + search["analysis.fieldtype"]; + } + if (search["verbose_output"] == undefined) { + $scope.verbose = true; + } else { + $scope.verbose = search["verbose_output"] == "1"; + } + + if ($scope.fieldOrType || $scope.indexText || $scope.queryText) { + params.core = $routeParams.core; + var parts = $scope.fieldOrType.split("="); + var fieldOrType = parts[0] == "fieldname" ? "field_names" : "field_types"; + + Analysis.field(params, function(data) { + $scope.result = processAnalysisData(data.analysis, fieldOrType); + }); + } + }; + + $scope.changeFieldOrType = function() { + var parts = $scope.fieldOrType.split("="); + if (parts[0]=='fieldname') { + $scope.schemaBrowserUrl = "field=" + parts[1]; + } else { + $scope.schemaBrowserUrl = "type=" + parts[1]; + } + }; + + $scope.toggleVerbose = function() { + $scope.verbose = !$scope.verbose; + $scope.updateQueryString(); + }; + + $scope.refresh(); + } +); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js new file mode 100644 index 000000000..b0ba4217d --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js @@ -0,0 +1,1021 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('CloudController', + function($scope, $location, Zookeeper, Constants, Collections, System, Metrics, ZookeeperStatus) { + + $scope.showDebug = false; + + $scope.$on("cloud-dump", function(event) { + $scope.showDebug = true; + }); + + $scope.closeDebug = function() { + $scope.showDebug = false; + }; + + var view = $location.search().view ? $location.search().view : "nodes"; + if (view === "tree") { + $scope.resetMenu("cloud-tree", Constants.IS_ROOT_PAGE); + treeSubController($scope, Zookeeper); + } else if (view === "graph") { + $scope.resetMenu("cloud-graph", Constants.IS_ROOT_PAGE); + graphSubController($scope, Zookeeper, false); + } else if (view === "nodes") { + $scope.resetMenu("cloud-nodes", Constants.IS_ROOT_PAGE); + nodesSubController($scope, Collections, System, Metrics); + } else if (view === "zkstatus") { + $scope.resetMenu("cloud-zkstatus", Constants.IS_ROOT_PAGE); + zkStatusSubController($scope, ZookeeperStatus, false); + } + } +); + +function getOrCreateObj(name, object) { + if (name in object) { + entry = object[name]; + } else { + entry = {}; + object[name] = entry; + } + return entry; +} + +function getOrCreateList(name, object) { + if (name in object) { + entry = object[name]; + } else { + entry = []; + object[name] = entry; + } + return entry; +} + +function ensureInList(string, list) { + if (list.indexOf(string) === -1) { + list.push(string); + } +} + +/* Puts a node name into the hosts structure */ +function ensureNodeInHosts(node_name, hosts) { + var hostName = node_name.split(":")[0]; + var host = getOrCreateObj(hostName, hosts); + var hostNodes = getOrCreateList("nodes", host); + ensureInList(node_name, hostNodes); +} + +// from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/ +function bytesToSize(bytes) { + var sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; + if (bytes === 0) return '0b'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + if (bytes === 0) return bytes + '' + sizes[i]; + return (bytes / Math.pow(1024, i)).toFixed(1) + '' + sizes[i]; +} + +function numDocsHuman(docs) { + var sizes = ['', 'k', 'mn', 'bn', 'tn']; + if (docs === 0) return '0'; + var i = parseInt(Math.floor(Math.log(docs) / Math.log(1000))); + if (i === 0) return docs + '' + sizes[i]; + return (docs / Math.pow(1000, i)).toFixed(1) + '' + sizes[i]; +} + +/* Returns a style class depending on percentage */ +var styleForPct = function (pct) { + if (pct < 60) return "pct-normal"; + if (pct < 80) return "pct-warn"; + return "pct-critical" +}; + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +function coreNameToLabel(name) { + return name.replace(/(.*?)_shard((\d+_?)+)_replica_?[ntp]?(\d+)/, '\$1_s\$2r\$4'); +} + +var nodesSubController = function($scope, Collections, System, Metrics) { + $scope.pageSize = 10; + $scope.showNodes = true; + $scope.showTree = false; + $scope.showGraph = false; + $scope.showData = false; + $scope.showAllDetails = false; + $scope.showDetails = {}; + $scope.from = 0; + $scope.to = $scope.pageSize - 1; + $scope.filterType = "node"; // Pre-initialize dropdown + + $scope.toggleAllDetails = function() { + $scope.showAllDetails = !$scope.showAllDetails; + for (var node in $scope.nodes) { + $scope.showDetails[node] = $scope.showAllDetails; + } + for (var host in $scope.hosts) { + $scope.showDetails[host] = $scope.showAllDetails; + } + }; + + $scope.toggleDetails = function(key) { + $scope.showDetails[key] = !$scope.showDetails[key] === true; + }; + + $scope.toggleHostDetails = function(key) { + $scope.showDetails[key] = !$scope.showDetails[key] === true; + for (var nodeId in $scope.hosts[key].nodes) { + var node = $scope.hosts[key].nodes[nodeId]; + $scope.showDetails[node] = $scope.showDetails[key]; + } + }; + + $scope.nextPage = function() { + $scope.from += parseInt($scope.pageSize); + $scope.reload(); + }; + + $scope.previousPage = function() { + $scope.from = Math.max(0, $scope.from - parseInt($scope.pageSize)); + $scope.reload(); + }; + + // Checks if this node is the first (alphabetically) for a given host. Used to decide rowspan in table + $scope.isFirstNodeForHost = function(node) { + var hostName = node.split(":")[0]; + var nodesInHost = $scope.filteredNodes.filter(function (node) { + return node.startsWith(hostName); + }); + return nodesInHost[0] === node; + }; + + // Returns the first live node for this host, to make sure we pick host-level metrics from a live node + $scope.firstLiveNodeForHost = function(key) { + var hostName = key.split(":")[0]; + var liveNodesInHost = $scope.filteredNodes.filter(function (key) { + return key.startsWith(hostName); + }).filter(function (key) { + return $scope.live_nodes.includes(key); + }); + return liveNodesInHost.length > 0 ? liveNodesInHost[0] : key; + }; + + // Initializes the cluster state, list of nodes, collections etc + $scope.initClusterState = function() { + var nodes = {}; + var hosts = {}; + var live_nodes = []; + + // We build a node-centric view of the cluster state which we can easily consume to render the table + Collections.status(function (data) { + // Fetch cluster state from collections API and invert to a nodes structure + for (var name in data.cluster.collections) { + var collection = data.cluster.collections[name]; + collection.name = name; + var shards = collection.shards; + collection.shards = []; + for (var shardName in shards) { + var shard = shards[shardName]; + shard.name = shardName; + shard.collection = collection.name; + var replicas = shard.replicas; + shard.replicas = []; + for (var replicaName in replicas) { + var core = replicas[replicaName]; + core.name = replicaName; + core.label = coreNameToLabel(core['core']); + core.collection = collection.name; + core.shard = shard.name; + core.shard_state = shard.state; + + var node_name = core['node_name']; + var node = getOrCreateObj(node_name, nodes); + var cores = getOrCreateList("cores", node); + cores.push(core); + node['base_url'] = core.base_url; + node['id'] = core.base_url.replace(/[^\w\d]/g, ''); + node['host'] = node_name.split(":")[0]; + var collections = getOrCreateList("collections", node); + ensureInList(core.collection, collections); + ensureNodeInHosts(node_name, hosts); + } + } + } + + live_nodes = data.cluster.live_nodes; + for (n in data.cluster.live_nodes) { + node = data.cluster.live_nodes[n]; + if (!(node in nodes)) { + var hostName = node.split(":")[0]; + nodes[node] = {}; + nodes[node]['host'] = hostName; + } + ensureNodeInHosts(node, hosts); + } + + // Make sure nodes are sorted alphabetically to align with rowspan in table + for (var host in hosts) { + hosts[host].nodes.sort(); + } + + $scope.nodes = nodes; + $scope.hosts = hosts; + $scope.live_nodes = live_nodes; + + $scope.Math = window.Math; + $scope.reload(); + }); + }; + + $scope.filterInput = function() { + $scope.from = 0; + $scope.to = $scope.pageSize - 1; + $scope.reload(); + }; + + /* + Reload will fetch data for the current page of the table and thus refresh numbers. + It is also called whenever a filter or paging action is executed + */ + $scope.reload = function() { + var nodes = $scope.nodes; + var node_keys = Object.keys(nodes); + var hosts = $scope.hosts; + var live_nodes = $scope.live_nodes; + var hostNames = Object.keys(hosts); + hostNames.sort(); + var pageSize = isNumeric($scope.pageSize) ? $scope.pageSize : 10; + + // Calculate what nodes that will show on this page + var nodesToShow = []; + var nodesParam; + var hostsToShow = []; + var filteredNodes; + var filteredHosts; + var isFiltered = false; + switch ($scope.filterType) { + case "node": // Find what nodes match the node filter + if ($scope.nodeFilter) { + filteredNodes = node_keys.filter(function (node) { + return node.indexOf($scope.nodeFilter) !== -1; + }); + } + break; + + case "collection": // Find what collections match the collection filter and what nodes that have these collections + if ($scope.collectionFilter) { + candidateNodes = {}; + nodesCollections = []; + for (var i = 0 ; i < node_keys.length ; i++) { + var node_name = node_keys[i]; + var node = nodes[node_name]; + nodeColl = {}; + nodeColl['node'] = node_name; + collections = {}; + node.cores.forEach(function(core) { + collections[core.collection] = true; + }); + nodeColl['collections'] = Object.keys(collections); + nodesCollections.push(nodeColl); + } + nodesCollections.forEach(function(nc) { + matchingColls = nc['collections'].filter(function (collection) { + return collection.indexOf($scope.collectionFilter) !== -1; + }); + if (matchingColls.length > 0) { + candidateNodes[nc.node] = true; + } + }); + filteredNodes = Object.keys(candidateNodes); + } + break; + + case "health": + + } + + if (filteredNodes) { + // If filtering is active, calculate what hosts contain the nodes that match the filters + isFiltered = true; + filteredHosts = filteredNodes.map(function (node) { + return node.split(":")[0]; + }).filter(function (item, index, self) { + return self.indexOf(item) === index; + }); + } else { + filteredNodes = node_keys; + filteredHosts = hostNames; + } + filteredNodes.sort(); + filteredHosts.sort(); + + // Find what hosts & nodes (from the filtered set) that should be displayed on current page + for (var id = $scope.from ; id < $scope.from + pageSize && filteredHosts[id] ; id++) { + var hostName = filteredHosts[id]; + hostsToShow.push(hostName); + if (isFiltered) { // Only show the nodes per host matching active filter + nodesToShow = nodesToShow.concat(filteredNodes.filter(function (node) { + return node.startsWith(hostName); + })); + } else { + nodesToShow = nodesToShow.concat(hosts[hostName]['nodes']); + } + } + nodesParam = nodesToShow.filter(function (node) { + return live_nodes.includes(node); + }).join(','); + var deadNodes = nodesToShow.filter(function (node) { + return !live_nodes.includes(node); + }); + deadNodes.forEach(function (node) { + nodes[node]['dead'] = true; + }); + $scope.nextEnabled = $scope.from + pageSize < filteredHosts.length; + $scope.prevEnabled = $scope.from - pageSize >= 0; + nodesToShow.sort(); + hostsToShow.sort(); + + /* + Fetch system info for all selected nodes + Pick the data we want to display and add it to the node-centric data structure + */ + System.get({"nodes": nodesParam}, function (systemResponse) { + for (var node in systemResponse) { + if (node in nodes) { + var s = systemResponse[node]; + nodes[node]['system'] = s; + var memTotal = s.system.totalPhysicalMemorySize; + var memFree = s.system.freePhysicalMemorySize; + var memPercentage = Math.floor((memTotal - memFree) / memTotal * 100); + nodes[node]['memUsedPct'] = memPercentage; + nodes[node]['memUsedPctStyle'] = styleForPct(memPercentage); + nodes[node]['memTotal'] = bytesToSize(memTotal); + nodes[node]['memFree'] = bytesToSize(memFree); + nodes[node]['memUsed'] = bytesToSize(memTotal - memFree); + + var heapTotal = s.jvm.memory.raw.total; + var heapFree = s.jvm.memory.raw.free; + var heapPercentage = Math.floor((heapTotal - heapFree) / heapTotal * 100); + nodes[node]['heapUsed'] = bytesToSize(heapTotal - heapFree); + nodes[node]['heapUsedPct'] = heapPercentage; + nodes[node]['heapUsedPctStyle'] = styleForPct(heapPercentage); + nodes[node]['heapTotal'] = bytesToSize(heapTotal); + nodes[node]['heapFree'] = bytesToSize(heapFree); + + var jvmUptime = s.jvm.jmx.upTimeMS / 1000; // Seconds + nodes[node]['jvmUptime'] = secondsForHumans(jvmUptime); + nodes[node]['jvmUptimeSec'] = jvmUptime; + + nodes[node]['uptime'] = s.system.uptime.replace(/.*up (.*?,.*?),.*/, "$1"); + nodes[node]['loadAvg'] = Math.round(s.system.systemLoadAverage * 100) / 100; + nodes[node]['cpuPct'] = Math.ceil(s.system.processCpuLoad); + nodes[node]['cpuPctStyle'] = styleForPct(Math.ceil(s.system.processCpuLoad)); + nodes[node]['maxFileDescriptorCount'] = s.system.maxFileDescriptorCount; + nodes[node]['openFileDescriptorCount'] = s.system.openFileDescriptorCount; + } + } + }); + + /* + Fetch metrics for all selected nodes. Only pull the metrics that we'll show to save bandwidth + Pick the data we want to display and add it to the node-centric data structure + */ + Metrics.get({ + "nodes": nodesParam, + "prefix": "CONTAINER.fs,org.eclipse.jetty.server.handler.DefaultHandler.get-requests,INDEX.sizeInBytes,SEARCHER.searcher.numDocs,SEARCHER.searcher.deletedDocs,SEARCHER.searcher.warmupTime" + }, + function (metricsResponse) { + for (var node in metricsResponse) { + if (node in nodes) { + var m = metricsResponse[node]; + nodes[node]['metrics'] = m; + var diskTotal = m.metrics['solr.node']['CONTAINER.fs.totalSpace']; + var diskFree = m.metrics['solr.node']['CONTAINER.fs.usableSpace']; + var diskPercentage = Math.floor((diskTotal - diskFree) / diskTotal * 100); + nodes[node]['diskUsedPct'] = diskPercentage; + nodes[node]['diskUsedPctStyle'] = styleForPct(diskPercentage); + nodes[node]['diskTotal'] = bytesToSize(diskTotal); + nodes[node]['diskFree'] = bytesToSize(diskFree); + + var r = m.metrics['solr.jetty']['org.eclipse.jetty.server.handler.DefaultHandler.get-requests']; + nodes[node]['req'] = r.count; + nodes[node]['req1minRate'] = Math.floor(r['1minRate'] * 100) / 100; + nodes[node]['req5minRate'] = Math.floor(r['5minRate'] * 100) / 100; + nodes[node]['req15minRate'] = Math.floor(r['15minRate'] * 100) / 100; + nodes[node]['reqp75_ms'] = Math.floor(r['p75_ms']); + nodes[node]['reqp95_ms'] = Math.floor(r['p95_ms']); + nodes[node]['reqp99_ms'] = Math.floor(r['p99_ms']); + + var cores = nodes[node]['cores']; + var indexSizeTotal = 0; + var docsTotal = 0; + var graphData = []; + if (cores) { + for (coreId in cores) { + var core = cores[coreId]; + var keyName = "solr.core." + core['core'].replace(/(.*?)_(shard(\d+_?)+)_(replica.*?)/, '\$1.\$2.\$4'); + var nodeMetric = m.metrics[keyName]; + var size = nodeMetric['INDEX.sizeInBytes']; + size = (typeof size !== 'undefined') ? size : 0; + core['sizeInBytes'] = size; + core['size'] = bytesToSize(size); + if (core['shard_state'] !== 'active' || core['state'] !== 'active') { + // If core state is not active, display the real state, or if shard is inactive, display that + var labelState = (core['state'] !== 'active') ? core['state'] : core['shard_state']; + core['label'] += "_(" + labelState + ")"; + } + indexSizeTotal += size; + var numDocs = nodeMetric['SEARCHER.searcher.numDocs']; + numDocs = (typeof numDocs !== 'undefined') ? numDocs : 0; + core['numDocs'] = numDocs; + core['numDocsHuman'] = numDocsHuman(numDocs); + core['avgSizePerDoc'] = bytesToSize(numDocs === 0 ? 0 : size / numDocs); + var deletedDocs = nodeMetric['SEARCHER.searcher.deletedDocs']; + deletedDocs = (typeof deletedDocs !== 'undefined') ? deletedDocs : 0; + core['deletedDocs'] = deletedDocs; + core['deletedDocsHuman'] = numDocsHuman(deletedDocs); + var warmupTime = nodeMetric['SEARCHER.searcher.warmupTime']; + warmupTime = (typeof warmupTime !== 'undefined') ? warmupTime : 0; + core['warmupTime'] = warmupTime; + docsTotal += core['numDocs']; + } + for (coreId in cores) { + core = cores[coreId]; + var graphObj = {}; + graphObj['label'] = core['label']; + graphObj['size'] = core['sizeInBytes']; + graphObj['sizeHuman'] = core['size']; + graphObj['pct'] = (core['sizeInBytes'] / indexSizeTotal) * 100; + graphData.push(graphObj); + } + cores.sort(function (a, b) { + return b.sizeInBytes - a.sizeInBytes + }); + } else { + cores = {}; + } + graphData.sort(function (a, b) { + return b.size - a.size + }); + nodes[node]['graphData'] = graphData; + nodes[node]['numDocs'] = numDocsHuman(docsTotal); + nodes[node]['sizeInBytes'] = indexSizeTotal; + nodes[node]['size'] = bytesToSize(indexSizeTotal); + nodes[node]['sizePerDoc'] = docsTotal === 0 ? '0b' : bytesToSize(indexSizeTotal / docsTotal); + + // Build the d3 powered bar chart + $('#chart' + nodes[node]['id']).empty(); + var chart = d3.select('#chart' + nodes[node]['id']).append('div').attr('class', 'chart'); + + // Add one div per bar which will group together both labels and bars + var g = chart.selectAll('div') + .data(nodes[node]['graphData']).enter() + .append('div'); + + // Add the bars + var bars = g.append("div") + .attr("class", "rect") + .text(function (d) { + return d.label + ':\u00A0\u00A0' + d.sizeHuman; + }); + + // Execute the transition to show the bars + bars.transition() + .ease('elastic') + .style('width', function (d) { + return d.pct + '%'; + }); + } + } + }); + $scope.nodes = nodes; + $scope.hosts = hosts; + $scope.live_nodes = live_nodes; + $scope.nodesToShow = nodesToShow; + $scope.hostsToShow = hostsToShow; + $scope.filteredNodes = filteredNodes; + $scope.filteredHosts = filteredHosts; + }; + $scope.initClusterState(); +}; + +var zkStatusSubController = function($scope, ZookeeperStatus) { + $scope.showZkStatus = true; + $scope.showNodes = false; + $scope.showTree = false; + $scope.showGraph = false; + $scope.tree = {}; + $scope.showData = false; + $scope.showDetails = false; + + $scope.toggleDetails = function() { + $scope.showDetails = !$scope.showDetails === true; + }; + + $scope.initZookeeper = function() { + ZookeeperStatus.monitor({}, function(data) { + $scope.zkState = data.zkStatus; + $scope.mainKeys = ["ok", "clientPort", "secureClientPort", "zk_server_state", "zk_version", + "zk_approximate_data_size", "zk_znode_count", "zk_num_alive_connections"]; + $scope.detailKeys = ["dataDir", "dataLogDir", + "zk_avg_latency", "zk_max_file_descriptor_count", "zk_watch_count", + "zk_packets_sent", "zk_packets_received", + "tickTime", "maxClientCnxns", "minSessionTimeout", "maxSessionTimeout"]; + $scope.ensembleMainKeys = ["serverId", "electionPort", "quorumPort"]; + $scope.ensembleDetailKeys = ["peerType", "electionAlg", "initLimit", "syncLimit", + "zk_followers", "zk_synced_followers", "zk_pending_syncs", + "server.1", "server.2", "server.3", "server.4", "server.5"]; + $scope.notEmptyRow = function(key) { + for (hostId in $scope.zkState.details) { + if (key in $scope.zkState.details[hostId]) return true; + } + return false; + }; + }); + }; + + $scope.initZookeeper(); +}; + +var treeSubController = function($scope, Zookeeper) { + $scope.showZkStatus = false; + $scope.showTree = true; + $scope.showGraph = false; + $scope.tree = {}; + $scope.showData = false; + + $scope.showTreeLink = function(link) { + var path = decodeURIComponent(link.replace(/.*[\\?&]path=([^&#]*).*/, "$1")); + Zookeeper.detail({path: path}, function(data) { + $scope.znode = data.znode; + var path = data.znode.path.split( '.' ); + if(path.length >1) { + $scope.lang = path.pop(); + } else { + var lastPathElement = data.znode.path.split( '/' ).pop(); + if (lastPathElement == "managed-schema") { + $scope.lang = "xml"; + } + } + $scope.showData = true; + }); + }; + + $scope.hideData = function() { + $scope.showData = false; + }; + + $scope.initTree = function() { + Zookeeper.simple(function(data) { + $scope.tree = data.tree; + }); + }; + + $scope.initTree(); +}; + +/** + * Translates seconds into human readable format of seconds, minutes, hours, days, and years + * + * @param {number} seconds The number of seconds to be processed + * @return {string} The phrase describing the the amount of time + */ +function secondsForHumans ( seconds ) { + var levels = [ + [Math.floor(seconds / 31536000), 'y'], + [Math.floor((seconds % 31536000) / 86400), 'd'], + [Math.floor(((seconds % 31536000) % 86400) / 3600), 'h'], + [Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), 'm'] + ]; + var returntext = ''; + + for (var i = 0, max = levels.length; i < max; i++) { + if ( levels[i][0] === 0 ) continue; + returntext += ' ' + levels[i][0] + levels[i][1]; + } + return returntext.trim() === '' ? '0m' : returntext.trim(); +} + +var graphSubController = function ($scope, Zookeeper) { + $scope.showZkStatus = false; + $scope.showTree = false; + $scope.showGraph = true; + + $scope.filterType = "status"; + + $scope.helperData = { + protocol: [], + host: [], + hostname: [], + port: [], + pathname: [], + replicaType: [], + base_url: [], + core: [], + node_name: [], + state: [], + core_node: [] + }; + + $scope.next = function() { + $scope.pos += $scope.rows; + $scope.initGraph(); + }; + + $scope.previous = function() { + $scope.pos = Math.max(0, $scope.pos - $scope.rows); + $scope.initGraph(); + }; + + $scope.resetGraph = function() { + $scope.pos = 0; + $scope.initGraph(); + }; + + $scope.initGraph = function() { + Zookeeper.liveNodes(function (data) { + var live_nodes = {}; + for (var c in data.tree[0].children) { + live_nodes[data.tree[0].children[c].data.title] = true; + } + + var params = {view: "graph"}; + if ($scope.rows) { + params.start = $scope.pos; + params.rows = $scope.rows; + } + + var filter = ($scope.filterType=='status') ? $scope.pagingStatusFilter : $scope.pagingFilter; + + if (filter) { + params.filterType = $scope.filterType; + params.filter = filter; + } + + Zookeeper.clusterState(params, function (data) { + eval("var state=" + data.znode.data); // @todo fix horrid means to parse JSON + + var leaf_count = 0; + var graph_data = { + name: null, + children: [] + }; + + for (var c in state) { + var shards = []; + for (var s in state[c].shards) { + var shard_status = state[c].shards[s].state; + shard_status = shard_status == 'inactive' ? 'shard-inactive' : shard_status; + var nodes = []; + for (var n in state[c].shards[s].replicas) { + leaf_count++; + var replica = state[c].shards[s].replicas[n] + + var uri = replica.base_url; + var parts = uri.match(/^(\w+:)\/\/(([\w\d\.-]+)(:(\d+))?)(.+)$/); + var uri_parts = { + protocol: parts[1], + host: parts[2], + hostname: parts[3], + port: parseInt(parts[5] || 80, 10), + pathname: parts[6], + replicaType: replica.type, + base_url: replica.base_url, + core: replica.core, + node_name: replica.node_name, + state: replica.state, + core_node: n + }; + + $scope.helperData.protocol.push(uri_parts.protocol); + $scope.helperData.host.push(uri_parts.host); + $scope.helperData.hostname.push(uri_parts.hostname); + $scope.helperData.port.push(uri_parts.port); + $scope.helperData.pathname.push(uri_parts.pathname); + $scope.helperData.replicaType.push(uri_parts.replicaType); + $scope.helperData.base_url.push(uri_parts.base_url); + $scope.helperData.core.push(uri_parts.core); + $scope.helperData.node_name.push(uri_parts.node_name); + $scope.helperData.state.push(uri_parts.state); + $scope.helperData.core_node.push(uri_parts.core_node); + + var replica_status = replica.state; + + if (!live_nodes[replica.node_name]) { + replica_status = 'gone'; + } else if(shard_status=='shard-inactive') { + replica_status += ' ' + shard_status; + } + + var node = { + name: uri, + data: { + type: 'node', + state: replica_status, + leader: 'true' === replica.leader, + uri: uri_parts + } + }; + nodes.push(node); + } + + var shard = { + name: shard_status == "shard-inactive" ? s + ' (inactive)' : s, + data: { + type: 'shard', + state: shard_status, + range: state[c].shards[s].range + + }, + children: nodes + }; + shards.push(shard); + } + + var collection = { + name: c, + data: { + type: 'collection', + pullReplicas: state[c].pullReplicas, + replicationFactor: state[c].replicationFactor, + router: state[c].router.name, + maxShardsPerNode: state[c].maxShardsPerNode, + autoAddReplicas: state[c].autoAddReplicas, + nrtReplicas: state[c].nrtReplicas, + tlogReplicas: state[c].tlogReplicas, + numShards: shards.length + }, + children: shards + }; + graph_data.children.push(collection); + } + $scope.helperData.protocol = $.unique($scope.helperData.protocol); + $scope.helperData.host = $.unique($scope.helperData.host); + $scope.helperData.hostname = $.unique($scope.helperData.hostname); + $scope.helperData.port = $.unique($scope.helperData.port); + $scope.helperData.pathname = $.unique($scope.helperData.pathname); + $scope.helperData.replicaType = $.unique($scope.helperData.replicaType); + $scope.helperData.base_url = $.unique($scope.helperData.base_url); + $scope.helperData.core = $.unique($scope.helperData.core); + $scope.helperData.node_name = $.unique($scope.helperData.node_name); + $scope.helperData.state = $.unique($scope.helperData.state); + $scope.helperData.core_node = $.unique($scope.helperData.core_node); + + if (data.znode && data.znode.paging) { + $scope.showPaging = true; + + var parr = data.znode.paging.split('|'); + if (parr.length < 3) { + $scope.showPaging = false; + } else { + $scope.start = Math.max(parseInt(parr[0]), 0); + $scope.prevEnabled = ($scope.start > 0); + $scope.rows = parseInt(parr[1]); + $scope.total = parseInt(parr[2]); + + if ($scope.rows == -1) { + $scope.showPaging = false; + } else { + var filterType = parr.length > 3 ? parr[3] : ''; + + if (filterType == '' || filterType == 'none') filterType = 'status'; + + +$('#cloudGraphPagingFilterType').val(filterType); + + var filter = parr.length > 4 ? parr[4] : ''; + var page = Math.floor($scope.start / $scope.rows) + 1; + var pages = Math.ceil($scope.total / $scope.rows); + $scope.last = Math.min($scope.start + $scope.rows, $scope.total); + $scope.nextEnabled = ($scope.last < $scope.total); + } + } + } + else { + $scope.showPaging = false; + } + $scope.graphData = graph_data; + $scope.leafCount = leaf_count; + }); + }); + }; + + $scope.initGraph(); + $scope.pos = 0; +}; + +solrAdminApp.directive('graph', function(Constants) { + return { + restrict: 'EA', + scope: { + data: "=", + leafCount: "=", + helperData: "=", + }, + link: function (scope, element, attrs) { + var helper_path_class = function (p) { + var classes = ['link']; + classes.push('lvl-' + p.target.depth); + + if (p.target.data && p.target.data.leader) { + classes.push('leader'); + } + + if (p.target.data && p.target.data.state) { + classes.push(p.target.data.state); + } + + return classes.join(' '); + }; + + var helper_node_class = function (d) { + var classes = ['node']; + classes.push('lvl-' + d.depth); + + if (d.data && d.data.leader) { + classes.push('leader'); + } + + if (d.data && d.data.state) { + if(!(d.data.type=='shard' && d.data.state=='active')){ + classes.push(d.data.state); + } + } + + return classes.join(' '); + }; + + var helper_tooltip_text = function (d) { + if (!d.data) { + return tooltip; + } + var tooltip; + + if (! d.data.type) { + return tooltip; + } + + + if (d.data.type == 'collection') { + tooltip = d.name + " {<br/> "; + tooltip += "numShards: [" + d.data.numShards + "],<br/>"; + tooltip += "maxShardsPerNode: [" + d.data.maxShardsPerNode + "],<br/>"; + tooltip += "router: [" + d.data.router + "],<br/>"; + tooltip += "autoAddReplicas: [" + d.data.autoAddReplicas + "],<br/>"; + tooltip += "replicationFactor: [" + d.data.replicationFactor + "],<br/>"; + tooltip += "nrtReplicas: [" + d.data.nrtReplicas + "],<br/>"; + tooltip += "pullReplicas: [" + d.data.pullReplicas + "],<br/>"; + tooltip += "tlogReplicas: [" + d.data.tlogReplicas + "],<br/>"; + tooltip += "}"; + } else if (d.data.type == 'shard') { + tooltip = d.name + " {<br/> "; + tooltip += "range: [" + d.data.range + "],<br/>"; + tooltip += "state: [" + d.data.state + "],<br/>"; + tooltip += "}"; + } else if (d.data.type == 'node') { + tooltip = d.data.uri.core_node + " {<br/>"; + + if (0 !== scope.helperData.core.length) { + tooltip += "core: [" + d.data.uri.core + "],<br/>"; + } + + if (0 !== scope.helperData.node_name.length) { + tooltip += "node_name: [" + d.data.uri.node_name + "],<br/>"; + } + tooltip += "}"; + } + + return tooltip; + }; + + var helper_node_text = function (d) { + if (!d.data || !d.data.uri) { + return d.name; + } + + var name = d.data.uri.hostname; + if (1 !== scope.helperData.protocol.length) { + name = d.data.uri.protocol + '//' + name; + } + + if (1 !== scope.helperData.port.length) { + name += ':' + d.data.uri.port; + } + + if (1 !== scope.helperData.pathname.length) { + name += d.data.uri.pathname; + } + + if(0 !== scope.helperData.replicaType.length) { + name += ' (' + d.data.uri.replicaType[0] + ')'; + } + + return name; + }; + + scope.$watch("data", function(newValue, oldValue) { + if (newValue) { + flatGraph(element, scope.data, scope.leafCount); + } + + $('text').tooltip({ + content: function() { + return $(this).attr('title'); + } + }); + }); + + + function setNodeNavigationBehavior(node, view){ + node + .attr('data-href', function (d) { + if (d.type == "node"){ + return getNodeUrl(d, view); + } + else{ + return ""; + } + }) + .on('click', function(d) { + if (d.data.type == "node"){ + location.href = getNodeUrl(d, view); + } + }); + } + + function getNodeUrl(d, view){ + var url = d.name + Constants.ROOT_URL + "#/~cloud"; + if (view != undefined){ + url += "?view=" + view; + } + return url; + } + + var flatGraph = function(element, graphData, leafCount) { + var w = element.width(), + h = leafCount * 20; + + var tree = d3.layout.tree().size([h, w - 400]); + + var diagonal = d3.svg.diagonal().projection(function (d) { + return [d.y, d.x]; + }); + + d3.select('#canvas', element).html(''); + var vis = d3.select('#canvas', element).append('svg') + .attr('width', w) + .attr('height', h) + .append('g') + .attr('transform', 'translate(100, 0)'); + + var nodes = tree.nodes(graphData); + + var link = vis.selectAll('path.link') + .data(tree.links(nodes)) + .enter().append('path') + .attr('class', helper_path_class) + .attr('d', diagonal); + + var node = vis.selectAll('g.node') + .data(nodes) + .enter().append('g') + .attr('class', helper_node_class) + .attr('transform', function (d) { + return 'translate(' + d.y + ',' + d.x + ')'; + }) + + node.append('circle') + .attr('r', 4.5); + + node.append('text') + .attr('dx', function (d) { + return 0 === d.depth ? -8 : 8; + }) + .attr('dy', function (d) { + return 5; + }) + .attr('text-anchor', function (d) { + return 0 === d.depth ? 'end' : 'start'; + }) + .attr("title", helper_tooltip_text) + .text(helper_node_text); + + setNodeNavigationBehavior(node); + }; + } + }; +}); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js new file mode 100644 index 000000000..01e1964e0 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js @@ -0,0 +1,62 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +solrAdminApp.controller('ClusterSuggestionsController', +function($scope, $http, Constants) { + $scope.resetMenu("cluster-suggestion", Constants.IS_COLLECTION_PAGE); + $scope.data={}; + var dataArr =[]; + var dataJson = {}; + //function to display suggestion + $http({ + method: 'GET', + url: '/api/cluster/autoscaling/suggestions' + }).then(function successCallback(response) { + $scope.data = response.data; + $scope.parsedData = $scope.data.suggestions; + }, function errorCallback(response) { + }); + //function to perform operation + $scope.postdata = function (x) { + x.loading = true; + var path=x.operation.path; + var command=x.operation.command; + var fullPath='/api/'+path; + console.log(fullPath); + console.log(command); + $http.post(fullPath, JSON.stringify(command)).then(function (response) { + if (response.data) + console.log(response.data); + x.loading = false; + x.done = true; + x.run=true; + $scope.msg = "Command Submitted Successfully!"; + }, function (response) { + x.failed=true; + $scope.msg = "Service does not exist"; + $scope.statusval = response.status; + $scope.statustext = response.statusText; + $scope.headers = response.headers(); + }); + }; + $scope.showPopover = function() { + $scope.popup = true; + }; + + $scope.hidePopover = function () { + $scope.popup = false; + }; +}); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js new file mode 100644 index 000000000..d1834b2bd --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js @@ -0,0 +1,39 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('CollectionOverviewController', +function($scope, $routeParams, Collections, Constants) { + $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + Collections.status({}, function(data) { + $scope.selectedCollection = data.cluster.collections[$routeParams.core]; + $scope.selectedCollection.name = $routeParams.core; + $scope.rootUrl = Constants.ROOT_URL; + }); + }; + + $scope.showReplica = function(replica) { + replica.show = !replica.show; + } + + $scope.hideShard = function(shard) { + shard.hide = !shard.hide; + } + + $scope.refresh(); +}); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js new file mode 100644 index 000000000..978fc71e6 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js @@ -0,0 +1,289 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('CollectionsController', + function($scope, $routeParams, $location, $timeout, Collections, Zookeeper, Constants){ + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + + $scope.refresh = function() { + + $scope.rootUrl = Constants.ROOT_URL + "#/~collections/" + $routeParams.collection; + + Collections.status(function (data) { + $scope.collections = []; + for (var name in data.cluster.collections) { + var collection = data.cluster.collections[name]; + collection.name = name; + collection.type = 'collection'; + var shards = collection.shards; + collection.shards = []; + for (var shardName in shards) { + var shard = shards[shardName]; + shard.name = shardName; + shard.collection = collection.name; + var replicas = shard.replicas; + shard.replicas = []; + for (var replicaName in replicas) { + var replica = replicas[replicaName]; + replica.name = replicaName; + replica.collection = collection.name; + replica.shard = shard.name; + shard.replicas.push(replica); + } + collection.shards.push(shard); + } + $scope.collections.push(collection); + if ($routeParams.collection == name) { + $scope.collection = collection; + } + } + // Fetch aliases using LISTALIASES to get properties + Collections.listaliases(function (adata) { + // TODO: Population of aliases array duplicated in app.js + $scope.aliases = []; + for (var key in adata.aliases) { + props = {}; + if (key in adata.properties) { + props = adata.properties[key]; + } + var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props}; + $scope.aliases.push(alias); + if ($routeParams.collection == 'alias_' + key) { + $scope.collection = alias; + } + } + // Decide what is selected in list + if ($routeParams.collection && !$scope.collection) { + alert("No collection or alias called " + $routeParams.collection); + $location.path("/~collections"); + } + }); + + $scope.liveNodes = data.cluster.liveNodes; + }); + Zookeeper.configs(function(data) { + $scope.configs = []; + var items = data.tree[0].children; + for (var i in items) { + $scope.configs.push({name: items[i].data.title}); + } + }); + }; + + $scope.hideAll = function() { + $scope.showRename = false; + $scope.showAdd = false; + $scope.showDelete = false; + $scope.showSwap = false; + $scope.showCreateAlias = false; + $scope.showDeleteAlias = false; + }; + + $scope.showAddCollection = function() { + $scope.hideAll(); + $scope.showAdd = true; + $scope.newCollection = { + name: "", + routerName: "compositeId", + numShards: 1, + configName: "", + replicationFactor: 1, + maxShardsPerNode: 1, + autoAddReplicas: 'false' + }; + }; + + $scope.toggleCreateAlias = function() { + $scope.hideAll(); + $scope.showCreateAlias = true; + } + + $scope.toggleDeleteAlias = function() { + $scope.hideAll(); + $scope.showDeleteAlias = true; + } + + $scope.cancelCreateAlias = $scope.cancelDeleteAlias = function() { + $scope.hideAll(); + } + + $scope.createAlias = function() { + var collections = []; + for (var i in $scope.aliasCollections) { + collections.push($scope.aliasCollections[i].name); + } + Collections.createAlias({name: $scope.aliasToCreate, collections: collections.join(",")}, function(data) { + $scope.cancelCreateAlias(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/alias_" + $scope.aliasToCreate); + }); + } + $scope.deleteAlias = function() { + Collections.deleteAlias({name: $scope.collection.name}, function(data) { + $scope.hideAll(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/"); + }); + + }; + $scope.addCollection = function() { + if (!$scope.newCollection.name) { + $scope.addMessage = "Please provide a core name"; + } else if (false) { //@todo detect whether core exists + $scope.AddMessage = "A core with that name already exists"; + } else { + var coll = $scope.newCollection; + var params = { + name: coll.name, + "router.name": coll.routerName, + numShards: coll.numShards, + "collection.configName": coll.configName, + replicationFactor: coll.replicationFactor, + maxShardsPerNode: coll.maxShardsPerNode, + autoAddReplicas: coll.autoAddReplicas + }; + if (coll.shards) params.shards = coll.shards; + if (coll.routerField) params["router.field"] = coll.routerField; + Collections.add(params, function(data) { + $scope.cancelAddCollection(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/" + $scope.newCollection.name); + }); + } + }; + + $scope.cancelAddCollection = function() { + delete $scope.addMessage; + $scope.showAdd = false; + }; + + $scope.showDeleteCollection = function() { + $scope.hideAll(); + if ($scope.collection) { + $scope.showDelete = true; + } else { + alert("No collection selected."); + } + }; + + $scope.deleteCollection = function() { + if ($scope.collection.name == $scope.collectionDeleteConfirm) { + Collections.delete({name: $scope.collection.name}, function (data) { + $location.path("/~collections"); + }); + } else { + $scope.deleteMessage = "Collection names do not match."; + } + }; + + $scope.reloadCollection = function() { + if (!$scope.collection) { + alert("No collection selected."); + return; + } + Collections.reload({name: $scope.collection.name}, + function(successData) { + $scope.reloadSuccess = true; + $timeout(function() {$scope.reloadSuccess=false}, 1000); + }, + function(failureData) { + $scope.reloadFailure = true; + $timeout(function() {$scope.reloadFailure=false}, 1000); + $location.path("/~collections"); + }); + }; + + $scope.toggleAddReplica = function(shard) { + $scope.hideAll(); + shard.showAdd = !shard.showAdd; + delete $scope.addReplicaMessage; + + Zookeeper.liveNodes({}, function(data) { + $scope.nodes = []; + var children = data.tree[0].children; + for (var child in children) { + $scope.nodes.push(children[child].data.title); + } + }); + }; + + $scope.toggleRemoveReplica = function(replica) { + $scope.hideAll(); + replica.showRemove = !replica.showRemove; + }; + + $scope.toggleRemoveShard = function(shard) { + $scope.hideAll(); + shard.showRemove = !shard.showRemove; + }; + + $scope.deleteShard = function(shard) { + Collections.deleteShard({collection: shard.collection, shard:shard.name}, function(data) { + shard.deleted = true; + $timeout(function() { + $scope.refresh(); + }, 2000); + }); + } + + $scope.deleteReplica = function(replica) { + Collections.deleteReplica({collection: replica.collection, shard:replica.shard, replica:replica.name}, function(data) { + replica.deleted = true; + $timeout(function() { + $scope.refresh(); + }, 2000); + }); + } + $scope.addReplica = function(shard) { + var params = { + collection: shard.collection, + shard: shard.name, + } + if (shard.replicaNodeName && shard.replicaNodeName != "") { + params.node = shard.replicaNodeName; + } + Collections.addReplica(params, function(data) { + shard.replicaAdded = true; + $timeout(function () { + shard.replicaAdded = false; + shard.showAdd = false; + $$scope.refresh(); + }, 2000); + }); + }; + + $scope.toggleShard = function(shard) { + shard.show = !shard.show; + } + + $scope.toggleReplica = function(replica) { + replica.show = !replica.show; + } + + $scope.refresh(); + } +); + +var flatten = function(data) { + var list = []; + for (var name in data) { + var entry = data[name]; + entry.name = name; + list.push(entry); + } + return list; +} diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js new file mode 100644 index 000000000..0e2b3d2a6 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js @@ -0,0 +1,93 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('CoreOverviewController', +function($scope, $rootScope, $routeParams, Luke, CoreSystem, Update, Replication, Ping, Constants) { + $scope.resetMenu("overview", Constants.IS_CORE_PAGE); + $scope.refreshIndex = function() { + Luke.index({core: $routeParams.core}, + function(data) { + $scope.index = data.index; + delete $scope.statsMessage; + }, + function(error) { + $scope.statsMessage = "Luke is not configured"; + } + ); + }; + + $scope.refreshReplication = function() { + Replication.details({core: $routeParams.core}, + function(data) { + $scope.isSlave = data.details.isSlave == "true"; + $scope.isMaster = data.details.isMaster == "true"; + $scope.replication = data.details; + }, + function(error) { + $scope.replicationMessage = "Replication is not configured"; + }); + }; + + $scope.refreshSystem = function() { + CoreSystem.get({core: $routeParams.core}, + function(data) { + $scope.core = data.core; + delete $scope.systemMessage; + }, + function(error) { + $scope.systemMessage = "/admin/system Handler is not configured"; + } + ); + }; + + $scope.refreshPing = function() { + Ping.status({core: $routeParams.core}, function(data) { + if (data.error) { + $scope.healthcheckStatus = false; + if (data.error.code == 503) { + $scope.healthcheckMessage = 'Ping request handler is not configured with a healthcheck file.'; + } + } else { + $scope.healthcheckStatus = data.status == "enabled"; + } + }); + }; + + $scope.toggleHealthcheck = function() { + if ($scope.healthcheckStatus) { + Ping.disable( + function(data) {$scope.healthcheckStatus = false}, + function(error) {$scope.healthcheckMessage = error} + ); + } else { + Ping.enable( + function(data) {$scope.healthcheckStatus = true}, + function(error) {$scope.healthcheckMessage = error} + ); + } + }; + + $scope.refresh = function() { + $scope.refreshIndex(); + $scope.refreshReplication(); + $scope.refreshSystem(); + $scope.refreshPing(); + }; + + $scope.refresh(); +}); + diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js new file mode 100644 index 000000000..202b6224c --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js @@ -0,0 +1,180 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('CoreAdminController', + function($scope, $routeParams, $location, $timeout, $route, Cores, Update, Constants){ + $scope.resetMenu("cores", Constants.IS_ROOT_PAGE); + $scope.selectedCore = $routeParams.corename; // use 'corename' not 'core' to distinguish from /solr/:core/ + $scope.refresh = function() { + Cores.get(function(data) { + var coreCount = 0; + var cores = data.status; + for (_obj in cores) coreCount++; + $scope.hasCores = coreCount >0; + if (!$scope.selectedCore && coreCount==0) { + $scope.showAddCore(); + return; + } else if (!$scope.selectedCore) { + for (firstCore in cores) break; + $scope.selectedCore = firstCore; + $location.path("/~cores/" + $scope.selectedCore).replace(); + } + $scope.core = cores[$scope.selectedCore]; + $scope.corelist = []; + $scope.swapCorelist = []; + for (var core in cores) { + $scope.corelist.push(cores[core]); + if (cores[core] != $scope.core) { + $scope.swapCorelist.push(cores[core]); + } + } + if ($scope.swapCorelist.length>0) { + $scope.swapOther = $scope.swapCorelist[0].name; + } + }); + }; + $scope.showAddCore = function() { + $scope.hideAll(); + $scope.showAdd = true; + $scope.newCore = { + name: "new_core", + dataDir: "data", + instanceDir: "new_core", + config: "solrconfig.xml", + schema: "schema.xml", + collection: "", + shard: "" + }; + }; + + $scope.addCore = function() { + if (!$scope.newCore.name) { + $scope.addMessage = "Please provide a core name"; + } else if (false) { //@todo detect whether core exists + $scope.AddMessage = "A core with that name already exists"; + } else { + var params = { + name: $scope.newCore.name, + instanceDir: $scope.newCore.instanceDir, + config: $scope.newCore.config, + schema: $scope.newCore.schema, + dataDir: $scope.newCore.dataDir + }; + if ($scope.isCloud) { + params.collection = $scope.newCore.collection; + params.shard = $scope.newCore.shard; + } + Cores.add(params, function(data) { + $location.path("/~cores/" + $scope.newCore.name); + $scope.cancelAddCore(); + }); + } + }; + + $scope.cancelAddCore = function() { + delete $scope.addMessage; + $scope.showAdd = false + }; + + $scope.unloadCore = function() { + var answer = confirm( 'Do you really want to unload Core "' + $scope.selectedCore + '"?' ); + if( !answer ) return; + Cores.unload({core: $scope.selectedCore}, function(data) { + $location.path("/~cores"); + }); + }; + + $scope.showRenameCore = function() { + $scope.hideAll(); + $scope.showRename = true; + }; + + $scope.renameCore = function() { + if (!$scope.other) { + $scope.renameMessage = "Please provide a new name for the " + $scope.selectedCore + " core"; + } else if ($scope.other == $scope.selectedCore) { + $scope.renameMessage = "New name must be different from the current one"; + } else { + Cores.rename({core:$scope.selectedCore, other: $scope.other}, function(data) { + $location.path("/~cores/" + $scope.other); + $scope.cancelRename(); + }); + } + }; + + $scope.cancelRenameCore = function() { + $scope.showRename = false; + delete $scope.renameMessage; + $scope.other = ""; + }; + + $scope.showSwapCores = function() { + $scope.hideAll(); + $scope.showSwap = true; + }; + + $scope.swapCores = function() { + if (!$scope.swapOther) { + $scope.swapMessage = "Please select a core to swap with"; + } else if ($scope.swapOther == $scope.selectedCore) { + $scope.swapMessage = "Cannot swap with the same core"; + } else { + Cores.swap({core: $scope.selectedCore, other: $scope.swapOther}, function(data) { + $location.path("/~cores/" + $scope.swapOther); + delete $scope.swapOther; + $scope.cancelSwapCores(); + }); + } + }; + + $scope.cancelSwapCores = function() { + delete $scope.swapMessage; + $scope.showSwap = false; + } + + $scope.reloadCore = function() { + if ($scope.initFailures[$scope.selectedCore]) { + delete $scope.initFailures[$scope.selectedCore]; + $scope.showInitFailures = Object.keys(data.initFailures).length>0; + } + Cores.reload({core: $scope.selectedCore}, + function(data) { + if (data.error) { + $scope.reloadFailure = true; + $timeout(function() { + $scope.reloadFailure = false; + $route.reload(); + }, 1000); + } else { + $scope.reloadSuccess = true; + $timeout(function () { + $scope.reloadSuccess = false; + $route.reload(); + }, 1000); + } + }); + }; + + $scope.hideAll = function() { + $scope.showRename = false; + $scope.showAdd = false; + $scope.showSwap = false; + }; + + $scope.refresh(); + } +); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js new file mode 100644 index 000000000..c31b6f0fb --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js @@ -0,0 +1,302 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +var dataimport_timeout = 2000; + +solrAdminApp.controller('DataImportController', + function($scope, $rootScope, $routeParams, $location, $timeout, $interval, $cookies, Mbeans, DataImport, Constants) { + $scope.resetMenu("dataimport", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function () { + Mbeans.info({core: $routeParams.core, cat: 'QUERY'}, function (data) { + var mbeans = data['solr-mbeans'][1]; + $scope.handlers = []; + for (var key in mbeans) { + if (mbeans[key]['class'] !== key && mbeans[key]['class'] === 'org.apache.solr.handler.dataimport.DataImportHandler') { + $scope.handlers.push(key); + } + } + $scope.hasHandlers = $scope.handlers.length > 0; + + if (!$routeParams.handler) { + $location.path("/" + $routeParams.core + "/dataimport/" + $scope.handlers[0]); + } else { + $scope.currentHandler = $routeParams.handler; + } + }); + + $scope.handler = $routeParams.handler; + if ($scope.handler && $scope.handler[0]=="/") { + $scope.handler = $scope.handler.substr(1); + } + if ($scope.handler) { + DataImport.config({core: $routeParams.core, name: $scope.handler}, function (data) { + try { + $scope.config = data.config; + var xml = $.parseXML(data.config); + $scope.entities = []; + $('document > entity', xml).each(function (i, element) { + $scope.entities.push($(element).attr('name')); + }); + $scope.refreshStatus(); + } catch (err) { + console.log(err); + } + }); + } + $scope.lastUpdate = "unknown"; + $scope.lastUpdateUTC = ""; + }; + + $scope.toggleDebug = function () { + $scope.isDebugMode = !$scope.isDebugMode; + if ($scope.isDebugMode) { + // also enable Debug checkbox + $scope.form.showDebug = true; + } + $scope.showConfiguration = true; + } + + $scope.toggleConfiguration = function () { + $scope.showConfiguration = !$scope.showConfiguration; + } + + $scope.toggleRawStatus = function () { + $scope.showRawStatus = !$scope.showRawStatus; + } + + $scope.toggleRawDebug = function () { + $scope.showRawDebug = !$scope.showRawDebug; + } + + $scope.reload = function () { + DataImport.reload({core: $routeParams.core, name: $scope.handler}, function () { + $scope.reloaded = true; + $timeout(function () { + $scope.reloaded = false; + }, 5000); + $scope.refresh(); + }); + } + + $scope.form = { + command: "full-import", + verbose: false, + clean: false, + commit: true, + showDebug: false, + custom: "", + core: $routeParams.core + }; + + $scope.submit = function () { + var params = {}; + for (var key in $scope.form) { + if (key == "showDebug") { + if ($scope.form.showDebug) { + params["debug"] = true; + } + } else { + params[key] = $scope.form[key]; + } + } + if (params.custom.length) { + var customParams = $scope.form.custom.split("&"); + for (var i in customParams) { + var parts = customParams[i].split("="); + params[parts[0]] = parts[1]; + } + } + delete params.custom; + + if ($scope.isDebugMode) { + params.dataConfig = $scope.config; + } + + params.core = $routeParams.core; + params.name = $scope.handler; + + DataImport.post(params, function (data) { + $scope.rawResponse = JSON.stringify(data, null, 2); + $scope.refreshStatus(); + }); + }; + + $scope.abort = function () { + $scope.isAborting = true; + DataImport.abort({core: $routeParams.core, name: $scope.handler}, function () { + $timeout(function () { + $scope.isAborting = false; + $scope.refreshStatus(); + }, 4000); + }); + } + + $scope.refreshStatus = function () { + + console.log("Refresh Status"); + + $scope.isStatusLoading = true; + DataImport.status({core: $routeParams.core, name: $scope.handler}, function (data) { + if (data[0] == "<") { + $scope.hasHandlers = false; + return; + } + + var now = new Date(); + $scope.lastUpdate = now.toTimeString().split(' ').shift(); + $scope.lastUpdateUTC = now.toUTCString(); + var messages = data.statusMessages; + var messagesCount = 0; + for( var key in messages ) { messagesCount++; } + + if (data.status == 'busy') { + $scope.status = "indexing"; + + $scope.timeElapsed = data.statusMessages['Time Elapsed']; + $scope.elapsedSeconds = parseSeconds($scope.timeElapsed); + + var info = $scope.timeElapsed ? 'Indexing since ' + $scope.timeElapsed : 'Indexing ...'; + $scope.info = showInfo(messages, true, info, $scope.elapsedSeconds); + + } else if (messages.RolledBack) { + $scope.status = "failure"; + $scope.info = showInfo(messages, true); + } else if (messages.Aborted) { + $scope.status = "aborted"; + $scope.info = showInfo(messages, true, 'Aborting current Import ...'); + } else if (data.status == "idle" && messagesCount != 0) { + $scope.status = "success"; + $scope.info = showInfo(messages, true); + } else { + $scope.status = "idle"; + $scope.info = showInfo(messages, false, 'No information available (idle)'); + } + + delete data.$promise; + delete data.$resolved; + + $scope.rawStatus = JSON.stringify(data, null, 2); + + $scope.isStatusLoading = false; + $scope.statusUpdated = true; + $timeout(function () { + $scope.statusUpdated = false; + }, dataimport_timeout / 2); + }); + }; + + $scope.updateAutoRefresh = function () { + $scope.autorefresh = !$scope.autorefresh; + $cookies.dataimport_autorefresh = $scope.autorefresh ? true : null; + if ($scope.autorefresh) { + $scope.refreshTimeout = $interval($scope.refreshStatus, dataimport_timeout); + var onRouteChangeOff = $scope.$on('$routeChangeStart', function() { + $interval.cancel($scope.refreshTimeout); + onRouteChangeOff(); + }); + + } else if ($scope.refreshTimeout) { + $interval.cancel($scope.refreshTimeout); + } + $scope.refreshStatus(); + }; + + $scope.refresh(); + +}); + +var showInfo = function (messages, showFull, info_text, elapsed_seconds) { + + var info = {}; + if (info_text) { + info.text = info_text; + } else { + info.text = messages[''] || ''; + // format numbers included in status nicely + /* @todo this pretty printing is hard to work out how to do in an Angularesque way: + info.text = info.text.replace(/\d{4,}/g, + function (match, position, string) { + return app.format_number(parseInt(match, 10)); + } + ); + */ + + var time_taken_text = messages['Time taken']; + info.timeTaken = parseSeconds(time_taken_text); + } + info.showDetails = false; + + if (showFull) { + if (!elapsed_seconds) { + var time_taken_text = messages['Time taken']; + elapsed_seconds = parseSeconds(time_taken_text); + } + + info.showDetails = true; + + var document_config = { + 'Requests': 'Total Requests made to DataSource', + 'Fetched': 'Total Rows Fetched', + 'Skipped': 'Total Documents Skipped', + 'Processed': 'Total Documents Processed' + }; + + info.docs = []; + for (var key in document_config) { + var value = parseInt(messages[document_config[key]], 10); + var doc = {desc: document_config[key], name: key, value: value}; + if (elapsed_seconds && key != 'Skipped') { + doc.speed = Math.round(value / elapsed_seconds); + } + info.docs.push(doc); + } + + var dates_config = { + 'Started': 'Full Dump Started', + 'Aborted': 'Aborted', + 'Rolledback': 'Rolledback' + }; + + info.dates = []; + for (var key in dates_config) { + var value = messages[dates_config[key]]; + if (value) { + value = value.replace(" ", "T")+".000Z"; + console.log(value); + var date = {desc: dates_config[key], name: key, value: value}; + info.dates.push(date); + } + } + } + return info; +} + +var parseSeconds = function(time) { + var seconds = 0; + var arr = new String(time || '').split('.'); + var parts = arr[0].split(':').reverse(); + + for (var i = 0; i < parts.length; i++) { + seconds += ( parseInt(parts[i], 10) || 0 ) * Math.pow(60, i); + } + + if (arr[1] && 5 <= parseInt(arr[1][0], 10)) { + seconds++; // treat more or equal than .5 as additional second + } + return seconds; +} diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js new file mode 100644 index 000000000..d38265a05 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js @@ -0,0 +1,137 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +//helper for formatting JSON and others + +var DOC_PLACEHOLDER = '<doc>\n' + + '<field name="id">change.me</field>' + + '<field name="title">change.me</field>' + + '</doc>'; + +var ADD_PLACEHOLDER = '<add>\n' + DOC_PLACEHOLDER + '</add>\n'; + +solrAdminApp.controller('DocumentsController', + function($scope, $rootScope, $routeParams, $location, Luke, Update, FileUpload, Constants) { + $scope.resetMenu("documents", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function () { + Luke.schema({core: $routeParams.core}, function(data) { + //TODO: handle dynamic fields + delete data.schema.fields._version_; + $scope.fields = Object.keys(data.schema.fields); + }); + $scope.document = ""; + $scope.handler = "/update"; + $scope.type = "json"; + $scope.commitWithin = 1000; + $scope.overwrite = true; + }; + + $scope.refresh(); + + $scope.changeDocumentType = function () { + $scope.placeholder = ""; + if ($scope.type == 'json') { + $scope.placeholder = '{"id":"change.me","title":"change.me"}'; + } else if ($scope.type == 'csv') { + $scope.placeholder = "id,title\nchange.me,change.me"; + } else if ($scope.type == 'solr') { + $scope.placeholder = ADD_PLACEHOLDER; + } else if ($scope.type == 'xml') { + $scope.placeholder = DOC_PLACEHOLDER; + } + }; + + $scope.addWizardField = function () { + if ($scope.document == "") $scope.document = "{}"; + var doc = JSON.parse($scope.document); + doc[$scope.fieldName] = $scope.fieldData; + $scope.document = JSON.stringify(doc, null, '\t'); + $scope.fieldData = ""; + }; + + $scope.submit = function () { + var contentType = ""; + var postData = ""; + var params = {}; + var doingFileUpload = false; + + if ($scope.handler[0] == '/') { + params.handler = $scope.handler.substring(1); + } else { + params.handler = 'update'; + params.qt = $scope.handler; + } + + params.commitWithin = $scope.commitWithin; + params.overwrite = $scope.overwrite; + params.core = $routeParams.core; + params.wt = "json"; + + if ($scope.type == "json" || $scope.type == "wizard") { + postData = "[" + $scope.document + "]"; + contentType = "json"; + } else if ($scope.type == "csv") { + postData = $scope.document; + contentType = "csv"; + } else if ($scope.type == "xml") { + postData = "<add>" + $scope.document + "</add>"; + contentType = "xml"; + } else if ($scope.type == "upload") { + doingFileUpload = true; + params.raw = $scope.literalParams; + } else if ($scope.type == "solr") { + postData = $scope.document; + if (postData[0] == "<") { + contentType = "xml"; + } else if (postData[0] == "{" || postData[0] == '[') { + contentType = "json"; + } else { + alert("Cannot identify content type") + } + } + if (!doingFileUpload) { + var callback = function (success) { + $scope.responseStatus = "success"; + delete success.$promise; + delete success.$resolved; + $scope.response = JSON.stringify(success, null, ' '); + }; + var failure = function (failure) { + $scope.responseStatus = failure; + }; + if (contentType == "json") { + Update.postJson(params, postData, callback, failure); + } else if (contentType == "xml") { + Update.postXml(params, postData, callback, failure); + } else if (contentType == "csv") { + Update.postCsv(params, postData, callback, failure); + } + } else { + var file = $scope.fileUpload; + console.log('file is ' + JSON.stringify(file)); + var uploadUrl = "/fileUpload"; + FileUpload.upload(params, $scope.fileUpload, function (success) { + $scope.responseStatus = "success"; + $scope.response = JSON.stringify(success, null, ' '); + }, function (failure) { + $scope.responseStatus = "failure"; + $scope.response = JSON.stringify(failure, null, ' '); + }); + } + } + }); + diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js new file mode 100644 index 000000000..c1d7abfe2 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js @@ -0,0 +1,100 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +var contentTypeMap = { xml : 'text/xml', html : 'text/html', js : 'text/javascript', json : 'application/json', 'css' : 'text/css' }; +var languages = {js: "javascript", xml:"xml", xsl:"xml", vm: "xml", html: "xml", json: "json", css: "css"}; + +solrAdminApp.controller('FilesController', + function($scope, $rootScope, $routeParams, $location, Files, Constants) { + $scope.resetMenu("files", Constants.IS_COLLECTION_PAGE); + + $scope.file = $location.search().file; + $scope.content = null; + + $scope.baseurl = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // Including /solr/ context + + $scope.refresh = function () { + + var process = function (path, tree) { + var params = {core: $routeParams.core}; + if (path.slice(-1) == '/') { + params.file = path.slice(0, -1); + } else if (path!='') { + params.file = path; + } + + Files.list(params, function (data) { + var filenames = Object.keys(data.files); + filenames.sort(); + for (var i in filenames) { + var file = filenames[i]; + var filedata = data.files[file]; + var state = undefined; + var children = undefined; + + if (filedata.directory) { + file = file + "/"; + if ($scope.file && $scope.file.indexOf(path + file) == 0) { + state = "open"; + } else { + state = "closed"; + } + children = []; + process(path + file, children); + } + tree.push({ + data: { + title: file, + attr: { id: path + file} + }, + children: children, + state: state + }); + } + }); + } + $scope.tree = []; + process("", $scope.tree); + + if ($scope.file && $scope.file != '' && $scope.file.split('').pop()!='/') { + var extension; + if ($scope.file == "managed-schema") { + extension = contentTypeMap['xml']; + } else { + extension = $scope.file.match( /\.(\w+)$/)[1] || ''; + } + var contentType = (contentTypeMap[extension] || 'text/plain' ) + ';charset=utf-8'; + + Files.get({core: $routeParams.core, file: $scope.file, contentType: contentType}, function(data) { + $scope.content = data.data; + $scope.url = data.config.url + "?" + $.param(data.config.params); // relative URL + if (contentType.indexOf("text/plain") && (data.data.indexOf("<?xml")>=0) || data.data.indexOf("<!--")>=0) { + $scope.lang = "xml"; + } else { + $scope.lang = languages[extension] || "txt"; + } + }); + } + }; + + $scope.showTreeLink = function(data) { + var file = data.args[0].id; + $location.search({file:file}); + }; + + $scope.refresh(); + }); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js new file mode 100644 index 000000000..f7ce5a8d2 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js @@ -0,0 +1,97 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +solrAdminApp.controller('IndexController', function($scope, System, Cores, Constants) { + $scope.resetMenu("index", Constants.IS_ROOT_PAGE); + $scope.reload = function() { + System.get(function(data) { + $scope.system = data; + + // load average + var load_average = ( data.system.uptime || '' ).match( /load averages?: (\d+[.,]\d\d),? (\d+[.,]\d\d),? (\d+[.,]\d\d)/ ); + if (load_average) { + for (var i=0;i<2;i++) { + load_average[i]=load_average[i].replace(",","."); // for European users + } + $scope.load_average = load_average.slice(1); + } + + // physical memory + var memoryMax = parse_memory_value(data.system.totalPhysicalMemorySize); + $scope.memoryTotal = parse_memory_value(data.system.totalPhysicalMemorySize - data.system.freePhysicalMemorySize); + $scope.memoryPercentage = ($scope.memoryTotal / memoryMax * 100).toFixed(1)+ "%"; + $scope.memoryMax = pretty_print_bytes(memoryMax); + $scope.memoryTotalDisplay = pretty_print_bytes($scope.memoryTotal); + + // swap space + var swapMax = parse_memory_value(data.system.totalSwapSpaceSize); + $scope.swapTotal = parse_memory_value(data.system.totalSwapSpaceSize - data.system.freeSwapSpaceSize); + $scope.swapPercentage = ($scope.swapTotal / swapMax * 100).toFixed(1)+ "%"; + $scope.swapMax = pretty_print_bytes(swapMax); + $scope.swapTotalDisplay = pretty_print_bytes($scope.swapTotal); + + // file handles + $scope.fileDescriptorPercentage = (data.system.openFileDescriptorCount / data.system.maxFileDescriptorCount *100).toFixed(1) + "%"; + + // java memory + var javaMemoryMax = parse_memory_value(data.jvm.memory.raw.max || data.jvm.memory.max); + $scope.javaMemoryTotal = parse_memory_value(data.jvm.memory.raw.total || data.jvm.memory.total); + $scope.javaMemoryUsed = parse_memory_value(data.jvm.memory.raw.used || data.jvm.memory.used); + $scope.javaMemoryTotalPercentage = ($scope.javaMemoryTotal / javaMemoryMax *100).toFixed(1) + "%"; + $scope.javaMemoryUsedPercentage = ($scope.javaMemoryUsed / $scope.javaMemoryTotal *100).toFixed(1) + "%"; + $scope.javaMemoryPercentage = ($scope.javaMemoryUsed / javaMemoryMax * 100).toFixed(1) + "%"; + $scope.javaMemoryTotalDisplay = pretty_print_bytes($scope.javaMemoryTotal); + $scope.javaMemoryUsedDisplay = pretty_print_bytes($scope.javaMemoryUsed); // @todo These should really be an AngularJS Filter: {{ javaMemoryUsed | bytes }} + $scope.javaMemoryMax = pretty_print_bytes(javaMemoryMax); + + // no info bar: + $scope.noInfo = !( + data.system.totalPhysicalMemorySize && data.system.freePhysicalMemorySize && + data.system.totalSwapSpaceSize && data.system.freeSwapSpaceSize && + data.system.openFileDescriptorCount && data.system.maxFileDescriptorCount); + + // command line args: + $scope.commandLineArgs = data.jvm.jmx.commandLineArgs.sort(); + }); + }; + $scope.reload(); +}); + +var parse_memory_value = function( value ) { + if( value !== Number( value ) ) + { + var units = 'BKMGTPEZY'; + var match = value.match( /^(\d+([,\.]\d+)?) (\w).*$/ ); + var value = parseFloat( match[1] ) * Math.pow( 1024, units.indexOf( match[3].toUpperCase() ) ); + } + + return value; +}; + +var pretty_print_bytes = function(byte_value) { + var unit = null; + + byte_value /= 1024; + byte_value /= 1024; + unit = 'MB'; + + if( 1024 <= byte_value ) { + byte_value /= 1024; + unit = 'GB'; + } + return byte_value.toFixed( 2 ) + ' ' + unit; +}; diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js new file mode 100644 index 000000000..001edfb1a --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js @@ -0,0 +1,45 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('JavaPropertiesController', + function($scope, Properties, Constants){ + $scope.resetMenu("java-props", Constants.IS_ROOT_PAGE); + $scope.refresh = function() { + Properties.get(function(data) { + var sysprops = data["system.properties"]; + var sep = sysprops["path.separator"] + var props = []; + for (var key in sysprops) { + var value = sysprops[key]; + var key = key.replace(/\./g, '.​'); + if (key.indexOf(".path")!=-1 || key.indexOf(".dirs")) { + var values = []; + var parts = value.split(sep); + for (var i in parts) { + values.push({pos:i, value:parts[i]}) + } + props.push({name: key, values: values}); + } else { + props.push({name: key, values: [value]}); + } + } + $scope.props = props; + }); + }; + + $scope.refresh(); + }); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js new file mode 100644 index 000000000..585c90a21 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js @@ -0,0 +1,158 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +var format_time_content = function( time, timeZone ) { + var format_time_options = {}; + if (timeZone && timeZone!="Local") { + format_time_options.timeZone = timeZone; + } + return time.toLocaleString( undefined, format_time_options ); +} + +solrAdminApp.controller('LoggingController', + function($scope, $timeout, $cookies, Logging, Constants){ + $scope.resetMenu("logging", Constants.IS_ROOT_PAGE); + $scope.timezone = $cookies.logging_timezone || "Local"; + $scope.refresh = function() { + Logging.events(function(data) { + $scope.since = new Date(); + $scope.sinceDisplay = format_time_content($scope.since, "Local"); + var events = data.history.docs; + for (var i=0; i<events.length; i++) { + var event = events[i]; + var time = new Date(event.time); + event.local_time = format_time_content(time, "Local"); + event.utc_time = format_time_content(time, "UTC"); + event.loggerBase = event.logger.split( '.' ).pop(); + + if( !event.trace ) { + var lines = event.message.split( "\n" ); + if( lines.length > 1) { + event.trace = event.message; + event.message = lines[0]; + } + } + event.message = event.message.replace(/,/g, ',​'); + event.showTrace = false; + } + $scope.events = events; + $scope.watcher = data.watcher; + /* @todo sticky_mode + // state element is in viewport + sticky_mode = ( state.position().top <= $( window ).scrollTop() + $( window ).height() - ( $( 'body' ).height() - state.position().top ) ); + // initial request + if( 0 === since ) { + sticky_mode = true; + } + $scope.loggingEvents = events; + + if( sticky_mode ) + { + $( 'body' ) + .animate + ( + { scrollTop: state.position().top }, + 1000 + ); + } + */ + }); + $scope.timeout = $timeout($scope.refresh, 10000); + var onRouteChangeOff = $scope.$on('$routeChangeStart', function() { + $timeout.cancel($scope.timeout); + onRouteChangeOff(); + }); + }; + $scope.refresh(); + $scope.toggleRefresh = function() { + if(!$scope.stopped) { + $scope.stopped = true; + $timeout.cancel($scope.timeout); + } else { + $scope.stopped = false; + $scope.timeout = $timeout($scope.refresh, 10000); + } + }; + $scope.toggleTimezone = function() { + $scope.timezone = ($scope.timezone=="Local") ? "UTC":"Local"; + $cookies.logging_timezone = $scope.timezone; + } + $scope.toggleRow = function(event) { + event.showTrace =! event.showTrace; + }; + } +) + +.controller('LoggingLevelController', + function($scope, Logging) { + $scope.resetMenu("logging-levels"); + + var packageOf = function(logger) { + var parts = logger.name.split("."); + return !parts.pop() ? "" : parts.join("."); + }; + + var shortNameOf = function(logger) {return logger.name.split(".").pop();} + + var makeTree = function(loggers, packag) { + var tree = []; + for (var i=0; i<loggers.length; i++) { + var logger = loggers[i]; + logger.packag = packageOf(logger); + logger.short = shortNameOf(logger); + if (logger.packag == packag) { + logger.children = makeTree(loggers, logger.name); + tree.push(logger); + } + } + return tree; + }; + + $scope.refresh = function() { + Logging.levels(function(data) { + $scope.logging = makeTree(data.loggers, ""); + $scope.watcher = data.watcher; + $scope.levels = []; + for (level in data.levels) { + $scope.levels.push({name:data.levels[level], pos:level}); + } + }); + }; + + $scope.toggleOptions = function(logger) { + if (logger.showOptions) { + logger.showOptions = false; + delete $scope.currentLogger; + } else { + if ($scope.currentLogger) { + $scope.currentLogger.showOptions = false; + } + logger.showOptions = true; + $scope.currentLogger = logger; + } + }; + + $scope.setLevel = function(logger, newLevel) { + var setString = logger.name + ":" + newLevel; + logger.showOptions = false; + Logging.setLevel({set: setString}, function(data) { + $scope.refresh(); + }); + }; + + $scope.refresh(); + }); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js new file mode 100644 index 000000000..8127c6fda --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js @@ -0,0 +1,317 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('LoginController', + ['$scope', '$routeParams', '$rootScope', '$location', '$window', 'AuthenticationService', 'Constants', + function ($scope, $routeParams, $rootScope, $location, $window, AuthenticationService, Constants) { + $scope.resetMenu("login", Constants.IS_ROOT_PAGE); + $scope.subPath = $routeParams.route; + $rootScope.exceptions = {}; + + // Session variables set in app.js 401 interceptor + var wwwAuthHeader = sessionStorage.getItem("auth.wwwAuthHeader"); + var authScheme = sessionStorage.getItem("auth.scheme"); + if (wwwAuthHeader) { + // Parse www-authenticate header + var wwwHeader = wwwAuthHeader.match(/(\w+)(\s+)?(.*)/); + authScheme = "unknown"; + var authParams = {}; + if (wwwHeader && wwwHeader.length >= 1) + authScheme = wwwHeader[1]; + if (wwwHeader && wwwHeader.length >= 3) + authParams = www_auth_parse_params(wwwHeader[3]); + if (typeof authParams === 'string' || authParams instanceof String) { + $scope.authParamsError = authParams; + } else { + $scope.authParamsError = undefined; + } + var realm = authParams['realm']; + sessionStorage.setItem("auth.realm", realm); + if (authScheme === 'Basic' || authScheme === 'xBasic') { + authScheme = 'Basic'; + } + sessionStorage.setItem("auth.scheme", authScheme); + } + + var supportedSchemes = ['Basic', 'Bearer', 'Negotiate']; + $scope.authSchemeSupported = supportedSchemes.includes(authScheme); + + if (authScheme === 'Bearer') { + // Check for OpenId redirect response + var errorText = ""; + $scope.isCallback = false; + if ($scope.subPath === 'callback') { + $scope.isCallback = true; + var hash = $location.hash(); + var hp = AuthenticationService.decodeHashParams(hash); + var expectedState = sessionStorage.getItem("auth.stateRandom") + "_" + sessionStorage.getItem("auth.location"); + sessionStorage.setItem("auth.state", "error"); + if (hp['access_token'] && hp['token_type'] && hp['state']) { + // Validate state + if (hp['state'] !== expectedState) { + $scope.error = "Problem with auth callback"; + console.log("Expected state param " + expectedState + " but got " + hp['state']); + errorText += "Invalid values in state parameter. "; + } + // Validate token type + if (hp['token_type'].toLowerCase() !== "bearer") { + console.log("Expected token_type param 'bearer', but got " + hp['token_type']); + errorText += "Invalid values in token_type parameter. "; + } + // Unpack ID token and validate nonce, get username + if (hp['id_token']) { + var idToken = hp['id_token'].split("."); + if (idToken.length === 3) { + var payload = AuthenticationService.decodeJwtPart(idToken[1]); + if (!payload['nonce'] || payload['nonce'] !== sessionStorage.getItem("auth.nonce")) { + errorText += "Invalid 'nonce' value, possible attack detected. Please log in again. "; + } + + if (errorText === "") { + sessionStorage.setItem("auth.username", payload['sub']); + sessionStorage.setItem("auth.header", "Bearer " + hp['access_token']); + sessionStorage.removeItem("auth.statusText"); + sessionStorage.removeItem("auth.stateRandom"); + sessionStorage.removeItem("auth.wwwAuthHeader"); + console.log("User " + payload['sub'] + " is logged in"); + var redirectTo = sessionStorage.getItem("auth.location"); + console.log("Redirecting to stored location " + redirectTo); + sessionStorage.setItem("auth.state", "authenticated"); + sessionStorage.removeItem("http401"); + $location.path(redirectTo).hash(""); + } + } else { + console.log("Expected JWT compact id_token param but got " + idToken); + errorText += "Invalid values in id_token parameter. "; + } + } else { + console.log("Callback was missing the id_token parameter, could not validate nonce."); + errorText += "Callback was missing the id_token parameter, could not validate nonce. "; + } + if (hp['access_token'].split(".").length !== 3) { + console.log("Expected JWT compact access_token param but got " + hp['access_token']); + errorText += "Invalid values in access_token parameter. "; + } + if (errorText !== "") { + $scope.error = "Problems with OpenID callback"; + $scope.errorDescription = errorText; + $scope.http401 = "true"; + } + // End callback processing + } else if (hp['error']) { + // The callback had errors + console.log("Error received from idp: " + hp['error']); + var errorDescriptions = {}; + errorDescriptions['invalid_request'] = "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed."; + errorDescriptions['unauthorized_client'] = "The client is not authorized to request an access token using this method."; + errorDescriptions['access_denied'] = "The resource owner or authorization server denied the request."; + errorDescriptions['unsupported_response_type'] = "The authorization server does not support obtaining an access token using this method."; + errorDescriptions['invalid_scope'] = "The requested scope is invalid, unknown, or malformed."; + errorDescriptions['server_error'] = "The authorization server encountered an unexpected condition that prevented it from fulfilling the request."; + errorDescriptions['temporarily_unavailable'] = "The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server."; + $scope.error = "Callback from Id Provider contained error. "; + if (hp['error_description']) { + $scope.errorDescription = decodeURIComponent(hp['error_description']); + } else { + $scope.errorDescription = errorDescriptions[hp['error']]; + } + if (hp['error_uri']) { + $scope.errorDescription += " More information at " + hp['error_uri'] + ". "; + } + if (hp['state'] !== expectedState) { + $scope.errorDescription += "The state parameter returned from ID Provider did not match the one we sent."; + } + sessionStorage.setItem("auth.state", "error"); + } + } + } + + if (errorText === "" && !$scope.error && authParams) { + $scope.error = authParams['error']; + $scope.errorDescription = authParams['error_description']; + $scope.authData = AuthenticationService.getAuthDataHeader(); + } + + $scope.authScheme = sessionStorage.getItem("auth.scheme"); + $scope.authRealm = sessionStorage.getItem("auth.realm"); + $scope.wwwAuthHeader = sessionStorage.getItem("auth.wwwAuthHeader"); + $scope.statusText = sessionStorage.getItem("auth.statusText"); + $scope.authLocation = sessionStorage.getItem("auth.location"); + $scope.authLoggedinUser = sessionStorage.getItem("auth.username"); + $scope.authHeader = sessionStorage.getItem("auth.header"); + + $scope.login = function () { + AuthenticationService.SetCredentials($scope.username, $scope.password); + $location.path($scope.authLocation); // Redirect to the location that caused the login prompt + }; + + $scope.logout = function() { + // reset login status + AuthenticationService.ClearCredentials(); + $location.path("/"); + }; + + $scope.jwtLogin = function () { + var stateRandom = Math.random().toString(36).substr(2); + sessionStorage.setItem("auth.stateRandom", stateRandom); + var authState = stateRandom + "_" + sessionStorage.getItem("auth.location"); + var authNonce = Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2); + sessionStorage.setItem("auth.nonce", authNonce); + var params = { + "response_type" : "id_token token", + "client_id" : $scope.authData['client_id'], + "redirect_uri" : $window.location.href.split('#')[0], + "scope" : "openid " + $scope.authData['scope'], + "state" : authState, + "nonce" : authNonce + }; + + var endpointBaseUrl = $scope.authData['authorizationEndpoint']; + var loc = endpointBaseUrl + "?" + paramsToString(params); + console.log("Redirecting to " + loc); + sessionStorage.setItem("auth.state", "expectCallback"); + $window.location.href = loc; + + function paramsToString(params) { + var arr = []; + for (var p in params) { + if( params.hasOwnProperty(p) ) { + arr.push(p + "=" + encodeURIComponent(params[p])); + } + } + return arr.join("&"); + } + }; + + $scope.jwtIsLoginNode = function() { + var redirect = $scope.authData ? $scope.authData['redirect_uris'] : undefined; + if (redirect && Array.isArray(redirect) && redirect.length > 0) { + var isLoginNode = false; + redirect.forEach(function(uri) { // Check that current node URL is among the configured callback URIs + if ($window.location.href.startsWith(uri)) isLoginNode = true; + }); + return isLoginNode; + } else { + return true; // no redirect UIRs configured, all nodes are potential login nodes + } + }; + + $scope.jwtFindLoginNode = function() { + var redirect = $scope.authData ? $scope.authData['redirect_uris'] : undefined; + if (redirect && Array.isArray(redirect) && redirect.length > 0) { + var loginNode = redirect[0]; + redirect.forEach(function(uri) { // if current node is in list, return its callback uri + if ($window.location.href.startsWith(uri)) loginNode = uri; + }); + return loginNode; + } else { + return $window.location.href.split('#')[0]; // Return base url of current URL as the url to use + } + }; + + // Redirect to login node if this is not a valid one + $scope.jwtGotoLoginNode = function() { + if (!$scope.jwtIsLoginNode()) { + $window.location.href = $scope.jwtFindLoginNode(); + } + }; + + $scope.jwtLogout = function() { + // reset login status + AuthenticationService.ClearCredentials(); + $location.path("/"); + }; + + $scope.isLoggedIn = function() { + return (sessionStorage.getItem("auth.username") !== null); + }; + }]); + +// This function is copied and adapted from MIT-licensed https://github.com/randymized/www-authenticate/blob/master/lib/parsers.js +www_auth_parse_params= function (header) { + // This parser will definitely fail if there is more than one challenge + var params = {}; + var tok, last_tok, _i, _len, key, value; + var state= 0; //0: token, + var m= header.split(/([",=])/); + for (_i = 0, _len = m.length; _i < _len; _i++) { + last_tok= tok; + tok = m[_i]; + if (!tok.length) continue; + switch (state) { + case 0: // token + key= tok.trim(); + state= 1; // expect equals + continue; + case 1: // expect equals + if ('=' != tok) return 'Equal sign was expected after '+key; + state= 2; + continue; + case 2: // expect value + if ('"' == tok) { + value= ''; + state= 3; // expect quoted + continue; + } + else { + params[key]= value= tok.trim(); + state= 9; // expect comma or end + continue; + } + case 3: // handling quoted string + if ('"' == tok) { + state= 8; // end quoted + continue; + } + else { + value+= tok; + state= 3; // continue accumulating quoted string + continue; + } + case 8: // end quote encountered + if ('"' == tok) { + // double quoted + value+= '"'; + state= 3; // back to quoted string + continue; + } + if (',' == tok) { + params[key]= value; + state= 0; + continue; + } + else { + return 'Unexpected token ('+tok+') after '+value+'"'; + } + continue; + case 9: // expect commma + if (',' != tok) return 'Comma expected after '+value; + state= 0; + continue; + } + } + switch (state) { // terminal state + case 0: // Empty or ignoring terminal comma + case 9: // Expecting comma or end of header + return params; + case 8: // Last token was end quote + params[key]= value; + return params; + default: + return 'Unexpected end of www-authenticate value.'; + } +}; diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js new file mode 100644 index 000000000..a537b37d7 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js @@ -0,0 +1,167 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('PluginsController', + function($scope, $rootScope, $routeParams, $location, Mbeans, Constants) { + $scope.resetMenu("plugins", Constants.IS_CORE_PAGE); + + if ($routeParams.legacytype) { + // support legacy URLs. Angular cannot change #path without reloading controller + $location.path("/"+$routeParams.core+"/plugins"); + $location.search("type", $routeParams.legacytype); + return; + } + + $scope.refresh = function() { + Mbeans.stats({core: $routeParams.core}, function (data) { + var type = $location.search().type; + $scope.types = getPluginTypes(data, type); + $scope.type = getSelectedType($scope.types, type); + + if ($scope.type && $routeParams.entry) { + $scope.plugins = $routeParams.entry.split(","); + openPlugins($scope.type, $scope.plugins); + } else { + $scope.plugins = []; + } + }); + }; + + $scope.selectPluginType = function(type) { + $location.search({entry:null, type: type.lower}); + $scope.type = type; + }; + + $scope.selectPlugin = function(plugin) { + plugin.open = !plugin.open; + + if (plugin.open) { + $scope.plugins.push(plugin.name); + } else { + $scope.plugins.splice($scope.plugins.indexOf(plugin.name), 1); + } + + if ($scope.plugins.length==0) { + $location.search("entry", null); + } else { + $location.search("entry", $scope.plugins.join(',')); + } + } + + $scope.startRecording = function() { + $scope.isRecording = true; + Mbeans.reference({core: $routeParams.core}, function(data) { + $scope.reference = data.reference; + console.log($scope.reference); + }) + } + + $scope.stopRecording = function() { + $scope.isRecording = false; + console.log($scope.reference); + Mbeans.delta({core: $routeParams.core}, $scope.reference, function(data) { + parseDelta($scope.types, data); + }); + } + + $scope.refresh(); + }); + +var getPluginTypes = function(data, selected) { + var keys = []; + var mbeans = data["solr-mbeans"]; + for (var i=0; i<mbeans.length; i+=2) { + var key = mbeans[i]; + var lower = key.toLowerCase(); + var plugins = getPlugins(mbeans[i+1]); + if (plugins.length == 0) continue; + keys.push({name: key, + selected: lower == selected, + changes: 0, + lower: lower, + plugins: plugins + }); + } + keys.sort(function(a,b) {return a.name > b.name}); + return keys; +}; + +var getPlugins = function(data) { + var plugins = []; + for (var key in data) { + var pluginProperties = data[key]; + var stats = pluginProperties.stats; + delete pluginProperties.stats; + for (var stat in stats) { + // add breaking space after a bracket or @ to handle wrap long lines: + stats[stat] = new String(stats[stat]).replace( /([\(@])/g, '$1​'); + } + plugin = {name: key, changed: false, stats: stats, open:false}; + plugin.properties = pluginProperties; + plugins.push(plugin); + } + plugins.sort(function(a,b) {return a.name > b.name}); + return plugins; +}; + +var getSelectedType = function(types, selected) { + if (selected) { + for (var i in types) { + if (types[i].lower == selected) { + return types[i]; + } + } + } +}; + +var parseDelta = function(types, data) { + + var getByName = function(list, name) { + for (var i in list) { + if (list[i].name == name) return list[i]; + } + } + + var mbeans = data["solr-mbeans"] + for (var i=0; i<mbeans.length; i+=2) { + var typeName = mbeans[i]; + var type = getByName(types, typeName); + var plugins = mbeans[i+1]; + for (var key in plugins) { + var changedPlugin = plugins[key]; + if (changedPlugin._changed_) { + var plugin = getByName(type.plugins, key); + var stats = changedPlugin.stats; + delete changedPlugin.stats; + plugin.properties = changedPlugin; + for (var stat in stats) { + // add breaking space after a bracket or @ to handle wrap long lines: + plugin.stats[stat] = new String(stats[stat]).replace( /([\(@])/g, '$1​'); + } + plugin.changed = true; + type.changes++; + } + } + } +}; + +var openPlugins = function(type, selected) { + for (var i in type.plugins) { + var plugin = type.plugins[i]; + plugin.open = selected.indexOf(plugin.name)>=0; + } +} diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js new file mode 100644 index 000000000..e8a38707a --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js @@ -0,0 +1,120 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('QueryController', + function($scope, $routeParams, $location, Query, Constants){ + $scope.resetMenu("query", Constants.IS_COLLECTION_PAGE); + + // @todo read URL parameters into scope + $scope.query = {q:'*:*'}; + $scope.filters = [{fq:""}]; + $scope.dismax = {defType: "dismax"}; + $scope.edismax = {defType: "edismax", stopwords: true, lowercaseOperators: false}; + $scope.hl = {hl:"on"}; + $scope.facet = {facet: "on"}; + $scope.spatial = {}; + $scope.spellcheck = {spellcheck:"on"}; + $scope.debugQuery = {debugQuery: "on"}; + $scope.qt = "/select"; + + $scope.doQuery = function() { + var params = {}; + + var set = function(key, value) { + if (params[key]) { + params[key].push(value); + } else { + params[key] = [value]; + } + } + var copy = function(params, query) { + for (var key in query) { + terms = query[key]; + // Booleans have no length property - only set them if true + if (((typeof(terms) == typeof(true) && terms) || terms.length > 0) && key[0]!="$") { + set(key, terms); + } + } + }; + + copy(params, $scope.query); + + if ($scope.isDismax) copy(params, $scope.dismax); + if ($scope.isEdismax) copy(params, $scope.edismax); + if ($scope.isHighlight) copy(params, $scope.hl); + if ($scope.isFacet) copy(params, $scope.facet); + if ($scope.isSpatial) copy(params, $scope.spatial); + if ($scope.isSpellcheck) copy(params, $scope.spellcheck); + if ($scope.isDebugQuery) copy(params, $scope.debugQuery); + + if ($scope.rawParams) { + var rawParams = $scope.rawParams.split(/[&\n]/); + for (var i in rawParams) { + var param = rawParams[i]; + var equalPos = param.indexOf("="); + if (equalPos > -1) { + set(param.substring(0, equalPos), param.substring(equalPos+1)); + } else { + set(param, ""); // Use empty value for params without "=" + } + } + } + + var qt = $scope.qt ? $scope.qt : "/select"; + + for (var filter in $scope.filters) { + copy(params, $scope.filters[filter]); + } + + params.core = $routeParams.core; + if (qt[0] == '/') { + params.handler = qt.substring(1); + } else { // Support legacy style handleSelect=true configs + params.handler = "select"; + set("qt", qt); + } + var url = Query.url(params); + Query.query(params, function(data) { + $scope.lang = $scope.query.wt; + if ($scope.lang == undefined || $scope.lang == '') { + $scope.lang = "json"; + } + $scope.response = data; + // Use relative URL to make it also work through proxies that may have a different host/port/context + $scope.url = url; + $scope.hostPortContext = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // For display only + }); + }; + + if ($location.search().q) { + $scope.query.q = $location.search()["q"]; + $scope.doQuery(); + } + + $scope.removeFilter = function(index) { + if ($scope.filters.length === 1) { + $scope.filters = [{fq: ""}]; + } else { + $scope.filters.splice(index, 1); + } + }; + + $scope.addFilter = function(index) { + $scope.filters.splice(index+1, 0, {fq:""}); + }; + } +); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js new file mode 100644 index 000000000..9f7ac3e41 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js @@ -0,0 +1,235 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('ReplicationController', + function($scope, $rootScope, $routeParams, $interval, $timeout, Replication, Constants) { + $scope.resetMenu("replication", Constants.IS_CORE_PAGE); + + $scope.iterationCount = 1; + + $scope.refresh = function() { + Replication.details({core:$routeParams.core}, function(response) { + var timeout; + var interval; + if ($scope.interval) $interval.cancel($scope.interval); + $scope.isSlave = (response.details.isSlave === 'true'); + if ($scope.isSlave) { + $scope.progress = getProgressDetails(response.details.slave); + $scope.iterations = getIterations(response.details.slave); + $scope.versions = getSlaveVersions(response.details); + $scope.settings = getSlaveSettings(response.details); + if ($scope.settings.isReplicating) { + timeout = $timeout($scope.refresh, 1000); + } else if(!$scope.settings.isPollingDisabled && $scope.settings.pollInterval) { + interval = $scope.interval = $interval(function() { + $scope.settings.tick--; + }, 1000, $scope.settings.tick); + timeout = $timeout($scope.refresh, 1000*(1+$scope.settings.tick)); + } + } else { + $scope.versions = getMasterVersions(response.details); + } + $scope.master = getMasterSettings(response.details, $scope.isSlave); + + var onRouteChangeOff = $scope.$on('$routeChangeStart', function() { + if (interval) $interval.cancel(interval); + if (timeout) $timeout.cancel(timeout); + onRouteChangeOff(); + }); + }); + + }; + + $scope.execute = function(command) { + Replication.command({core:$routeParams.core, command:command}, function(data){$scope.refresh()}); + } + + $scope.showIterations = function() { $scope.iterationCount = 100000}; // limitTo should accept undefined, but doesn't work. + $scope.hideIterations = function() { $scope.iterationCount = 1}; + + $scope.refresh(); + }); + +var getProgressDetails = function(progress) { + + progress.timeRemaining = parseSeconds(progress.timeRemaining); + progress.totalPercent = parseInt(progress.totalPercent); + if (progress.totalPercent === 0) { + progress.totalPercentWidth = "1px"; + } else { + progress.totalPercentWidth = progress.totalPercent + "%"; + } + progress.currentFileSizePercent = parseInt(progress.currentFileSizePercent); + + if (!progress.indexReplicatedAtList) { + progress.indexReplicatedAtList = []; + } + + if (!progress.replicationFailedAtList) { + progress.replicationFailedAtList = []; + } + return progress; +}; + +var getIterations = function(slave) { + + var iterations = []; + + var find = function(list, date) { + return list.filter(function(e) {return e.date == date}); + }; + + for (var i in slave.indexReplicatedAtList) { + var date = slave.indexReplicatedAtList[i]; + var iteration = {date:date, status:"replicated", latest: false}; + if (date == slave.indexReplicatedAt) { + iteration.latest = true; + } + iterations.push(iteration); + } + + for (var i in slave.replicationFailedAtList) { + var failedDate = slave.replicationFailedAtList[i]; + var matchingIterations = find(iterations, failedDate); + if (matchingIterations[0]) { + iteration = matchingIterations[0]; + iteration.status = "failed"; + } else { + iteration = {date: failedDate, status:"failed", latest:false}; + iterations.push(iteration); + } + if (failedDate == slave.replicationFailedAt) { + iteration.latest = true; + } + } + iterations.sort(function(a,b){ return a.date> b.date;}).reverse(); + return iterations; +}; + +var getMasterVersions = function(data) { + versions = {masterSearch:{}, master:{}}; + + versions.masterSearch.version = data.indexVersion; + versions.masterSearch.generation = data.generation; + versions.masterSearch.size = data.indexSize; + + versions.master.version = data.master.replicableVersion || '-'; + versions.master.generation = data.master.replicableGeneration || '-'; + versions.master.size = '-'; + + return versions; +}; + +var getSlaveVersions = function(data) { + versions = {masterSearch: {}, master: {}, slave: {}}; + + versions.slave.version = data.indexVersion; + versions.slave.generation = data.generation; + versions.slave.size = data.indexSize; + + versions.master.version = data.slave.masterDetails.replicableVersion || '-'; + versions.master.generation = data.slave.masterDetails.replicableGeneration || '-'; + versions.master.size = '-'; + + versions.masterSearch.version = data.slave.masterDetails.indexVersion; + versions.masterSearch.generation = data.slave.masterDetails.generation; + versions.masterSearch.size = data.slave.masterDetails.indexSize; + + versions.changedVersion = data.indexVersion !== data.slave.masterDetails.indexVersion; + versions.changedGeneration = data.generation !== data.slave.masterDetails.generation; + + return versions; +}; + +var parseDateToEpoch = function(date) { + // ["Sat Mar 03 11:00:00 CET 2012", "Sat", "Mar", "03", "11:00:00", "CET", "2012"] + var parts = date.match( /^(\w+)\s+(\w+)\s+(\d+)\s+(\d+\:\d+\:\d+)\s+(\w+)\s+(\d+)$/ ); + + // "Sat Mar 03 2012 10:37:33" + var d = new Date( parts[1] + ' ' + parts[2] + ' ' + parts[3] + ' ' + parts[6] + ' ' + parts[4] ); + return d.getTime(); +} + +var parseSeconds = function(time) { + var seconds = 0; + var arr = new String(time || '').split('.'); + var parts = arr[0].split(':').reverse(); + + for (var i = 0; i < parts.length; i++) { + seconds += ( parseInt(parts[i], 10) || 0 ) * Math.pow(60, i); + } + + if (arr[1] && 5 <= parseInt(arr[1][0], 10)) { + seconds++; // treat more or equal than .5 as additional second + + } + + return seconds; +} + +var getSlaveSettings = function(data) { + var settings = {}; + settings.masterUrl = data.slave.masterUrl; + settings.isPollingDisabled = data.slave.isPollingDisabled == 'true'; + settings.pollInterval = data.slave.pollInterval; + settings.isReplicating = data.slave.isReplicating == 'true'; + settings.nextExecutionAt = data.slave.nextExecutionAt; + + if(settings.isReplicating) { + settings.isApprox = true; + settings.tick = parseSeconds(settings.pollInterval); + } else if (!settings.isPollingDisabled && settings.pollInterval) { + if( settings.nextExecutionAt ) { + settings.nextExecutionAtEpoch = parseDateToEpoch(settings.nextExecutionAt); + settings.currentTime = parseDateToEpoch(data.slave.currentDate); + + if( settings.nextExecutionAtEpoch > settings.currentTime) { + settings.isApprox = false; + settings.tick = ( settings.nextExecutionAtEpoch - settings.currentTime) / 1000; + } + } + } + return settings; +}; + +var getMasterSettings = function(details, isSlave) { + var master = {}; + var masterData = isSlave ? details.slave.masterDetails.master : details.master; + master.replicationEnabled = masterData.replicationEnabled == "true"; + master.replicateAfter = masterData.replicateAfter.join(", "); + + if (masterData.confFiles) { + master.files = []; + var confFiles = masterData.confFiles.split(','); + for (var i=0; i<confFiles.length; i++) { + var file = confFiles[i]; + var short = file; + var title = file; + if (file.indexOf(":")>=0) { + title = file.replace(':', ' ยป '); + var parts = file.split(':'); + if (isSlave) { + short = parts[1]; + } else { + short = parts[0]; + } + } + master.files.push({title:title, name:short}); + } + } + return master; +} diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js new file mode 100644 index 000000000..8a80d4297 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js @@ -0,0 +1,611 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +var cookie_schema_browser_autoload = 'schema-browser_autoload'; + +solrAdminApp.controller('SchemaController', + function($scope, $routeParams, $location, $cookies, $timeout, Luke, Constants, Schema, Config) { + $scope.resetMenu("schema", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function () { + Luke.schema({core: $routeParams.core}, function (schema) { + Luke.raw({core: $routeParams.core}, function (index) { + var data = mergeIndexAndSchemaData(index, schema.schema); + + $scope.fieldsAndTypes = getFieldsAndTypes(data); + $scope.is = {}; + + var search = $location.search(); + leftbar = {}; + $scope.isField = $scope.isDynamicField = $scope.isType = false; + $scope.showing = true; + if (search.field) { + $scope.selectedType = "Field"; + $scope.is.field = true; + $scope.name = search.field; + leftbar.fields = [$scope.name]; + var field = data.fields[$scope.name]; + leftbar.types = [field.type]; + if (field.dynamicBase) leftbar.dynamicFields = [field.dynamicBase]; + if (field.copySources && field.copySources.length>0) { + leftbar.copyFieldSources = sortedObjectArray(field.copySources.sort()); + } + if (field.copyDests && field.copyDests.length>0) { + leftbar.copyFieldDests = sortedObjectArray(field.copyDests.sort()); + } + $scope.fieldOrType = "field=" + $scope.name; + } else if (search["dynamic-field"]) { + $scope.selectedType = "Dynamic Field"; + $scope.is.dynamicField = true; + $scope.name = search["dynamic-field"]; + leftbar.dynamicFields = [$scope.name]; + leftbar.types = [data.dynamic_fields[$scope.name].type]; + $scope.fieldOrType = "dynamic-field=" + $scope.name; + } else if (search.type) { + $scope.selectedType = "Type"; + $scope.is.type = true; + $scope.name = search.type; + leftbar.types = [$scope.name]; + leftbar.fields = filterFields("fields", data, $scope.name); + leftbar.dynamicFields = filterFields("dynamic_fields", data, $scope.name); + $scope.fieldOrType = "type=" + $scope.name; + } else { + $scope.showing = false; + } + $scope.leftbar = leftbar; + $scope.core = $routeParams.core; + $scope.uniqueKeyField = data.unique_key_field; + $scope.similarity = data.similarity; + if ($scope.similarity && $scope.similarity.className) { + $scope.similarity.className = shortenPackages($scope.similarity.className); + } + $scope.isUniqueKeyField = ($scope.selectedType == "Field" && $scope.name == $scope.uniqueKeyField); + + $scope.display = getFieldProperties(data, $routeParams.core, $scope.is, $scope.name); + $scope.analysis = getAnalysisInfo(data, $scope.is, $scope.name); + + $scope.isAutoload = $cookies[cookie_schema_browser_autoload] == "true"; + if ($scope.isAutoload) { + $scope.toggleTerms(); + } + + $scope.types = Object.keys(schema.schema.types); + }); + }); + Config.get({core: $routeParams.core}, function(data) { + $scope.isSchemaUpdatable = (data.config.hasOwnProperty('schemaFactory') == false || data.config.schemaFactory.class == "ManagedIndexSchemaFactory"); + }); + }; + $scope.refresh(); + + $scope.selectFieldOrType = function() { + $location.search($scope.fieldOrType); + } + + $scope.toggleAnalyzer = function(analyzer) { + analyzer.show = !analyzer.show; + } + + $scope.loadTermInfo = function() { + var params = {fl: $scope.name, core: $routeParams.core}; + if ($scope.topTermsCount) { + params.numTerms = $scope.topTermsCount; + } + $scope.isLoadingTerms = true; + Luke.field(params, function (data) { + $scope.isLoadingTerms = false; + $scope.termInfo = getTermInfo(data.fields[$scope.name]); + if (!$scope.topTermsCount) { + $scope.topTermsCount = $scope.termInfo.termCount; + } + }); + } + + $scope.toggleTerms = function() { + $scope.showTerms = !$scope.showTerms; + + if ($scope.showTerms) { + $scope.loadTermInfo(); + } + } + + $scope.loadAllTerms = function() { + $scope.topTermsCount = $scope.termInfo.maxTerms; + $scope.loadTermInfo(); + } + + $scope.toggleAutoload = function() { + $scope.isAutoload = !$scope.isAutoload; + $cookies[cookie_schema_browser_autoload] = $scope.isAutoload; + console.log("cookie: " + $cookies[cookie_schema_browser_autoload]); + } + + $scope.hideAll = function() { + $scope.showAddField = false; + $scope.showAddDynamicField = false; + $scope.showAddCopyField = false; + } + + $scope.toggleAddField = function() { + if ($scope.showAddField && $scope.adding == "field") { + $scope.hideAll(); + } else { + $scope.hideAll(); + $scope.showAddField = true; + $scope.adding = "field"; + + $scope.newField = { + stored: "true", + indexed: "true", + uninvertible: "true" + } + delete $scope.addErrors; + } + } + + $scope.addField = function() { + delete $scope.addErrors; + var data = {"add-field": $scope.newField}; + Schema.post({core: $routeParams.core}, data, function(data) { + if (data.errors) { + $scope.addErrors = data.errors[0].errorMessages; + if (typeof $scope.addErrors === "string") { + $scope.addErrors = [$scope.addErrors]; + } + } else { + $scope.added = true; + $timeout(function() { + $scope.showAddField = false; + $scope.added = false; + $scope.refresh(); + }, 1500); + } + }); + } + + $scope.toggleAddDynamicField = function() { + if ($scope.showAddField && $scope.adding == "dynamicField") { + $scope.hideAll(); + } else { + $scope.hideAll(); + $scope.showAddField = true; + $scope.adding = "dynamicField"; + + $scope.newField = { + stored: "true", + indexed: "true" + } + delete $scope.addErrors; + } + } + + $scope.addDynamicField = function() { + delete $scope.addErrors; + var data = {"add-dynamic-field": $scope.newField}; + Schema.post({core: $routeParams.core}, data, function(data) { + if (data.errors) { + $scope.addErrors = data.errors[0].errorMessages; + if (typeof $scope.addErrors === "string") { + $scope.addErrors = [$scope.addErrors]; + } + } else { + $scope.added = true; + $timeout(function() { + $scope.showAddField = false; + $scope.added = false; + $scope.refresh(); + }, 1500); + } + }); + } + + $scope.toggleAddCopyField = function() { + if ($scope.showAddCopyField) { + $scope.hideAll(); + } else { + $scope.hideAll(); + $scope.showAddCopyField = true; + + $scope.copyField = {}; + delete $scope.addCopyFieldErrors; + } + } + $scope.addCopyField = function() { + delete $scope.addCopyFieldErrors; + var data = {"add-copy-field": $scope.copyField}; + Schema.post({core: $routeParams.core}, data, function(data) { + if (data.errors) { + $scope.addCopyFieldErrors = data.errors[0].errorMessages; + if (typeof $scope.addCopyFieldErrors === "string") { + $scope.addCopyFieldErrors = [$scope.addCopyFieldErrors]; + } + } else { + $scope.showAddCopyField = false; + $timeout($scope.refresh, 1500); + } + }); + } + + $scope.toggleDelete = function() { + if ($scope.showDelete) { + $scope.showDelete = false; + } else { + if ($scope.is.field) { + $scope.deleteData = {'delete-field': {name: $scope.name}}; + } else if ($scope.is.dynamicField) { + $scope.deleteData = {'delete-dynamic-field': {name: $scope.name}}; + } else { + alert("TYPE NOT KNOWN"); + } + $scope.showDelete = true; + } + } + + $scope.delete = function() { + Schema.post({core: $routeParams.core}, $scope.deleteData, function(data) { + if (data.errors) { + $scope.deleteErrors = data.errors[0].errorMessages; + if (typeof $scope.deleteErrors === "string") { + $scope.deleteErrors = [$scope.deleteErrors]; + } + } else { + $scope.deleted = true; + $timeout(function() { + $location.search(""); + }, 1500 + ); + } + }); + } + $scope.toggleDeleteCopyField = function(field) { + field.show = !field.show; + delete field.errors; + } + $scope.deleteCopyField = function(field, source, dest) { + data = {'delete-copy-field': {source: source, dest: dest}}; + Schema.post({core: $routeParams.core}, data, function(data) { + if (data.errors) { + field.errors = data.errors[0].errorMessages; + if (typeof $scope.deleteErrors === "string") { + field.errors = [field.errors]; + } + } else { + field.deleted = true; + $timeout($scope.refresh, 1500); + } + }); + } + } +); + +var getFieldsAndTypes = function(data) { + var fieldsAndTypes = []; + var fields = Object.keys(data.fields).sort(); + for (var i in fields) { + fieldsAndTypes.push({ + group: "Fields", + value: "field=" + fields[i], + label: fields[i] + }); + } + var dynamic_fields = Object.keys(data.dynamic_fields).sort(); + for (var i in dynamic_fields) { + fieldsAndTypes.push({ + group: "Dynamic Fields", + value: "dynamic-field=" + dynamic_fields[i], + label: dynamic_fields[i] + }); + } + var types = Object.keys(data.types).sort(); + for (var i in types) { + fieldsAndTypes.push({ + group: "Types", + value: "type=" + types[i], + label: types[i] + }); + } + return fieldsAndTypes; +}; + +var filterFields = function(type, data, name) { + var fields = []; + for (var i in data.types[name].fields) { + var field = data.types[name].fields[i]; + if (data[type][field]) { + fields.push(field) + } + } + return fields.sort(); +} + +var mergeIndexAndSchemaData = function(index, schema) { + + var data = { + unique_key_field: null, + similarity: null, + key: {}, + fields: {}, + dynamic_fields: {}, + types: {}, + relations: { + f_df: {}, + f_t: {}, + df_f: {}, + df_t: {}, + t_f: {}, + t_df: {} + } + }; + + data.fields = index.fields; + + data.key = index.info.key; + + data.unique_key_field = schema.uniqueKeyField; + data.similarity = schema.similarity; + + data.dynamic_fields = schema.dynamicFields; + data.types = schema.types; + + for (var field in schema.fields) { + data.fields[field] = + $.extend({}, data.fields[field], schema.fields[field]); + } + + for (var field in data.fields) { + var copy_dests = data.fields[field].copyDests; + for (var i in copy_dests) { + var copy_dest = copy_dests[i]; + if (!data.fields[copy_dest]) { + data.fields[copy_dest] = { + partial: true, + copySources: [] + }; + } + + if (data.fields[copy_dest].partial) { + data.fields[copy_dest].copySources.push(field); + } + } + + var copy_sources = data.fields[field].copySources; + for (var i in copy_sources) { + var copy_source = copy_sources[i]; + if (!data.fields[copy_source]) { + data.fields[copy_source] = { + partial: true, + copyDests: [] + }; + } + + if (data.fields[copy_source].partial) { + data.fields[copy_source].copyDests.push(field); + } + } + + data.relations.f_t[field] = data.fields[field].type; + + if (!data.relations.t_f[data.fields[field].type]) { + data.relations.t_f[data.fields[field].type] = []; + } + data.relations.t_f[data.fields[field].type].push(field); + + if (data.fields[field].dynamicBase) { + data.relations.f_df[field] = data.fields[field].dynamicBase; + + if (!data.relations.df_f[data.fields[field].dynamicBase]) { + data.relations.df_f[data.fields[field].dynamicBase] = []; + } + data.relations.df_f[data.fields[field].dynamicBase].push(field); + } + } + + for (var dynamic_field in data.dynamic_fields) { + data.relations.df_t[dynamic_field] = data.dynamic_fields[dynamic_field].type; + + if (!data.relations.t_df[data.dynamic_fields[dynamic_field].type]) { + data.relations.t_df[data.dynamic_fields[dynamic_field].type] = []; + } + data.relations.t_df[data.dynamic_fields[dynamic_field].type].push(dynamic_field); + } + return data; +}; + +var getFieldProperties = function(data, core, is, name) { + + var display = {}; + + display.partialState = is.field && !!data.fields[name].partial; + + display.columns = []; + display.rows = []; + var allFlags = ""; + + var addRow = function(name, flags) { + if (flags[0]!='(') { + display.rows.push({name:name, flags:flags}); + for (var i in flags) { + if (flags[i]!="-" && allFlags.indexOf(flags[i])<0) { + allFlags+=flags[i]; + } + } + } else { + display.rows.push({name:name, comment:flags}); + } + } + + // Identify the rows for our field property table + if (is.field && data.fields[name]) { + if (data.fields[name].flags) { + addRow('Properties', data.fields[name].flags); + } + if (data.fields[name].schema) { + addRow('Schema', data.fields[name].schema); + } + if (data.fields[name].index) { + addRow('Index', data.fields[name].index); + } + display.docs = data.fields[name].docs; + display.docsUrl = "#/" + core + "/query?q=" + name + ":[* TO *]"; + display.distinct = data.fields[name].distinct; + display.positionIncrementGap = data.fields[name].positionIncrementGap; + if (data.types[data.fields[name].type]) { + display.similarity = data.types[data.fields[name].type].similarity; + } else { + display.similarity = null; + } + } else if (is.dynamicField && data.dynamic_fields[name] && data.dynamic_fields[name].flags) { + addRow('Properties', data.dynamic_fields[name].flags); + display.similarity = data.types[data.dynamic_fields[name].type].similarity; + } else if (is.type && data.types[name]) { + display.similarity = data.types[name].similarity; + } + if (display.similarity && display.similarity.className) { + display.similarity.className = shortenPackages(display.similarity.className); + } + + // identify columns in field property table: + for (var key in data.key) { + if (allFlags.indexOf(key)>=0) { + display.columns.push({key: key, name: data.key[key]}); + } + } + + // identify rows and cell values in field property table: + for (var i in display.rows) { + var row = display.rows[i]; + row.cells = []; + + if (!row.flags) { + continue; // Match the special case in the LukeRequestHandler + } + + for (var j in display.columns) { + var flag = display.columns[j].key; + row.cells.push({key: flag, value: row.flags.indexOf(flag)>=0}); + } + } + + return display; +}; + +var getAnalysisInfo = function(data, is, name) { + + var analysis = {}; + + if (is.field) { + var type = data.relations.f_t[name]; + analysis.query = "analysis.fieldname=" + name; + } + else if (is.dynamicField) { + var type = data.relations.df_t[name]; + analysis.query = "analysis.fieldtype=" + type; + } + else if (is.type) { + var type = name; + analysis.query = "analysis.fieldtype=" + name; + } + + var processComponentType = function (label, key, componentTypeData) { + if (componentTypeData) { + var components = []; + for (var componentName in componentTypeData) { + var componentData = componentTypeData[componentName]; + var component = {className: componentData.className, args:[]}; + if (componentData.args) { + for (var argName in componentData.args) { + var argValue = componentData.args[argName]; + if (argValue == "1" || argValue == "true") { + component.args.push({name: argName, booleanValue:true}); + } else if (argValue == "0" || argValue == "false") { + component.args.push({name: argName, booleanValue:false}); + } else { + component.args.push({name: argName, value:argValue}); + } + } + } + components.push(component); + } + return {label: label, key: key, components: components}; + } else { + return {label: label, key: key}; + } + } + + var buildAnalyzer = function (analyzerData) { + var analyzer = {}; + analyzer.className = analyzerData.className; + analyzer.componentTypes = []; + if (analyzerData.tokenizer) { + analyzer.componentTypes.push(processComponentType("Char Filters", "charFilters", analyzerData.charFilters)); + analyzer.componentTypes.push(processComponentType("Tokenizer", "tokenizer", {tokenizer: analyzerData.tokenizer})); + analyzer.componentTypes.push(processComponentType("Token Filters", "tokenFilters", analyzerData.filters)); + } + return analyzer; + } + + analysis.data = data.types[type]; + if (analysis.data) { + analysis.analyzers = [ + {key: "index", name: "Index", detail: buildAnalyzer(analysis.data.indexAnalyzer)}, + {key: "query", name: "Query", detail: buildAnalyzer(analysis.data.queryAnalyzer)} + ]; + } + return analysis; +} + +var getTermInfo = function(data) { + + var termInfo = {}; + if (data && data.topTerms) { + termInfo.topTerms = []; + + var currentGroup = {count: 0} + for (var i = 0; i < data.topTerms.length; i += 2) { + var count = data.topTerms[i + 1]; + if (currentGroup.count != count) { + currentGroup = {count: count, terms: []}; + termInfo.topTerms.push(currentGroup); + } + currentGroup.terms.push(data.topTerms[i]); + } + termInfo.termCount = data.topTerms.length / 2; + termInfo.maxTerms = data.distinct; + } + + if(data && data.histogram) { + termInfo.histogram = []; + termInfo.histogramMax = 0; + for (var i = 0; i < data.histogram.length; i += 2) { + termInfo.histogram.push({key: data.histogram[i], value: data.histogram[i + 1]}); + termInfo.histogramMax = Math.max(termInfo.histogramMax, data.histogram[i + 1]); + } + } + return termInfo; +}; + +var sortedObjectArray = function(list) { + var objarr = []; + for (var i in list) { + objarr.push({"name": list[i]}); + } + return objarr; +}; + +var shortenPackages = function(className) { + return className.replace("org.apache.solr", "o.a.s").replace("org.apache.lucene", "o.a.l"); +}; diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js new file mode 100644 index 000000000..e835cc084 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js @@ -0,0 +1,99 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +var MB_FACTOR = 1024*1024; + +solrAdminApp.controller('SegmentsController', function($scope, $routeParams, $interval, Segments, Constants) { + $scope.resetMenu("segments", Constants.IS_CORE_PAGE); + + $scope.refresh = function() { + + Segments.get({core: $routeParams.core}, function(data) { + var segments = data.segments; + + var segmentSizeInBytesMax = getLargestSegmentSize(segments); + $scope.segmentMB = Math.floor(segmentSizeInBytesMax / MB_FACTOR); + $scope.xaxis = calculateXAxis(segmentSizeInBytesMax); + + $scope.documentCount = 0; + $scope.deletionCount = 0; + + $scope.segments = []; + for (var name in segments) { + var segment = segments[name]; + + var segmentSizeInBytesLog = Math.log(segment.sizeInBytes); + var segmentSizeInBytesMaxLog = Math.log(segmentSizeInBytesMax); + + segment.totalSize = Math.floor((segmentSizeInBytesLog / segmentSizeInBytesMaxLog ) * 100); + + segment.deletedDocSize = Math.floor((segment.delCount / segment.size) * segment.totalSize); + if (segment.delDocSize <= 0.001) delete segment.deletedDocSize; + + segment.aliveDocSize = segment.totalSize - segment.deletedDocSize; + + $scope.segments.push(segment); + + $scope.documentCount += segment.size; + $scope.deletionCount += segment.delCount; + } + $scope.deletionsPercentage = calculateDeletionsPercentage($scope.documentCount, $scope.deletionCount); + }); + }; + + $scope.toggleAutoRefresh = function() { + $scope.autorefresh = !$scope.autorefresh; + if ($scope.autorefresh) { + $scope.interval = $interval($scope.refresh, 1000); + var onRouteChangeOff = $scope.$on('$routeChangeStart', function() { + $interval.cancel($scope.interval); + onRouteChangeOff(); + }); + + } else if ($scope.interval) { + $interval.cancel($scope.interval); + } + }; + $scope.refresh(); +}); + +var calculateXAxis = function(segmentInBytesMax) { + var steps = []; + var log = Math.log(segmentInBytesMax); + + for (var j=0, step=log/4; j<3; j++, step+=log/4) { + steps.push({pos:j, value:Math.floor((Math.pow(Math.E, step))/MB_FACTOR)}) + } + return steps; +}; + +var getLargestSegmentSize = function(segments) { + var max = 0; + for (var name in segments) { + max = Math.max(max, segments[name].sizeInBytes); + } + return max; +}; + +var calculateDeletionsPercentage = function(docCount, delCount) { + if (docCount == 0) { + return 0; + } else { + var percent = delCount / docCount * 100; + return Math.round(percent * 100) / 100; + } +}; diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js new file mode 100644 index 000000000..92440c360 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js @@ -0,0 +1,239 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +solrAdminApp.controller('StreamController', + function($scope, $routeParams, $location, Query, Constants) { + + $scope.resetMenu("stream", Constants.IS_COLLECTION_PAGE); + + $scope.stream = { + wt: 'json', + expr: $scope.expr, + indent: 'on' + }; + $scope.qt = "stream"; + $scope.doExplanation = false + + $scope.doStream = function() { + + var params = {}; + params.core = $routeParams.core; + params.handler = $scope.qt; + params.expr = [$scope.expr] + if($scope.doExplanation){ + params.explain = [$scope.doExplanation] + } + + $scope.lang = "json"; + $scope.response = null; + $scope.url = ""; + + var url = Query.url(params); + + Query.query(params, function(data) { + + var jsonData = JSON.parse(data.toJSON().data); + if (undefined != jsonData["explanation"]) { + $scope.showExplanation = true; + + streamGraphSubController($scope, jsonData["explanation"]) + delete jsonData["explanation"] + } else { + $scope.showExplanation = false; + } + + data.data = JSON.stringify(jsonData,null,2); + + $scope.lang = "json"; + $scope.response = data; + $scope.url = url; + $scope.hostPortContext = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // For display only + + }); + }; + + if ($location.search().expr) { + $scope.expr = $location.search()["expr"]; + $scope.doStream(); + } + + } +); + +var streamGraphSubController = function($scope, explanation) { + $scope.showGraph = true; + $scope.pos = 0; + $scope.rows = 8; + + $scope.resetGraph = function() { + $scope.pos = 0; + $scope.initGraph(); + } + + $scope.initGraph = function(explanation) { + + data = explanation + + var leafCount = 0; + var maxDepth = 0; + var rootNode = {}; + + leafCount = 0; + + let recurse = function(dataNode, depth) { + + if (depth > maxDepth) { + maxDepth = depth; + } + + let graphNode = { + name: dataNode.expressionNodeId, + implementingClass: 'unknown', + data: {} + }; + + ["expressionNodeId", "expressionType", "functionName", "implementingClass", "expression", "note", "helpers"].forEach(function(key) { + graphNode.data[key] = dataNode[key]; + }); + + if (dataNode.children && dataNode.children.length > 0) { + graphNode.children = []; + dataNode.children.forEach(function(n) { + graphNode.children.push(recurse(n, depth + 1)); + }); + } else { + ++leafCount; + } + + return graphNode; + } + + $scope.showPaging = false; + $scope.isRadial = false; + $scope.explanationData = recurse(data, 1); + + $scope.depth = maxDepth + 1; + $scope.leafCount = leafCount; + }; + + $scope.initGraph(explanation); +}; + +solrAdminApp.directive('explanationGraph', function(Constants) { + return { + restrict: 'EA', + scope: { + data: "=", + leafCount: "=", + depth: "=" + }, + link: function(scope, element, attrs) { + + var helper_path_class = function(p) { + var classes = ['link']; + + return classes.join(' '); + }; + + var helper_node_class = function(d) { + var classes = ['node']; + + if (d.data && d.data.expressionType) { + classes.push(d.data.expressionType); + } + + return classes.join(' '); + }; + + var helper_node_text = function(d) { + if (d.data && d.data.functionName) { + return d.data.functionName; + } + + return d.name + }; + + var helper_tooltip = function(d) { + + return [ + "Function: " + d.data.functionName, + "Type: " + d.data.expressionType, + "Class: " + d.data.implementingClass.replace("org.apache.solr.client.solrj.io", "o.a.s.c.s.i"), + "=============", + d.data.expression + ].join("\n"); + } + + scope.$watch("data", function(newValue, oldValue) { + if (newValue) { + flatGraph(element, scope.data, scope.depth, scope.leafCount); + } + }); + + var flatGraph = function(element, graphData, depth, leafCount) { + var w = 100 + (depth * 100), + h = leafCount * 40; + + var tree = d3.layout.tree().size([h, w]); + + var diagonal = d3.svg.diagonal().projection(function(d) { + return [d.y * .7, d.x]; + }); + + d3.select('#canvas', element).html(''); + var vis = d3.select('#canvas', element).append('svg') + .attr('width', w) + .attr('height', h) + .append('g') + .attr('transform', 'translate(25, 0)'); + + var nodes = tree.nodes(graphData); + + var link = vis.selectAll('path.link') + .data(tree.links(nodes)) + .enter().append('path') + .attr('class', helper_path_class) + .attr('d', diagonal); + + var node = vis.selectAll('g.node') + .data(nodes) + .enter().append('g') + .attr('class', helper_node_class) + .attr('transform', function(d) { + return 'translate(' + d.y * .7 + ',' + d.x + ')'; + }) + + node.append('circle') + .attr('r', 4.5); + + node.append('title') + .text(helper_tooltip); + + node.append('text') + .attr('dx', function(d) { + return 8; + }) + .attr('dy', function(d) { + return 5; + }) + .attr('text-anchor', function(d) { + return 'start'; + }) + .text(helper_node_text) + }; + } + }; +}) diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js new file mode 100644 index 000000000..22a900295 --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js @@ -0,0 +1,50 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +solrAdminApp.controller('ThreadsController', + function($scope, Threads, Constants){ + $scope.resetMenu("threads", Constants.IS_ROOT_PAGE); + $scope.refresh = function() { + Threads.get(function(data) { + var threadDump = data.system.threadDump; + var threads = []; + for (var i=1; i<threadDump.length; i+=2) { + var thread = threadDump[i]; + if (!!thread.stackTrace) { + var stackTrace = []; + for (var j=0; j<thread.stackTrace.length; j++) { + var trace = thread.stackTrace[j].replace("(", "\u200B("); // allow wrapping to happen, \u200B is a zero-width space + stackTrace.push({id:thread.id + ":" + j, trace: trace}); + } + thread.stackTrace = stackTrace; + } + threads.push(thread); + } + $scope.threads = threads; + }); + }; + $scope.toggleStacktrace = function(thread) { + thread.showStackTrace = !thread.showStackTrace; + }; + $scope.toggleStacktraces = function() { + $scope.showAllStacktraces = !$scope.showAllStacktraces; + for (var i=0; i<$scope.threads.length; i++) { + $scope.threads[i].showStackTrace = $scope.showAllStacktraces; + } + }; + $scope.refresh(); +}); diff --git a/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js new file mode 100644 index 000000000..2d959e6ce --- /dev/null +++ b/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js @@ -0,0 +1,37 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * This controller is called whenever no other routes match. + * It is a place to intercept to look for special flows such as authentication callbacks (that do not support fragment in URL). + * Normal action is to redirect to dashboard "/" if no login is in progress + */ +solrAdminApp.controller('UnknownController', + ['$scope', '$window', '$routeParams', '$location', 'Constants', 'AuthenticationService', + function($scope, $window, $routeParams, $location, Constants, AuthenticationService) { + var fragment = $window.location.hash.startsWith("#/") ? $window.location.hash.substring(2) : $window.location.hash; + // Check if the URL is actually a callback from Identiy provider + if (AuthenticationService.isJwtCallback(fragment)) { + console.log("Detected an authentication callback, redirecting to /#/login/callback"); + $location.path("/login/callback").hash(fragment); + } else { + console.log("Redirecting from unknown path " + fragment + " to Dashboard"); + $location.path("/").hash(""); + } + } + ] +); |