This commit is contained in:
Dan Sosedoff 2014-10-13 13:55:19 -05:00
parent d579819946
commit 9bd0508219
7 changed files with 450 additions and 186 deletions

6
api.go
View File

@ -11,6 +11,10 @@ type Error struct {
Message string `json:"error"`
}
func API_Home(c *gin.Context) {
c.File("./static/index.html")
}
func API_RunQuery(c *gin.Context) {
query := strings.TrimSpace(c.Request.FormValue("query"))
@ -52,7 +56,7 @@ func API_GetTable(c *gin.Context) {
return
}
c.JSON(200, res.Format())
c.JSON(200, res)
}
func API_History(c *gin.Context) {

View File

@ -78,6 +78,7 @@ func main() {
router := gin.Default()
router.GET("/", API_Home)
router.GET("/info", API_Info)
router.GET("/tables", API_GetTables)
router.GET("/tables/:table", API_GetTable)

226
static/css/app.css Normal file
View File

@ -0,0 +1,226 @@
#nav {
position: fixed;
top: 0;
left: 250;
right: 0;
height: 50px;
box-shadow: inset 0 -1px 0 0 #b8b7b5;
background: #d8d7d6;
}
#nav ul {
margin: 0px;
padding: 0px;
height: 50px;
}
#nav ul li {
margin: 0px;
padding: 0px;
float: left;
height: 49px;
line-height: 49px;
font-size: 13px;
padding: 0px 14px;
color: #6c6b6b;
font-weight: 500;
margin: 0 1px;
cursor: pointer;
}
#nav ul li:first-child {
margin-left: 0px;
}
#nav ul li:hover {
color: rgba(0,0,0,0.9);
}
#nav ul li.selected {
position: relative;
background: #fff;
color: #000;
border-right: 1px solid #b8b7b5;
border-left: 1px solid #b8b7b5;
margin: 0;
}
#nav ul li.selected:before {
content: ' ';
position: absolute;
bottom: -1px;
left: 0; right: 0;
height: 1px;
widows: 100%;
background: #fff;
}
#nav ul li.selected:first-child {
border-left: none;
}
#sidebar {
position: fixed;
width: 250px;
left: 0;
top: 0;
bottom: 0;
background: #3a3633;
border-right: 1px solid #b4b4b4;
font-size: 13px;
overflow: scroll;
}
#sidebar .wrapper {
height: 100%;
}
#sidebar ul {
margin: 0px;
padding: 0px;
}
#sidebar ul li {
color: #000;
}
#sidebar ul li:hover {
color: #fff !important;
}
#body {
position: fixed;
top: 50px;
left: 250px;
bottom: 0px;
right: 0px;
overflow: scroll;
}
#input {
height: 255px;
overflow: hidden;
}
#input .wrapper {
position: relative;
}
#input .actions {
background: #fff;
padding: 10px;
height: 50px;
border-top: solid 1px #e1e2e3;
border-bottom: solid 1px #b8b7b5;
}
#input .actions .btn {
line-height: 30px;
height: 30px;
padding: 0px 13px;
margin: 0px;
font-size: 13px;
color: #fff;
border: none;
box-shadow: none;
background: #7eb54e;
}
#input .actions .btn:focus {
outline: 0 none;
box-shadow: 0;
}
#input .actions .btn:hover {
background: #7eb154;
}
#output {
position: absolute;
left: 0px;
top: 256px;
bottom: 0px;
right: 0px;
margin: 0px;
padding: 0px;
overflow: scroll;
}
#output.full {
top: 0px;
}
#results {
font-size: 12px;
margin: 0px;
padding: 0px;
}
#results.empty td {
border: 0px none;
}
#results tr:nth-child(even) > td {
border: none;
background: #f5f5f5;
}
#results tr:nth-child(odd) > td {
border: none;
background: #fff;
}
#results th {
background: #f3f3f3;
border-top: none;
border-bottom: 1px solid #eae9e9;
padding: 3px 9px;
line-height: 24px;
text-transform: uppercase;
font-size: 10px;
color: #6a6a6a;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
#results tr.selected td {
background: #3874d7;
color: #fff !important;
}
#results td {
color: #3a3633;
max-width: 200px;
overflow: hidden;
vertical-align: middle;
border: 0px none;
}
#results th:first-child,
#results td:first-child {
padding-left: 15px;
}
#custom_query {
height: 205px;
}
#sidebar ul {
padding: 0px;
margin: 0px;
}
#sidebar ul li {
list-style: none;
list-style-type: none;
line-height: 28px;
padding: 0px 8px;
cursor: pointer;
color: #a8a8a8 !important;
}
#sidebar li.selected {
color: #fff !important;
font-weight: bold;
background: rgba(69,69,69,.6);
-webkit-font-smoothing: antialiased;
}

