dbviewer 0.4.8 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -20
- data/app/controllers/concerns/dbviewer/database_operations.rb +28 -75
- data/app/controllers/dbviewer/api/base_controller.rb +19 -0
- data/app/controllers/dbviewer/api/database_controller.rb +10 -0
- data/app/controllers/dbviewer/api/queries_controller.rb +28 -0
- data/app/controllers/dbviewer/api/tables_controller.rb +64 -0
- data/app/controllers/dbviewer/application_controller.rb +1 -1
- data/app/controllers/dbviewer/home_controller.rb +1 -93
- data/app/controllers/dbviewer/tables_controller.rb +2 -2
- data/app/views/dbviewer/home/index.html.erb +6 -6
- data/app/views/dbviewer/tables/show.html.erb +135 -2
- data/app/views/layouts/dbviewer/application.html.erb +184 -84
- data/app/views/layouts/dbviewer/shared/_sidebar.html.erb +0 -1
- data/config/routes.rb +18 -7
- data/lib/dbviewer/database_manager.rb +40 -0
- data/lib/dbviewer/engine.rb +15 -0
- data/lib/dbviewer/logger.rb +0 -1
- data/lib/dbviewer/query_parser.rb +0 -10
- data/lib/dbviewer/version.rb +1 -1
- metadata +6 -2
@@ -32,9 +32,11 @@
|
|
32
32
|
/* Base styles and typography */
|
33
33
|
body {
|
34
34
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
35
|
-
font-size: 0.
|
35
|
+
font-size: 0.875rem;
|
36
36
|
line-height: 1.5;
|
37
37
|
letter-spacing: -0.01em;
|
38
|
+
color: #464c54; /* More subdued text color for light theme */
|
39
|
+
background-color: #f5f6f9; /* Subtle gray background */
|
38
40
|
}
|
39
41
|
|
40
42
|
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
@@ -43,11 +45,11 @@
|
|
43
45
|
line-height: 1.25;
|
44
46
|
}
|
45
47
|
|
46
|
-
/* Core layout styles */
|
48
|
+
/* Core layout styles - Grafana-like compact layout */
|
47
49
|
.dbviewer-wrapper { display: flex; flex-direction: column; min-height: 100vh; }
|
48
|
-
.dbviewer-navbar { height:
|
49
|
-
.dbviewer-navbar-spacer { height:
|
50
|
-
.dbviewer-content { display: flex; flex: 1; min-height: calc(100vh -
|
50
|
+
.dbviewer-navbar { height: 48px; } /* Reduced height for more compact header */
|
51
|
+
.dbviewer-navbar-spacer { height: 48px; } /* Creates space for the fixed navbar */
|
52
|
+
.dbviewer-content { display: flex; flex: 1; min-height: calc(100vh - 48px); padding-top: 0; }
|
51
53
|
|
52
54
|
/* Smooth theme transitions */
|
53
55
|
html {
|
@@ -67,19 +69,19 @@
|
|
67
69
|
transition: color 0.2s ease, background-color 0.2s ease;
|
68
70
|
}
|
69
71
|
|
70
|
-
/* Sidebar styles - enhanced for elegance */
|
72
|
+
/* Sidebar styles - enhanced for elegance with Grafana-like compact design */
|
71
73
|
.dbviewer-sidebar {
|
72
|
-
width:
|
73
|
-
height: calc(100vh -
|
74
|
+
width: 240px; /* More compact sidebar width */
|
75
|
+
height: calc(100vh - 48px);
|
74
76
|
position: fixed;
|
75
|
-
top:
|
77
|
+
top: 48px; /* Positioned right below the fixed navbar */
|
76
78
|
left: 0;
|
77
79
|
z-index: 1000;
|
78
80
|
display: flex;
|
79
81
|
flex-direction: column;
|
80
82
|
transition: transform 0.3s ease-in-out, background-color 0.2s ease, border-color 0.2s ease;
|
81
|
-
overflow: hidden;
|
82
|
-
box-shadow:
|
83
|
+
overflow: hidden;
|
84
|
+
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.04); /* Subtler shadow */
|
83
85
|
}
|
84
86
|
|
85
87
|
/* Dark mode overrides */
|
@@ -94,7 +96,7 @@
|
|
94
96
|
}
|
95
97
|
|
96
98
|
.dbviewer-sidebar-header {
|
97
|
-
padding:
|
99
|
+
padding: 0.8rem 1rem; /* Reduced padding for more compact header */
|
98
100
|
font-weight: 600;
|
99
101
|
align-items: center;
|
100
102
|
justify-content: space-between;
|
@@ -157,7 +159,7 @@
|
|
157
159
|
.dbviewer-sidebar-content {
|
158
160
|
flex: 1;
|
159
161
|
overflow-y: auto;
|
160
|
-
padding: 0.
|
162
|
+
padding: 0.25rem 0; /* Reduced padding */
|
161
163
|
height: 100%;
|
162
164
|
/* Improved scrollbar */
|
163
165
|
scrollbar-width: thin;
|
@@ -183,9 +185,9 @@
|
|
183
185
|
|
184
186
|
.dbviewer-main {
|
185
187
|
flex: 1;
|
186
|
-
margin-left:
|
187
|
-
padding: 2rem
|
188
|
-
padding-top:
|
188
|
+
margin-left: 240px; /* Reduced sidebar width */
|
189
|
+
padding: 1.2rem 1.5rem; /* Reduced padding for more compact look */
|
190
|
+
padding-top: 0.8rem; /* Adjusted for fixed header */
|
189
191
|
min-width: 0;
|
190
192
|
animation: fadeIn 0.5s ease-in-out;
|
191
193
|
transition: all 0.3s ease;
|
@@ -281,19 +283,20 @@
|
|
281
283
|
}
|
282
284
|
}
|
283
285
|
|
284
|
-
/* Extra small screens */
|
286
|
+
/* Extra small screens - more compact for small devices */
|
285
287
|
@media (max-width: 575.98px) {
|
286
|
-
.dbviewer-main { padding: 0.
|
288
|
+
.dbviewer-main { padding: 0.6rem; }
|
287
289
|
h1, .h1 { font-size: 1.6rem; }
|
288
290
|
h2, .h2 { font-size: 1.4rem; }
|
289
291
|
h3, .h3 { font-size: 1.2rem; }
|
290
292
|
|
291
293
|
.btn-sm {
|
292
|
-
font-size: 0.
|
294
|
+
font-size: 0.75rem;
|
295
|
+
padding: 0.2rem 0.5rem;
|
293
296
|
}
|
294
297
|
|
295
298
|
.card-body {
|
296
|
-
padding:
|
299
|
+
padding: 0.75rem;
|
297
300
|
}
|
298
301
|
}
|
299
302
|
|
@@ -343,30 +346,31 @@
|
|
343
346
|
border-color: rgba(134, 183, 254, 0.7);
|
344
347
|
}
|
345
348
|
|
346
|
-
/* Table structure and visualization components */
|
349
|
+
/* Table structure and visualization components - Grafana-inspired */
|
347
350
|
.dbviewer-card {
|
348
|
-
border-radius:
|
351
|
+
border-radius: 3px; /* Smaller border radius for Grafana look */
|
349
352
|
transition: all 0.2s ease;
|
350
353
|
overflow: hidden;
|
351
|
-
box-shadow: 0
|
354
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05); /* Subtler shadow */
|
352
355
|
height: 100%;
|
353
356
|
}
|
354
357
|
|
355
358
|
.dbviewer-card:hover {
|
356
|
-
box-shadow: 0
|
357
|
-
transform: translateY(-
|
359
|
+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.07), 0 1px 3px rgba(0, 0, 0, 0.06);
|
360
|
+
transform: translateY(-1px); /* Subtler hover effect */
|
358
361
|
}
|
359
362
|
|
360
|
-
/* Improved card headers */
|
363
|
+
/* Improved card headers - more compact */
|
361
364
|
.dbviewer-card .card-header {
|
362
365
|
font-weight: 600;
|
363
|
-
padding: 1rem
|
366
|
+
padding: 0.7rem 1rem; /* Reduced padding */
|
364
367
|
border-bottom-width: 1px;
|
368
|
+
font-size: 0.95rem; /* Slightly smaller font */
|
365
369
|
}
|
366
370
|
|
367
|
-
/* Card body padding */
|
371
|
+
/* Card body padding - more compact */
|
368
372
|
.dbviewer-card .card-body {
|
369
|
-
padding:
|
373
|
+
padding: 0.9rem 1rem; /* Reduced padding */
|
370
374
|
}
|
371
375
|
|
372
376
|
[data-bs-theme="light"] .dbviewer-card {
|
@@ -483,7 +487,7 @@
|
|
483
487
|
/* List group styling for dark mode */
|
484
488
|
[data-bs-theme="dark"] .list-group-item {
|
485
489
|
background-color: var(--bs-dark);
|
486
|
-
border-color: rgba(255, 255, 255, 0.
|
490
|
+
border-color: rgba(255, 255, 255, 0.08); /* Subtler border */
|
487
491
|
color: var(--bs-light);
|
488
492
|
}
|
489
493
|
|
@@ -494,10 +498,10 @@
|
|
494
498
|
z-index: 1;
|
495
499
|
}
|
496
500
|
|
497
|
-
/* Equal height for timeline and structure cards */
|
501
|
+
/* Equal height for timeline and structure cards - Grafana-inspired compact layout */
|
498
502
|
.two-column-layout .card { height: 100%; display: flex; flex-direction: column; }
|
499
|
-
.two-column-layout .card-body { flex: 1; display: flex; flex-direction: column; }
|
500
|
-
.two-column-layout .chart-container { flex: 1; min-height:
|
503
|
+
.two-column-layout .card-body { flex: 1; display: flex; flex-direction: column; padding: 0.75rem; } /* Reduced padding */
|
504
|
+
.two-column-layout .chart-container { flex: 1; min-height: 220px; } /* Reduced min-height */
|
501
505
|
.two-column-layout .structure-container { padding: 0; }
|
502
506
|
.two-column-layout .tab-content { flex: 1; overflow: hidden; display: flex; flex-direction: column; }
|
503
507
|
.two-column-layout .tab-pane { flex: 1; overflow: hidden; }
|
@@ -560,24 +564,28 @@
|
|
560
564
|
font-size: 1.2rem;
|
561
565
|
}
|
562
566
|
|
563
|
-
/* Header styles for a more professional look */
|
567
|
+
/* Header styles for a more professional Grafana-like look */
|
564
568
|
.navbar {
|
565
569
|
transition: all 0.3s ease;
|
566
|
-
padding: 0.
|
567
|
-
box-shadow: 0
|
570
|
+
padding: 0 0.75rem; /* Reduced horizontal padding */
|
571
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); /* Subtler shadow */
|
572
|
+
min-height: 48px; /* Fixed height */
|
568
573
|
}
|
569
574
|
|
570
575
|
.navbar-brand {
|
571
576
|
font-weight: 600;
|
572
577
|
letter-spacing: -0.01em;
|
578
|
+
font-size: 1.1rem; /* Smaller brand */
|
579
|
+
padding: 0.1rem 0; /* Reduced padding */
|
573
580
|
}
|
574
581
|
|
575
582
|
.nav-link {
|
576
583
|
font-weight: 500;
|
577
|
-
padding: 0.
|
578
|
-
border-radius:
|
584
|
+
padding: 0.35rem 0.65rem !important; /* Reduced padding */
|
585
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
579
586
|
transition: all 0.2s ease;
|
580
|
-
margin: 0
|
587
|
+
margin: 0 1px; /* Reduced margin */
|
588
|
+
font-size: 0.9rem; /* Smaller font */
|
581
589
|
}
|
582
590
|
|
583
591
|
.nav-link.active {
|
@@ -602,11 +610,11 @@
|
|
602
610
|
border-radius: 0;
|
603
611
|
border-left: 0;
|
604
612
|
border-right: 0;
|
605
|
-
padding: 0.
|
613
|
+
padding: 0.5rem 0.85rem; /* Reduced padding for compact look */
|
606
614
|
transition: all 0.15s ease-in-out;
|
607
615
|
position: relative;
|
608
616
|
font-weight: 500;
|
609
|
-
font-size: 0.
|
617
|
+
font-size: 0.85rem; /* Smaller font for Grafana-like appearance */
|
610
618
|
}
|
611
619
|
|
612
620
|
.list-group-item:first-child {
|
@@ -679,23 +687,23 @@
|
|
679
687
|
color: #adb5bd;
|
680
688
|
}
|
681
689
|
|
682
|
-
/* Enhanced button styling */
|
690
|
+
/* Enhanced button styling - Grafana-inspired */
|
683
691
|
.btn {
|
684
692
|
font-weight: 500;
|
685
|
-
padding: 0.
|
686
|
-
border-radius:
|
693
|
+
padding: 0.4rem 0.85rem; /* Reduced padding */
|
694
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
687
695
|
transition: all 0.2s ease;
|
688
|
-
box-shadow: 0 1px
|
696
|
+
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
689
697
|
}
|
690
698
|
|
691
699
|
.btn-sm {
|
692
|
-
padding: 0.
|
693
|
-
font-size: 0.
|
700
|
+
padding: 0.25rem 0.6rem; /* Reduced padding */
|
701
|
+
font-size: 0.8rem; /* Smaller font size */
|
694
702
|
}
|
695
703
|
|
696
704
|
.btn-lg {
|
697
|
-
padding: 0.
|
698
|
-
font-size:
|
705
|
+
padding: 0.5rem 1rem; /* Reduced padding */
|
706
|
+
font-size: 0.95rem; /* Smaller font size */
|
699
707
|
}
|
700
708
|
|
701
709
|
.btn:active {
|
@@ -760,23 +768,23 @@
|
|
760
768
|
text-decoration: underline;
|
761
769
|
}
|
762
770
|
|
763
|
-
/* Enhanced code blocks and SQL query styling */
|
771
|
+
/* Enhanced code blocks and SQL query styling - Grafana-inspired */
|
764
772
|
pre, code {
|
765
773
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
766
|
-
border-radius:
|
774
|
+
border-radius: 2px; /* Smaller radius */
|
767
775
|
transition: all 0.2s ease;
|
768
776
|
}
|
769
777
|
|
770
778
|
pre {
|
771
|
-
padding:
|
772
|
-
margin-bottom:
|
773
|
-
border-radius:
|
774
|
-
box-shadow: 0
|
779
|
+
padding: 0.75rem; /* Reduced padding */
|
780
|
+
margin-bottom: 0.75rem; /* Reduced margin */
|
781
|
+
border-radius: 2px; /* Smaller radius */
|
782
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
775
783
|
}
|
776
784
|
|
777
785
|
code {
|
778
|
-
font-size: 0.
|
779
|
-
padding: 0.
|
786
|
+
font-size: 0.825em; /* Smaller font */
|
787
|
+
padding: 0.15em 0.3em; /* Reduced padding */
|
780
788
|
}
|
781
789
|
|
782
790
|
/* SQL query code in tables */
|
@@ -868,7 +876,7 @@
|
|
868
876
|
color: #ffffff;
|
869
877
|
}
|
870
878
|
|
871
|
-
/* Enhanced table styling */
|
879
|
+
/* Enhanced table styling - Grafana-like compact design */
|
872
880
|
.table {
|
873
881
|
margin-bottom: 0;
|
874
882
|
width: 100%;
|
@@ -881,12 +889,12 @@
|
|
881
889
|
}
|
882
890
|
|
883
891
|
.table thead th {
|
884
|
-
padding:
|
892
|
+
padding: 0.65rem 0.75rem; /* Reduced padding */
|
885
893
|
font-weight: 600;
|
886
894
|
border-top: 0;
|
887
895
|
vertical-align: middle;
|
888
896
|
letter-spacing: 0.01em;
|
889
|
-
font-size: 0.
|
897
|
+
font-size: 0.78rem; /* Smaller font */
|
890
898
|
text-transform: uppercase;
|
891
899
|
position: sticky;
|
892
900
|
top: 0;
|
@@ -895,10 +903,10 @@
|
|
895
903
|
}
|
896
904
|
|
897
905
|
.table tbody td {
|
898
|
-
padding: 0.
|
906
|
+
padding: 0.5rem 0.75rem; /* Reduced padding */
|
899
907
|
vertical-align: middle;
|
900
908
|
border-color: var(--bs-border-color);
|
901
|
-
font-size: 0.
|
909
|
+
font-size: 0.85rem; /* Smaller font */
|
902
910
|
}
|
903
911
|
|
904
912
|
.table tbody tr:hover {
|
@@ -960,14 +968,21 @@
|
|
960
968
|
color: #000 !important;
|
961
969
|
}
|
962
970
|
|
963
|
-
/*
|
971
|
+
/* Badge styling - More Grafana-like */
|
972
|
+
.badge {
|
973
|
+
padding: 0.3em 0.5em;
|
974
|
+
font-size: 0.75em;
|
975
|
+
border-radius: 2px;
|
976
|
+
}
|
977
|
+
|
978
|
+
/* Enhanced alert styling - Grafana-inspired */
|
964
979
|
.alert {
|
965
|
-
border-radius:
|
980
|
+
border-radius: 2px; /* Smaller radius for Grafana look */
|
966
981
|
border-width: 0;
|
967
|
-
padding: 1rem
|
968
|
-
box-shadow: 0
|
982
|
+
padding: 0.7rem 1rem; /* Reduced padding */
|
983
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
969
984
|
position: relative;
|
970
|
-
border-left:
|
985
|
+
border-left: 3px solid transparent; /* Thinner border */
|
971
986
|
}
|
972
987
|
|
973
988
|
.alert-info {
|
@@ -1091,40 +1106,40 @@
|
|
1091
1106
|
background-color: var(--bs-light);
|
1092
1107
|
}
|
1093
1108
|
|
1094
|
-
/* Enhanced stat cards and metric icons */
|
1109
|
+
/* Enhanced stat cards and metric icons - Grafana-inspired */
|
1095
1110
|
.stat-card-bg {
|
1096
1111
|
background: #ffffff;
|
1097
|
-
border-radius:
|
1098
|
-
box-shadow: 0
|
1112
|
+
border-radius: 3px; /* Smaller radius for Grafana look */
|
1113
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.03);
|
1099
1114
|
transition: all 0.3s ease;
|
1100
1115
|
border: none;
|
1101
1116
|
}
|
1102
1117
|
|
1103
1118
|
.stat-card-bg:hover {
|
1104
|
-
transform: translateY(-
|
1105
|
-
box-shadow: 0
|
1119
|
+
transform: translateY(-1px); /* Subtler hover */
|
1120
|
+
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
|
1106
1121
|
}
|
1107
1122
|
|
1108
1123
|
[data-bs-theme="dark"] .stat-card-bg {
|
1109
1124
|
background-color: #212529;
|
1110
|
-
box-shadow: 0
|
1125
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08);
|
1111
1126
|
}
|
1112
1127
|
|
1113
|
-
/* Enhanced metric icon styling */
|
1128
|
+
/* Enhanced metric icon styling - Grafana-inspired */
|
1114
1129
|
.metric-icon {
|
1115
1130
|
display: flex;
|
1116
1131
|
align-items: center;
|
1117
1132
|
justify-content: center;
|
1118
|
-
border-radius:
|
1119
|
-
width:
|
1120
|
-
height:
|
1121
|
-
min-width:
|
1133
|
+
border-radius: 3px; /* Smaller radius */
|
1134
|
+
width: 50px; /* Smaller icon */
|
1135
|
+
height: 50px; /* Smaller icon */
|
1136
|
+
min-width: 50px; /* Smaller icon */
|
1122
1137
|
text-align: center;
|
1123
|
-
background: linear-gradient(135deg, rgba(var(--bs-primary-rgb), 0.
|
1138
|
+
background: linear-gradient(135deg, rgba(var(--bs-primary-rgb), 0.15) 0%, rgba(var(--bs-primary-rgb), 0.08) 100%);
|
1124
1139
|
color: var(--bs-primary);
|
1125
|
-
font-size: 1.
|
1140
|
+
font-size: 1.4rem; /* Smaller icon */
|
1126
1141
|
transition: all 0.3s ease;
|
1127
|
-
box-shadow: 0
|
1142
|
+
box-shadow: 0 1px 5px rgba(var(--bs-primary-rgb), 0.08);
|
1128
1143
|
}
|
1129
1144
|
|
1130
1145
|
.stat-card-bg:hover .metric-icon {
|
@@ -1181,17 +1196,28 @@
|
|
1181
1196
|
transition: all 0.2s ease;
|
1182
1197
|
}
|
1183
1198
|
|
1184
|
-
/* Improved table hover effect */
|
1199
|
+
/* Improved table hover effect - Grafana-like */
|
1185
1200
|
.table-hover > tbody > tr:hover {
|
1186
1201
|
transition: all 0.15s ease;
|
1187
1202
|
}
|
1188
1203
|
|
1189
1204
|
[data-bs-theme="light"] .table-hover > tbody > tr:hover {
|
1190
|
-
background-color: rgba(13, 110, 253, 0.
|
1205
|
+
background-color: rgba(13, 110, 253, 0.03); /* Subtler hover */
|
1191
1206
|
}
|
1192
1207
|
|
1193
1208
|
[data-bs-theme="dark"] .table-hover > tbody > tr:hover {
|
1194
|
-
background-color: rgba(13, 110, 253, 0.
|
1209
|
+
background-color: rgba(13, 110, 253, 0.05); /* Subtler hover */
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
/* Grafana-inspired grid layout adjustments */
|
1213
|
+
.row {
|
1214
|
+
margin-right: -0.5rem;
|
1215
|
+
margin-left: -0.5rem;
|
1216
|
+
}
|
1217
|
+
|
1218
|
+
.row > [class^="col-"] {
|
1219
|
+
padding-right: 0.5rem;
|
1220
|
+
padding-left: 0.5rem;
|
1195
1221
|
}
|
1196
1222
|
|
1197
1223
|
/* Button press effect */
|
@@ -1228,6 +1254,80 @@
|
|
1228
1254
|
[data-bs-theme="dark"] html::-webkit-scrollbar-thumb {
|
1229
1255
|
background-color: rgba(255, 255, 255, 0.2);
|
1230
1256
|
}
|
1257
|
+
|
1258
|
+
/* Grafana-like panel enhancements */
|
1259
|
+
body {
|
1260
|
+
color: #464c54; /* More subdued text color for light theme */
|
1261
|
+
background-color: #f5f6f9; /* Subtle gray background */
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
[data-bs-theme="dark"] body {
|
1265
|
+
background-color: #161719; /* Darker background for dark theme */
|
1266
|
+
color: #d8d9da; /* Softer text color for dark theme */
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
/* Make font sizes consistently smaller for more compact look */
|
1270
|
+
body {
|
1271
|
+
font-size: 0.875rem;
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
h1, .h1 { font-size: 1.6rem; }
|
1275
|
+
h2, .h2 { font-size: 1.4rem; }
|
1276
|
+
h3, .h3 { font-size: 1.2rem; }
|
1277
|
+
h4, .h4 { font-size: 1.1rem; }
|
1278
|
+
h5, .h5 { font-size: 1rem; }
|
1279
|
+
|
1280
|
+
/* Unify panel appearance */
|
1281
|
+
.card, .list-group {
|
1282
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.06);
|
1283
|
+
border: none;
|
1284
|
+
border-radius: 2px;
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
[data-bs-theme="dark"] .card,
|
1288
|
+
[data-bs-theme="dark"] .list-group {
|
1289
|
+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
|
1290
|
+
background-color: #212124;
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
/* Improved tab styling */
|
1294
|
+
.nav-tabs {
|
1295
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
.nav-tabs .nav-link {
|
1299
|
+
border: none;
|
1300
|
+
border-bottom: 2px solid transparent;
|
1301
|
+
background-color: transparent;
|
1302
|
+
padding: 0.5rem 0.75rem;
|
1303
|
+
margin-bottom: -1px;
|
1304
|
+
font-size: 0.9rem;
|
1305
|
+
}
|
1306
|
+
|
1307
|
+
.nav-tabs .nav-link.active {
|
1308
|
+
border-bottom-color: var(--bs-primary);
|
1309
|
+
color: var(--bs-primary);
|
1310
|
+
}
|
1311
|
+
|
1312
|
+
[data-bs-theme="dark"] .nav-tabs {
|
1313
|
+
border-color: rgba(255, 255, 255, 0.1);
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
/* Remove excessive margins between elements */
|
1317
|
+
.row + .row,
|
1318
|
+
.card + .card {
|
1319
|
+
margin-top: 0.75rem;
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
/* Elegant form controls */
|
1323
|
+
.form-control, .form-select {
|
1324
|
+
padding: 0.4rem 0.6rem;
|
1325
|
+
font-size: 0.875rem;
|
1326
|
+
height: auto;
|
1327
|
+
border-radius: 2px;
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
/* Add this right above the style closing tag */
|
1231
1331
|
</style>
|
1232
1332
|
</head>
|
1233
1333
|
<body>
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<div class="dbviewer-sidebar-top">
|
2
2
|
<div class="dbviewer-table-filter-container p-1 mb-0">
|
3
|
-
<i class="bi bi-search dbviewer-table-filter-icon"></i>
|
4
3
|
<input type="text" class="form-control form-control-sm dbviewer-table-filter mb-0"
|
5
4
|
id="tableSearch" placeholder="Filter tables..." aria-label="Filter tables">
|
6
5
|
</div>
|
data/config/routes.rb
CHANGED
@@ -19,13 +19,24 @@ Dbviewer::Engine.routes.draw do
|
|
19
19
|
# Homepage and API endpoints
|
20
20
|
get "dashboard", to: "home#index", as: :dashboard
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
namespace :api do
|
23
|
+
resources :tables, only: [ :index ] do
|
24
|
+
collection do
|
25
|
+
get "records"
|
26
|
+
get "relationships_count"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
resource :database, only: [], controller: "database" do
|
31
|
+
get "size"
|
32
|
+
end
|
33
|
+
|
34
|
+
resources :queries, only: [] do
|
35
|
+
collection do
|
36
|
+
get "recent"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
29
40
|
|
30
41
|
root to: "home#index"
|
31
42
|
end
|
@@ -180,8 +180,48 @@ module Dbviewer
|
|
180
180
|
@cache_manager.clear_all
|
181
181
|
end
|
182
182
|
|
183
|
+
# Calculate the total size of the database schema
|
184
|
+
# @return [Integer, nil] Database size in bytes or nil if unsupported
|
185
|
+
def fetch_schema_size
|
186
|
+
case adapter_name
|
187
|
+
when /mysql/
|
188
|
+
fetch_mysql_size
|
189
|
+
when /postgres/
|
190
|
+
fetch_postgres_size
|
191
|
+
when /sqlite/
|
192
|
+
fetch_sqlite_size
|
193
|
+
else
|
194
|
+
nil # Unsupported database type for size calculation
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
183
198
|
private
|
184
199
|
|
200
|
+
def fetch_mysql_size
|
201
|
+
query = "SELECT SUM(data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = DATABASE()"
|
202
|
+
fetch_size_from_query(query)
|
203
|
+
end
|
204
|
+
|
205
|
+
def fetch_postgres_size
|
206
|
+
query = "SELECT pg_database_size(current_database()) AS size"
|
207
|
+
fetch_size_from_query(query)
|
208
|
+
end
|
209
|
+
|
210
|
+
def fetch_sqlite_size
|
211
|
+
page_count = fetch_sqlite_pragma_value("page_count")
|
212
|
+
page_size = fetch_sqlite_pragma_value("page_size")
|
213
|
+
page_count * page_size
|
214
|
+
end
|
215
|
+
|
216
|
+
def fetch_sqlite_pragma_value(pragma_name)
|
217
|
+
execute_sqlite_pragma(pragma_name).first.values.first.to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
def fetch_size_from_query(query)
|
221
|
+
result = execute_query(query).first
|
222
|
+
result ? result["size"].to_i : nil
|
223
|
+
end
|
224
|
+
|
185
225
|
# Ensure we have a valid database connection
|
186
226
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter] The database connection
|
187
227
|
def ensure_connection
|
data/lib/dbviewer/engine.rb
CHANGED
@@ -20,8 +20,23 @@ module Dbviewer
|
|
20
20
|
initializer "dbviewer.notifications" do
|
21
21
|
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
22
22
|
event = ActiveSupport::Notifications::Event.new(*args)
|
23
|
+
|
24
|
+
next if skip_internal_query?(event)
|
25
|
+
|
23
26
|
Logger.instance.add(event)
|
24
27
|
end
|
25
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def skip_internal_query?(event)
|
33
|
+
caller_locations = caller_locations(1)
|
34
|
+
return false unless caller_locations
|
35
|
+
|
36
|
+
# Look for dbviewer in the call stack
|
37
|
+
caller_locations.any? { |l| l.path.include?("dbviewer") }
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
26
41
|
end
|
27
42
|
end
|
data/lib/dbviewer/logger.rb
CHANGED
@@ -68,15 +68,5 @@ module Dbviewer
|
|
68
68
|
rescue
|
69
69
|
[]
|
70
70
|
end
|
71
|
-
|
72
|
-
# Determine if a query should be skipped based on content
|
73
|
-
def self.should_skip_query?(event)
|
74
|
-
event.payload[:name] == "SCHEMA" ||
|
75
|
-
event.payload[:sql].include?("SHOW TABLES") ||
|
76
|
-
event.payload[:sql].include?("sqlite_master") ||
|
77
|
-
event.payload[:sql].include?("information_schema") ||
|
78
|
-
event.payload[:sql].include?("pg_catalog") ||
|
79
|
-
from_dbviewer?(event)
|
80
|
-
end
|
81
71
|
end
|
82
72
|
end
|
data/lib/dbviewer/version.rb
CHANGED