465 lines
13 KiB
JavaScript
465 lines
13 KiB
JavaScript
// allow the user to store recent queries for quick retrieval
|
|
var recentQueries = [];
|
|
var queryPointer = null;
|
|
|
|
// keep track of the databases that exist on the server
|
|
var availableDatabases = [];
|
|
var currentlySelectedDatabase = null;
|
|
|
|
// connection settings for the server, with sensible defaults
|
|
var connectionSettings = {
|
|
hostname: (window.location.hostname ? window.location.hostname: "localhost"),
|
|
port: "8086",
|
|
username: "",
|
|
password: "",
|
|
ssl: false
|
|
}
|
|
|
|
var connectionString = function() {
|
|
var protocol = (connectionSettings.ssl ? "https" : "http");
|
|
var host = connectionSettings.hostname + ":" + connectionSettings.port;
|
|
|
|
if (connectionSettings.username !== "") {
|
|
$.ajaxSetup({
|
|
headers: {
|
|
'Authorization': "Basic " + btoa(connectionSettings.username + ":" + connectionSettings.password)
|
|
}
|
|
});
|
|
}
|
|
|
|
return protocol + "://" + host;
|
|
}
|
|
|
|
var getSeriesFromJSON = function(data) {
|
|
var results = data.results[0].series;
|
|
return results;
|
|
}
|
|
|
|
// gets settings from the browser's localStorage and sets defaults if they aren't found
|
|
var loadSettings = function() {
|
|
var cs = localStorage.getItem("connectionSettings");
|
|
|
|
if (cs != null) { connectionSettings = JSON.parse(cs); }
|
|
|
|
document.getElementById('hostname').value = connectionSettings.hostname;
|
|
document.getElementById('port').value = connectionSettings.port;
|
|
document.getElementById('username').value = connectionSettings.username;
|
|
document.getElementById('password').value = connectionSettings.password;
|
|
document.getElementById('ssl').checked = connectionSettings.ssl;
|
|
|
|
getDatabases();
|
|
}
|
|
|
|
var updateSettings = function() {
|
|
var hostname = document.getElementById('hostname').value;
|
|
var port = document.getElementById('port').value;
|
|
var username = document.getElementById('username').value;
|
|
var password = document.getElementById('password').value;
|
|
var ssl = document.getElementById('ssl').checked;
|
|
|
|
if (hostname == "") { hostname = "localhost"; }
|
|
|
|
if (port == "") { port = "8086"; }
|
|
|
|
connectionSettings.hostname = hostname;
|
|
connectionSettings.port = port;
|
|
connectionSettings.username = username;
|
|
connectionSettings.password = password;
|
|
connectionSettings.ssl = ssl;
|
|
|
|
localStorage.setItem("connectionSettings", JSON.stringify(connectionSettings));
|
|
|
|
getDatabases();
|
|
}
|
|
|
|
var showSettings = function() {
|
|
$("#settings").show();
|
|
$("input#query").prop('disabled', true);
|
|
}
|
|
|
|
var hideSettings = function() {
|
|
$("#settings").hide();
|
|
$("input#query").prop('disabled', false);
|
|
}
|
|
|
|
// hide errors within the Write Data modal
|
|
var hideModalError = function() {
|
|
$("div#modal-error").empty().hide();
|
|
}
|
|
|
|
// show errors within the Write Data modal
|
|
var showModalError = function(message) {
|
|
hideModalSuccess();
|
|
|
|
$("div#modal-error").html("<p>" + message + "</p>").show();
|
|
}
|
|
|
|
// hide success messages within the Write Data modal
|
|
var hideModalSuccess = function() {
|
|
$("div#modal-success").empty().hide();
|
|
}
|
|
|
|
// show success messages within the Write Data modal
|
|
var showModalSuccess = function(message) {
|
|
hideModalError();
|
|
|
|
$("div#modal-success").html("<p>" + message + "</p>").show();
|
|
}
|
|
|
|
// hide errors from queries
|
|
var hideQueryError = function() {
|
|
$("div#query-error").empty().hide();
|
|
}
|
|
|
|
// show errors from queries
|
|
var showQueryError = function(message) {
|
|
hideQuerySuccess();
|
|
|
|
$("div#query-error").html("<p>" + message + "</p>").show();
|
|
}
|
|
|
|
// hide success messages from queries
|
|
var hideQuerySuccess = function() {
|
|
$("div#query-success").empty().hide();
|
|
}
|
|
|
|
// show success messages from queries
|
|
var showQuerySuccess = function(message) {
|
|
hideQueryError();
|
|
|
|
$("div#query-success").html("<p>" + message + "</p>").show();
|
|
}
|
|
|
|
// hide warning from database lookup
|
|
var hideDatabaseWarning = function() {
|
|
$("div#database-warning").empty().hide();
|
|
}
|
|
|
|
// show warning from database lookup
|
|
var showDatabaseWarning = function(message) {
|
|
$("div#database-warning").html("<p>" + message + "</p>").show();
|
|
}
|
|
|
|
// clear out the results table
|
|
var clearResults = function() {
|
|
$("div#table").empty();
|
|
}
|
|
|
|
// handle submissions of the query bar
|
|
var handleSubmit = function(e) {
|
|
var queryElement = document.getElementById('query');
|
|
var q = queryElement.value;
|
|
|
|
clearResults();
|
|
|
|
if (q == "") { return };
|
|
|
|
var query = $.get(connectionString() + "/query", {q: q, db: currentlySelectedDatabase}, function() {
|
|
hideQueryError();
|
|
hideQuerySuccess();
|
|
});
|
|
|
|
recentQueries.push(q);
|
|
queryPointer = recentQueries.length - 1;
|
|
|
|
query.fail(handleRequestError);
|
|
|
|
query.done(function (data) {
|
|
var firstRow = data.results[0];
|
|
if (firstRow.error) {
|
|
showQueryError("Server returned error: " + firstRow.error);
|
|
return
|
|
}
|
|
|
|
var series = getSeriesFromJSON(data);
|
|
|
|
if (series == null) {
|
|
showQuerySuccess("Success! (no results to display)");
|
|
getDatabases();
|
|
return
|
|
}
|
|
|
|
var values = series[0].values;
|
|
|
|
if ((values == null) || (values.length == 0)) {
|
|
showQueryError("Query returned no results!");
|
|
} else {
|
|
availableDatabases = values.map(function(value) {
|
|
return value[0];
|
|
});
|
|
|
|
hideDatabaseWarning();
|
|
React.render(
|
|
React.createElement(DataTable, {series: series}),
|
|
document.getElementById('table')
|
|
);
|
|
}
|
|
});
|
|
|
|
if (e != null) { e.preventDefault(); }
|
|
return false;
|
|
};
|
|
|
|
var handleRequestError = function(e) {
|
|
var errorText = e.status + " " + e.statusText;
|
|
showDatabaseWarning("Unable to fetch list of databases.");
|
|
|
|
if ("responseText" in e) {
|
|
try { errorText = "Server returned error: " + JSON.parse(e.responseText).error; } catch(e) {}
|
|
}
|
|
|
|
if (e.status == 400) {
|
|
hideSettings();
|
|
} else if (e.status == 401) {
|
|
if (errorText.indexOf("error authorizing query") > -1) {
|
|
hideSettings();
|
|
$("input#query").val("CREATE USER <username> WITH PASSWORD '<password>' WITH ALL PRIVILEGES").focus();
|
|
} else {
|
|
showSettings();
|
|
$("input#username").focus();
|
|
}
|
|
} else {
|
|
showSettings();
|
|
$("input#hostname").focus();
|
|
showDatabaseWarning("Hint: the InfluxDB API runs on port 8086 by default");
|
|
errorText = e.status + " " + e.statusText + " - Could not connect to " + connectionString();
|
|
}
|
|
showQueryError(errorText);
|
|
};
|
|
|
|
var handleKeypress = function(e) {
|
|
var queryElement = document.getElementById('query');
|
|
|
|
// key press == enter
|
|
if (e.keyCode == 13) {
|
|
e.preventDefault();
|
|
handleSubmit();
|
|
return false;
|
|
}
|
|
|
|
// if we don't have any recent queries, ignore the arrow keys
|
|
if (recentQueries.length == 0 ) { return }
|
|
|
|
// key press == up arrow
|
|
if (e.keyCode == 38) {
|
|
// TODO: stash the current query, if there is one?
|
|
if (queryPointer == recentQueries.length - 1) {
|
|
// this is buggy.
|
|
//recentQueries.push(queryElement.value);
|
|
//queryPointer = recentQueries.length - 1;
|
|
}
|
|
|
|
if (queryPointer != null && queryPointer > 0) {
|
|
queryPointer -= 1;
|
|
queryElement.value = recentQueries[queryPointer];
|
|
}
|
|
}
|
|
|
|
// key press == down arrow
|
|
if (e.keyCode == 40) {
|
|
if (queryPointer != null && queryPointer < recentQueries.length - 1) {
|
|
queryPointer += 1;
|
|
queryElement.value = recentQueries[queryPointer];
|
|
}
|
|
}
|
|
};
|
|
|
|
var QueryError = React.createClass({
|
|
render: function() {
|
|
return React.createElement("div", {className: "alert alert-danger"}, this.props.message)
|
|
}
|
|
});
|
|
|
|
var stringifyTags = function(tags) {
|
|
var tagStrings = [];
|
|
|
|
for(var index in tags) {
|
|
tagStrings.push(index + ":" + tags[index]);
|
|
}
|
|
|
|
return tagStrings.join(", ");
|
|
}
|
|
|
|
var DataTable = React.createClass({
|
|
render: function() {
|
|
var tables = this.props.series.map(function(series) {
|
|
return React.createElement("div", null,
|
|
React.createElement("h1", null, series.name),
|
|
React.createElement("h2", null, stringifyTags(series.tags)),
|
|
React.createElement("table", {className: "table"},
|
|
React.createElement(TableHeader, {data: series.columns}),
|
|
React.createElement(TableBody, {data: series})
|
|
)
|
|
);
|
|
});
|
|
|
|
return React.createElement("div", null, tables);
|
|
}
|
|
});
|
|
|
|
var TableHeader = React.createClass({
|
|
render: function() {
|
|
var headers = this.props.data.map(function(column) {
|
|
return React.createElement("th", null, column);
|
|
});
|
|
|
|
return React.createElement("tr", null, headers);
|
|
}
|
|
});
|
|
|
|
var TableBody = React.createClass({
|
|
render: function() {
|
|
var tableRows = this.props.data.values.map(function (row) {
|
|
return React.createElement(TableRow, {data: row});
|
|
});
|
|
|
|
return React.createElement("tbody", null, tableRows);
|
|
}
|
|
});
|
|
|
|
var TableRow = React.createClass({
|
|
render: function() {
|
|
var tableData = this.props.data.map(function (data, index) {
|
|
if (index == 0) {
|
|
return React.createElement("td", {className: "timestamp"}, null, data);
|
|
} else {
|
|
return React.createElement("td", null, pretty(data));
|
|
}
|
|
});
|
|
|
|
return React.createElement("tr", null, tableData);
|
|
}
|
|
});
|
|
|
|
var pretty = function(val) {
|
|
if (typeof val == 'string') {
|
|
return "\"" + val + "\"";
|
|
} else if (typeof val == 'boolean' ){
|
|
return val.toString();
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
|
|
var chooseDatabase = function (databaseName) {
|
|
currentlySelectedDatabase = databaseName;
|
|
document.getElementById("content-current-database").innerHTML = currentlySelectedDatabase;
|
|
}
|
|
|
|
var getDatabases = function () {
|
|
var q = "SHOW DATABASES";
|
|
var query = $.get(connectionString() + "/query", {q: q, db: currentlySelectedDatabase});
|
|
|
|
query.fail(handleRequestError);
|
|
|
|
query.done(function (data) {
|
|
hideSettings();
|
|
hideDatabaseWarning();
|
|
|
|
var firstRow = data.results[0];
|
|
if (firstRow.error) {
|
|
showDatabaseWarning(firstRow.error);
|
|
return;
|
|
}
|
|
|
|
var series = getSeriesFromJSON(data);
|
|
var values = series[0].values;
|
|
|
|
if ((values == null) || (values.length == 0)) {
|
|
availableDatabases = [];
|
|
updateDatabaseList();
|
|
|
|
showDatabaseWarning("No databases found.")
|
|
} else {
|
|
availableDatabases = values.map(function(value) {
|
|
return value[0];
|
|
}).sort();
|
|
|
|
if (currentlySelectedDatabase == null) {
|
|
chooseDatabase(availableDatabases[0]);
|
|
} else if (availableDatabases.indexOf(currentlySelectedDatabase) == -1) {
|
|
chooseDatabase(availableDatabases[0]);
|
|
}
|
|
updateDatabaseList();
|
|
}
|
|
});
|
|
}
|
|
|
|
var updateDatabaseList = function() {
|
|
var databaseList = $("ul#content-database-list");
|
|
|
|
databaseList.empty();
|
|
availableDatabases.forEach(function(database) {
|
|
var li = $("<li><a href=\"#\">" + database + "</a></li>");
|
|
databaseList.append(li);
|
|
});
|
|
|
|
if (availableDatabases.length == 0) {
|
|
document.getElementById("content-current-database").innerHTML = "…";
|
|
}
|
|
}
|
|
|
|
// when the page is ready, start everything up
|
|
$(document).ready(function () {
|
|
loadSettings();
|
|
|
|
// bind to the settings cog in the navbar
|
|
$("#action-settings").click(function (e) {
|
|
$("#settings").toggle();
|
|
});
|
|
|
|
// bind to the save button in the settings form
|
|
$("#form-settings").submit(function (e) {
|
|
updateSettings();
|
|
});
|
|
|
|
// bind to the items in the query template dropdown
|
|
$("ul#action-template label").click(function (e) {
|
|
var el = $(e.target);
|
|
$("input#query").val(el.data("query")).focus();
|
|
});
|
|
|
|
$("ul#content-database-list").on("click", function(e) {
|
|
if (e.target.tagName != "A") { return; }
|
|
|
|
chooseDatabase(e.target.innerHTML);
|
|
e.preventDefault();
|
|
})
|
|
|
|
// load the Write Data modal
|
|
$("button#action-send").click(function (e) {
|
|
var data = $("textarea#content-data").val();
|
|
|
|
var startTime = new Date().getTime();
|
|
var write = $.post(connectionString() + "/write?db=" + currentlySelectedDatabase, data, function() {
|
|
});
|
|
|
|
write.fail(function (e) {
|
|
if (e.status == 400) {
|
|
showModalError("Failed to write: " + e.responseText)
|
|
}
|
|
else {
|
|
showModalError("Failed to contact server: " + e.statusText)
|
|
}
|
|
});
|
|
|
|
write.done(function (data) {
|
|
var endTime = new Date().getTime();
|
|
var elapsed = endTime - startTime;
|
|
showModalSuccess("Write succeeded. (" + elapsed + "ms)");
|
|
});
|
|
|
|
});
|
|
|
|
// handle submit actions on the query bar
|
|
var form = document.getElementById('query-form');
|
|
form.addEventListener("submit", handleSubmit);
|
|
|
|
// handle keypresses on the query bar so we can get arrow keys and enter
|
|
var query = document.getElementById('query');
|
|
query.addEventListener("keydown", handleKeypress);
|
|
|
|
// make sure we start out with the query bar in focus
|
|
document.getElementById('query').focus();
|
|
})
|