View File

@ -1,191 +1,34 @@
<style>
#nav {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 39px;
background: #ccc;
border-bottom: 1px solid #aaa;
}
#sidebar {
width: 249px;
position: absolute;
left: 0;
top: 40;
bottom: 0;
background: #fff;
overflow: scroll;
border-right: 1px solid #aaa;
font-size: 13px;
}
#body {
position: absolute;
top: 40px;
left: 250px;
bottom: 0px;
right: 0px;
overflow: scroll;
}
#input {
position: absolute;
border-bottom: 1px solid #aaa;
left: 0px;
right: 0px;
top: 0px;
height: 249px;
}
#output {
position: absolute;
left: 0px;
top: 250px;
bottom: 0px;
right: 0px;
margin: 0px;
padding: 0px;
overflow: scroll;
}
#results {
font-size: 13px;
}
#results td {
max-width: 200px;
overflow: hidden;
}
#custom_query {
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}
ul {
padding: 0px;
margin: 0px;
}
ul li {
list-style: none;
list-style-type: none;
line-height: 25px;
padding: 0px 8px;
cursor: pointer;
}
ul li:hover {
background: #ddd;
}
</style>
<div id="nav"></div>
<div id="sidebar"><ul id="tables"></ul></div>
<div id="nav">
<ul>
<li id="table_content">Content</li>
<li id="table_structure">Structure</li>
<li id="table_indexes">Indexes</li>
<li id="table_query" class="selected">Run Query</li>
<li id="table_history">History</li>
</ul>
</div>
<div id="sidebar">
<div class="wrapper"><ul id="tables"></ul></div>
</div>
<div id="body">
<div id="input">
<textarea id="custom_query"></textarea>
<input type="button" value="Run" class="btn btn-sm btn-primary" id="run" />
<a href="#" class="btn btn-default btn-sm">Query History</a>
<div class="wrapper">
<div id="custom_query"></div>
<div class="actions">
<input type="button" id="run" value="Run Query" class="btn btn-sm btn-primary" />
</div>
</div>
</div>
<div id="output">
<div class="wrapper">
<table id="results" class="table table-striped"></table>
</div>
</div>
</div>
<link rel="stylesheet" href="/app/css/bootstrap.min.css" />
<script type="text/javascript"></script>
<script type="text/javascript" src="/app/js/jquery.min.js"></script>
<script type="text/javascript" src="/app/js/ace.js"></script>
<script type="text/javascript" src="/app/js/ace-pgsql.js"></script>
<script type="text/javascript">
function getTables(cb) {
$.getJSON("/tables", function(resp) {
cb(resp);
});
}
function executeQuery(query, cb) {
$.ajax({
url: "/query",
method: "post",
cache: false,
data: { query: query, format: "json" },
success: function(data) {
cb(data);
},
error: function(xhr, status, data) {
cb(jQuery.parseJSON(xhr.responseText));
}
});
}
function buildTable(results) {
$("#results").text("");
if (!results.rows) {
$("<tr><td>No records found</tr></tr>").appendTo("#results");
return;
}
if (results.error) {
$("<tr><td>ERROR: " + results.error + "</tr></tr>").appendTo("#results");
return;
}
var cols = "";
var rows = ""
results.columns.forEach(function(col) {
cols += "<th>" + col + "</th>";
});
results.rows.forEach(function(row) {
var r = "";
for (i in row) { r += "<td>" + row[i] + "</td>"; }
rows += "<tr>" + r + "</tr>";
});
$("<thead>" + cols + "</thead><tbody>" + rows + "</tobdy>").appendTo("#results");
}
$(document).ready(function() {
/*
var editor = ace.edit("custom_query");
editor.getSession().setMode("ace/mode/pgsql");
editor.getSession().setTabSize(2);
editor.getSession().setUseSoftTabs(true);
*/
$("#run").on("click", function() {
var query = $("#custom_query").val();
executeQuery(query, function(data) {
buildTable(data);
});
});
$("#tables").on("click", "li", function() {
var query = "SELECT * FROM " + $(this).text() + " ORDER BY id DESC LIMIT 100";
executeQuery(query, function(data) {
buildTable(data);
});
});
getTables(function(data) {
data.forEach(function(item) {
$("<li>" + item + "</li>").appendTo("#tables");
});
});
});
</script>
<link rel="stylesheet" href="/static/css/bootstrap.css" />
<link rel="stylesheet" href="/static/css/app.css" />
<script type="text/javascript" src="/static/js/jquery.js"></script>
<script type="text/javascript" src="/static/js/ace.js"></script>
<script type="text/javascript" src="/static/js/ace-pgsql.js"></script>
<script type="text/javascript" src="/static/js/app.js"></script>

