mirror of
https://github.com/danbulant/mysqlExporter
synced 2026-05-19 04:18:42 +00:00
401 lines
No EOL
16 KiB
HTML
401 lines
No EOL
16 KiB
HTML
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MySQL exporter</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap" rel="stylesheet">
|
|
</head>
|
|
|
|
<body>
|
|
<div id="app">Loading...</div>
|
|
|
|
<style>
|
|
body {
|
|
margin: 30px auto;
|
|
max-width: 900px;
|
|
line-height: 1.6;
|
|
font-size: 18px;
|
|
color: #444;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
* {
|
|
font-family: 'Noto Sans', sans-serif;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
font-family: Simplifica;
|
|
line-height: 1.2
|
|
}
|
|
|
|
a {
|
|
text-decoration: none;
|
|
color: rgb(0, 140, 255);
|
|
}
|
|
|
|
a:visited {
|
|
color: rgb(153, 0, 255);
|
|
}
|
|
|
|
table,
|
|
th,
|
|
td {
|
|
border: 1px solid black;
|
|
border-collapse: collapse;
|
|
word-break: normal;
|
|
text-align: center;
|
|
}
|
|
|
|
table {
|
|
width: 900px;
|
|
width: min(90vw, 900px);
|
|
table-layout: fixed;
|
|
}
|
|
|
|
th {
|
|
padding: 10px;
|
|
}
|
|
|
|
td {
|
|
padding: 5px;
|
|
word-break: break-all;
|
|
}
|
|
|
|
tr:hover {
|
|
background-color: rgb(214, 214, 214);
|
|
}
|
|
|
|
.privileges {
|
|
word-break: normal;
|
|
}
|
|
|
|
.null,
|
|
.key {
|
|
width: 30px;
|
|
}
|
|
|
|
.extra {
|
|
width: 35px;
|
|
}
|
|
|
|
.default {
|
|
width: 70px;
|
|
}
|
|
|
|
.head {
|
|
background-color: #444;
|
|
color: white;
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
</style>
|
|
|
|
<!--
|
|
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24"
|
|
width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
<g transform="translate(0 -1028.4)">
|
|
<g transform="matrix(.70711 .70711 -.70711 .70711 740.06 298.16)">
|
|
<path
|
|
d="m10.541 1028.9c-3.3134 0-5.9997 2.6-5.9997 6 0 3.3 2.6863 6 5.9997 6 3.314 0 6-2.7 6-6 0-3.4-2.686-6-6-6zm0 2c1.105 0 2 0.9 2 2s-0.895 2-2 2c-1.1042 0-1.9997-0.9-1.9997-2s0.8955-2 1.9997-2z"
|
|
fill="#f39c12" />
|
|
<g fill="#f1c40f">
|
|
<path
|
|
d="m10 0c-3.3137 0-6 2.6863-6 6s2.6863 6 6 6c3.314 0 6-2.6863 6-6s-2.686-6-6-6zm0 2c1.105 0 2 0.8954 2 2s-0.895 2-2 2c-1.1046 0-2-0.8954-2-2s0.8954-2 2-2z"
|
|
transform="translate(0 1028.4)" />
|
|
<rect height="2" width="6" y="1039.4" x="7" />
|
|
<path d="m8 13v9l2 2 2-2v-1l-2-1 2-1v-1l-1-1 1-1v-3z" transform="translate(0 1028.4)" />
|
|
</g>
|
|
<path d="m11 1041.4v4l1-1v-3h-1zm0 4v2.5l1-0.5v-1l-1-1zm0 3.5v2.5l1-1v-1l-1-0.5z" fill="#f39c12" />
|
|
<path d="m9 1041.4v10l1 1v-4-7h-1z" fill="#f39c12" />
|
|
</g>
|
|
</g>
|
|
</svg> -->
|
|
|
|
|
|
<script>
|
|
const func = e => {
|
|
var id = (e.target.id.startsWith("hidden_") ? "" : "hidden_") + e.target.id;
|
|
var idShort = (!e.target.id.startsWith("hidden_") ? e.target.id : e.target.id.substr("hidden_".length));
|
|
|
|
let hidden = document.getElementById(id);
|
|
hidden.classList.toggle("hidden");
|
|
|
|
if (hidden.classList.contains("hidden")) {
|
|
document.getElementById(idShort).innerText += "...";
|
|
} else {
|
|
document.getElementById(idShort).innerText = document.getElementById(idShort).innerText.substr(0,
|
|
document.getElementById(idShort).innerText.length - 3);
|
|
}
|
|
};
|
|
|
|
let s = document.getElementsByClassName("hiddable");
|
|
if(s) {
|
|
for(var el of s) {
|
|
el.onclick = func;
|
|
}
|
|
}
|
|
|
|
function key(title, color, color2, color3) {
|
|
return `<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24"
|
|
width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
<title>${title}</title>
|
|
<g transform="translate(0 -1028.4)">
|
|
<g transform="matrix(.70711 .70711 -.70711 .70711 740.06 298.16)">
|
|
<path
|
|
d="m10.541 1028.9c-3.3134 0-5.9997 2.6-5.9997 6 0 3.3 2.6863 6 5.9997 6 3.314 0 6-2.7 6-6 0-3.4-2.686-6-6-6zm0 2c1.105 0 2 0.9 2 2s-0.895 2-2 2c-1.1042 0-1.9997-0.9-1.9997-2s0.8955-2 1.9997-2z"
|
|
fill="${color}" />
|
|
<g fill="${color2}">
|
|
<path
|
|
d="m10 0c-3.3137 0-6 2.6863-6 6s2.6863 6 6 6c3.314 0 6-2.6863 6-6s-2.686-6-6-6zm0 2c1.105 0 2 0.8954 2 2s-0.895 2-2 2c-1.1046 0-2-0.8954-2-2s0.8954-2 2-2z"
|
|
transform="translate(0 1028.4)" />
|
|
<rect height="2" width="6" y="1039.4" x="7" />
|
|
<path d="m8 13v9l2 2 2-2v-1l-2-1 2-1v-1l-1-1 1-1v-3z" transform="translate(0 1028.4)" />
|
|
</g>
|
|
<path d="m11 1041.4v4l1-1v-3h-1zm0 4v2.5l1-0.5v-1l-1-1zm0 3.5v2.5l1-1v-1l-1-0.5z" fill="${color3}" />
|
|
<path d="m9 1041.4v10l1 1v-4-7h-1z" fill="${color3}" />
|
|
</g>
|
|
</g>
|
|
</svg>`;
|
|
}
|
|
|
|
function multikey() {
|
|
return `<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
width="22.000000pt" height="24.000000pt" viewBox="0 0 1113.000000 1280.000000"
|
|
preserveAspectRatio="xMidYMid meet">
|
|
<g transform="translate(0.000000,1280.000000) scale(0.100000,-0.100000)"
|
|
fill="#000000" stroke="none">
|
|
<title>Multi key</title>
|
|
<path d="M3130 12789 c-1268 -111 -2354 -895 -2856 -2061 -328 -763 -361
|
|
-1656 -90 -2445 377 -1097 1288 -1932 2415 -2213 146 -37 372 -76 491 -85 140
|
|
-10 128 3 114 -122 -97 -823 340 -1627 1086 -1999 266 -132 520 -196 840 -211
|
|
l145 -6 113 -146 c62 -80 188 -237 280 -348 l167 -202 50 -6 c190 -26 347
|
|
-142 400 -295 39 -110 39 -273 1 -417 l-14 -51 47 -7 c243 -34 450 -220 517
|
|
-466 23 -83 23 -269 1 -359 -8 -36 -15 -66 -14 -66 1 -1 45 -16 97 -34 132
|
|
-43 213 -91 296 -174 124 -125 170 -252 161 -452 l-5 -121 242 -151 c320 -200
|
|
580 -354 583 -345 12 39 98 449 117 560 l25 142 -110 243 c-105 230 -455 1008
|
|
-796 1768 -191 425 -623 1377 -690 1521 l-50 105 73 97 c134 178 234 369 304
|
|
580 18 54 34 96 36 94 3 -2 -7 -41 -21 -87 -15 -45 -24 -84 -21 -87 3 -4 53 6
|
|
110 21 l105 27 173 -119 c95 -66 261 -177 367 -246 182 -118 197 -126 230
|
|
-119 20 4 74 7 121 7 186 -1 326 -87 404 -249 42 -86 65 -179 73 -291 5 -73 8
|
|
-84 22 -78 32 13 183 16 244 4 34 -6 98 -29 142 -49 215 -101 341 -287 376
|
|
-554 l12 -94 121 -6 c210 -10 343 -63 457 -185 69 -74 132 -196 150 -292 19
|
|
-106 14 -101 130 -131 412 -107 823 -209 826 -205 9 8 -18 420 -37 572 -17
|
|
136 -22 161 -43 183 -141 153 -741 829 -1763 1987 -243 274 -532 600 -643 724
|
|
-123 138 -200 231 -197 240 3 8 22 65 44 125 226 626 105 1343 -316 1884 -182
|
|
233 -441 439 -722 573 -158 75 -403 146 -593 173 -48 6 -49 7 -43 38 31 179
|
|
41 617 18 822 -70 615 -281 1173 -630 1660 -542 757 -1386 1271 -2297 1399
|
|
-249 35 -549 45 -775 25z m516 -559 c490 -39 946 -195 1354 -464 913 -602
|
|
1398 -1656 1264 -2746 -26 -210 -12 -187 -139 -220 -479 -122 -894 -417 -1168
|
|
-829 -62 -94 -197 -353 -197 -378 0 -5 -39 -20 -86 -31 -185 -47 -410 -148
|
|
-579 -259 -282 -186 -487 -412 -670 -737 l-27 -49 -122 7 c-579 33 -1110 227
|
|
-1581 580 -147 110 -420 381 -526 521 -336 443 -528 922 -584 1457 -136 1303
|
|
625 2523 1859 2982 366 137 804 197 1202 166z m2326 -4146 c7 -7 -67 -142
|
|
-139 -254 -46 -71 -182 -260 -187 -260 -16 0 -199 40 -203 44 -4 3 -1 38 6 77
|
|
34 185 171 334 354 384 52 14 159 20 169 9z m438 -466 c0 -72 -24 -167 -59
|
|
-236 -52 -101 -49 -99 -109 -62 -29 17 -52 34 -52 36 0 2 24 37 53 77 29 41
|
|
75 111 102 155 28 45 53 82 57 82 5 0 8 -24 8 -52z m-1507 -623 c26 -9 47 -20
|
|
47 -24 0 -8 -163 -103 -260 -151 -103 -52 -217 -99 -323 -136 -113 -38 -111
|
|
-41 -56 73 54 113 176 210 310 248 68 19 215 14 282 -10z m313 -562 c-4 -21
|
|
-22 -70 -41 -109 -58 -119 -189 -224 -316 -253 -105 -25 -266 -7 -341 37 -28
|
|
17 -37 32 -17 32 47 0 495 203 639 289 36 22 69 40 74 40 4 1 5 -16 2 -36z"/>
|
|
</g>
|
|
</svg>`;
|
|
}
|
|
const app = document.getElementById("app");
|
|
|
|
function htmlToElement(html) {
|
|
var template = document.createElement('template');
|
|
html = html.trim(); // Never return a text node of whitespace as the result
|
|
template.innerHTML = html;
|
|
return template.content.firstChild;
|
|
}
|
|
|
|
(async () => {
|
|
if (window.location.hostname !== "localhost") {
|
|
console.warn("Fetching info supported only from localhost!");
|
|
return;
|
|
}
|
|
|
|
var dbRes = await fetch("/database");
|
|
var db = await dbRes.json();
|
|
if (!dbRes.ok) return console.warn("Couldn't fetch data");
|
|
|
|
app.innerText = "";
|
|
|
|
const appTitle = document.createElement("h1");
|
|
appTitle.innerText = "Data dictionary of " + db.name;
|
|
app.appendChild(appTitle);
|
|
|
|
const date = document.createElement("p");
|
|
date.innerText = "Loading table information...";
|
|
app.appendChild(date);
|
|
|
|
const tableList = document.createElement("ul");
|
|
app.appendChild(tableList);
|
|
|
|
var res = await fetch("/tables");
|
|
var tables = await res.json();
|
|
var re = await fetch("/fieldsIgnored");
|
|
var fieldsIgnored = await re.json();
|
|
fieldsIgnored = fieldsIgnored.map(el => el.toLowerCase());
|
|
|
|
date.innerText = "Generating data dictionary...";
|
|
|
|
for (var table in tables) {
|
|
var data = tables[table];
|
|
var tableEl = document.createElement("table");
|
|
var title = document.createElement("h2");
|
|
title.id = table.replace(/[\s]/g, "");
|
|
title.innerText = table;
|
|
app.appendChild(title);
|
|
|
|
if (!data.columns) {
|
|
console.error(data);
|
|
app.appendChild(htmlToElement(`<p>${data}<p>`));
|
|
continue;
|
|
}
|
|
|
|
var tableItem = document.createElement("li");
|
|
var tableLink = document.createElement("a");
|
|
tableItem.appendChild(tableLink);
|
|
tableLink.href = "#" + table.replace(/[\s]/g, "");
|
|
tableLink.innerText = table;
|
|
tableList.appendChild(tableItem);
|
|
|
|
try {
|
|
var comment = document.createElement("p");
|
|
comment.innerText = data.comment[0].table_comment;
|
|
app.appendChild(comment);
|
|
} catch (e) {}
|
|
|
|
function addField(fieldName, rename = fieldName) {
|
|
return fieldsIgnored.includes(fieldName.toLowerCase()) ? "" :
|
|
`<th class='${fieldName.toLowerCase()}'>${rename}</th>`;
|
|
}
|
|
|
|
var head = htmlToElement(`
|
|
<tr class="head">
|
|
${addField("Field")}
|
|
${addField("Type")}
|
|
${addField("Collation")}
|
|
${addField("Null", "NN")}
|
|
${addField("Key")}
|
|
${addField("Default")}
|
|
${addField("Extra")}
|
|
${addField("Privileges")}
|
|
${addField("Comment")}
|
|
</tr>
|
|
`);
|
|
|
|
tableEl.appendChild(head);
|
|
app.appendChild(tableEl);
|
|
for (var column of data.columns) {
|
|
var el = document.createElement("tr");
|
|
el.id = "column_" + table + "_" + column.Field;
|
|
|
|
for (var field in column) {
|
|
if (fieldsIgnored.includes(field.toLowerCase())) continue;
|
|
var td = document.createElement("td");
|
|
td.className = field.toLowerCase();
|
|
|
|
switch (field.toLowerCase()) {
|
|
case "privileges":
|
|
column[field] = column[field].split(",").join(", ");
|
|
break;
|
|
case "null":
|
|
let cb = document.createElement("input");
|
|
cb.type = "checkbox";
|
|
cb.checked = column[field] === "NO";
|
|
cb.disabled = true;
|
|
column[field] = cb;
|
|
break;
|
|
case "comment":
|
|
var text = column[field];
|
|
column[field] = document.createElement("span");
|
|
var span = column[field];
|
|
if (text.length < 20) {
|
|
span.innerText = text;
|
|
break;
|
|
}
|
|
var shown = document.createElement("span");
|
|
shown.innerText = text.substr(0, 15) + "...";
|
|
var hidden = document.createElement("span");
|
|
hidden.innerText = text.substr(15);
|
|
hidden.className = "hidden";
|
|
hidden.id = "hidden_span_" + table + "_" + column.Field;
|
|
shown.id = "span_" + table + "_" + column.Field;
|
|
|
|
span.onclick = func;
|
|
span.className = "hiddable";
|
|
|
|
// hidden.onclick = e => {
|
|
// let hidden = document.getElementById(e.target.id);
|
|
// hidden.classList.toggle("hidden");
|
|
// }
|
|
|
|
span.appendChild(shown);
|
|
span.appendChild(hidden);
|
|
|
|
break;
|
|
case "key":
|
|
switch (column[field]) {
|
|
case "PRI":
|
|
column[field] = htmlToElement(key("Primary key", "#f39c12", "#f1c40f",
|
|
"#f39c12"));
|
|
break;
|
|
case "UNI":
|
|
column[field] = htmlToElement(key("Unique key", "#bfbfbf", "#dbdbdb",
|
|
"#808080"));
|
|
break;
|
|
case "MUL":
|
|
column[field] = "";
|
|
break;
|
|
case "":
|
|
break;
|
|
default:
|
|
console.error("Unknown key:", column[field]);
|
|
}
|
|
case "extra":
|
|
if (column[field] === "auto_increment") {
|
|
column[field] = "AI";
|
|
td.title = "Auto increment";
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (column[field]) {
|
|
if (column[field] instanceof HTMLElement || column[field] instanceof SVGElement) {
|
|
td.appendChild(column[field]);
|
|
} else {
|
|
td.innerText = column[field];
|
|
}
|
|
} else if (field == "Default") {
|
|
td.innerText = "null";
|
|
}
|
|
el.appendChild(td);
|
|
}
|
|
|
|
tableEl.appendChild(el);
|
|
|
|
let t = document.querySelector(`#${el.id} > .key`);
|
|
|
|
var d = data.indexes.filter(e => e.Column_name === column.Field);
|
|
if (d[0] && !t.childElementCount)
|
|
t.appendChild(htmlToElement(multikey()));
|
|
}
|
|
}
|
|
|
|
var d = new Date();
|
|
date.innerHTML = `Generated ${d.toString()}.
|
|
<br>Number of tables: ${Object.keys(tables).length}
|
|
<br>Host: ${db.host}`;
|
|
})()
|
|
</script>
|
|
</body>
|
|
|
|
</html> |