191
static/js/app.js Normal file
View File

@ -0,0 +1,191 @@
function apiCall(path, cb) {
$.getJSON(path, function(resp) { cb(resp); });
}
function getTables(cb) { apiCall("/tables", cb); }
function getTableStructure(table, cb) { apiCall("/tables/" + table, cb); }
function getTableIndexes(table, cb) { apiCall("/tables/" + table + "/indexes", cb); }
function getHistory(cb) { apiCall("/history", cb); }
function executeQuery(query, cb) {
$.ajax({
url: "/query",
method: "post",
cache: false,
data: { query: query, format: "json" },
success: function(data) {
cb(data);
},
error: function(xhr, status, data) {
cb(jQuery.parseJSON(xhr.responseText));
}
});
}
function loadTables() {
getTables(function(data) {
data.forEach(function(item) {
$("<li>" + item + "</li>").appendTo("#tables");
});
});
}
function buildTable(results) {
$("#results").text("").removeClass("empty");
if (results.error) {
$("<tr><td>ERROR: " + results.error + "</tr></tr>").appendTo("#results");
$("#results").addClass("empty");
return;
}
if (!results.rows) {
$("<tr><td>No records found</tr></tr>").appendTo("#results");
$("#results").addClass("empty");
return;
}
var cols = "";
var rows = ""
results.columns.forEach(function(col) {
cols += "<th>" + col + "</th>";
});
results.rows.forEach(function(row) {
var r = "";
for (i in row) { r += "<td>" + row[i] + "</td>"; }
rows += "<tr>" + r + "</tr>";
});
$("<thead>" + cols + "</thead><tbody>" + rows + "</tobdy>").appendTo("#results");
}
function setCurrentTab(id) {
$("#nav ul li.selected").removeClass("selected");
$("#" + id).addClass("selected");
}
function showQueryHistory() {
getHistory(function(data) {
var rows = [];
for(i in data) {
rows.unshift([parseInt(i) + 1, data[i]]);
}
buildTable({ columns: ["id", "query"], rows: rows });
setCurrentTab("table_history");
$("#input").hide();
$("#output").addClass("full");
});
}
function showTableIndexes() {
var name = $("#tables li.selected").text();
if (name.length == 0) {
alert("Please select a table!");
return;
}
getTableIndexes(name, function(data) {
setCurrentTab("table_indexes");
buildTable(data);
$("#input").hide();
$("#output").addClass("full");
});
}
function showTableContent() {
var name = $("#tables li.selected").text();
if (name.length == 0) {
alert("Please select a table!");
return;
}
var query = "SELECT * FROM " + name + " LIMIT 100;";
executeQuery(query, function(data) {
buildTable(data);
setCurrentTab("table_content");
$("#input").hide();
$("#output").addClass("full");
});
}
function showTableStructure() {
var name = $("#tables li.selected").text();
if (name.length == 0) {
alert("Please select a table!");
return;
}
getTableStructure(name, function(data) {
setCurrentTab("table_structure");
buildTable(data);
});
}
function runQuery() {
setCurrentTab("table_query");
executeQuery(editor.getValue(), function(data) {
buildTable(data);
$("#input").show();
$("#output").removeClass("full");
});
}
var editor;
$(document).ready(function() {
editor = ace.edit("custom_query");
editor.getSession().setMode("ace/mode/pgsql");
editor.getSession().setTabSize(2);
editor.getSession().setUseSoftTabs(true);
$("#table_content").on("click", function() {
showTableContent();
});
$("#table_structure").on("click", function() {
showTableStructure();
});
$("#table_indexes").on("click", function() {
showTableIndexes();
});
$("#table_history").on("click", function() {
showQueryHistory();
});
$("#table_query").on("click", function() {
setCurrentTab("table_query");
$("#input").show();
$("#output").removeClass("full");
});
$("#run").on("click", function() {
runQuery();
});
$("#results").on("click", "tr", function() {
$("#results tr.selected").removeClass();
$(this).addClass("selected");
});
$("#tables").on("click", "li", function() {
$("#tables li.selected").removeClass("selected");
$(this).addClass("selected");
showTableContent();
});
loadTables();
});

File diff suppressed because one or more lines are too long