rails_nexus 2.0.3 → 2.1.0
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/app/assets/javascripts/rails_nexus/controllers.js +103 -6
- data/app/assets/stylesheets/rails_nexus/application.css +418 -42
- data/app/controllers/rails_nexus/backup_controller.rb +27 -19
- data/app/helpers/rails_nexus/application_helper.rb +5 -0
- data/app/javascript/controllers/rails_nexus_controller.js +8 -2
- data/app/javascript/controllers/rails_nexus_sidebar_controller.js +84 -3
- data/app/jobs/rails_nexus/backup_job.rb +18 -0
- data/app/models/rails_nexus/backup.rb +11 -11
- data/app/models/rails_nexus/backup_config.rb +84 -16
- data/app/services/rails_nexus/backup_service.rb +521 -115
- data/app/views/layouts/rails_nexus/_sidebar_navigation.html.erb +113 -0
- data/app/views/layouts/rails_nexus/application.html.erb +132 -26
- data/app/views/rails_nexus/backup/_form.html.erb +186 -37
- data/app/views/rails_nexus/backup/history.html.erb +17 -2
- data/app/views/rails_nexus/backup/index.html.erb +29 -72
- data/app/views/rails_nexus/logged_exceptions/_exceptions.html.erb +24 -55
- data/app/views/rails_nexus/logged_exceptions/index.html.erb +24 -102
- data/db/seeds/backup_configs.rb +140 -0
- data/lib/generators/rails_nexus/templates/migration.rb +32 -6
- data/lib/rails_nexus/version.rb +1 -1
- data/lib/tasks/rails_nexus.rake +14 -4
- metadata +4 -1
|
@@ -33,6 +33,7 @@ table { border-collapse: collapse; width: 100%; }
|
|
|
33
33
|
/* ─── Tailwind-like Utility Classes ─────────────────────────── */
|
|
34
34
|
|
|
35
35
|
/* Display */
|
|
36
|
+
[hidden] { display: none !important; }
|
|
36
37
|
.hidden { display: none !important; }
|
|
37
38
|
.block { display: block; }
|
|
38
39
|
.inline-block { display: inline-block; }
|
|
@@ -1026,22 +1027,30 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1026
1027
|
/* ─── Sidebar ───────────────────────────────────────────────── */
|
|
1027
1028
|
.rn-sidebar {
|
|
1028
1029
|
width: 16rem;
|
|
1029
|
-
|
|
1030
|
-
|
|
1030
|
+
overflow: hidden;
|
|
1031
|
+
background: var(--rn-surface);
|
|
1032
|
+
color: var(--rn-text-secondary);
|
|
1031
1033
|
display: flex;
|
|
1032
1034
|
flex-direction: column;
|
|
1033
1035
|
flex-shrink: 0;
|
|
1034
|
-
border-right: 1px solid var(--rn-
|
|
1036
|
+
border-right: 1px solid var(--rn-border);
|
|
1035
1037
|
position: sticky;
|
|
1036
1038
|
top: 0;
|
|
1037
1039
|
height: 100vh;
|
|
1038
1040
|
z-index: 30;
|
|
1041
|
+
transition: width 200ms ease, opacity 150ms ease, border-color 150ms ease;
|
|
1042
|
+
}
|
|
1043
|
+
.rn-sidebar.is-hidden {
|
|
1044
|
+
width: 0;
|
|
1045
|
+
border-right-color: transparent;
|
|
1046
|
+
opacity: 0;
|
|
1047
|
+
pointer-events: none;
|
|
1039
1048
|
}
|
|
1040
1049
|
@media (max-width: 1023px) { .rn-sidebar { display: none; } }
|
|
1041
1050
|
|
|
1042
1051
|
.rn-sidebar-logo {
|
|
1043
1052
|
padding: 1rem;
|
|
1044
|
-
border-bottom: 1px solid var(--rn-
|
|
1053
|
+
border-bottom: 1px solid var(--rn-border);
|
|
1045
1054
|
display: flex;
|
|
1046
1055
|
align-items: center;
|
|
1047
1056
|
justify-content: space-between;
|
|
@@ -1061,44 +1070,150 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1061
1070
|
flex-shrink: 0;
|
|
1062
1071
|
}
|
|
1063
1072
|
.rn-logo-text {
|
|
1073
|
+
display: block;
|
|
1064
1074
|
font-size: 0.9375rem;
|
|
1065
1075
|
font-weight: 700;
|
|
1066
1076
|
letter-spacing: -0.01em;
|
|
1067
|
-
color:
|
|
1077
|
+
color: var(--rn-text);
|
|
1078
|
+
}
|
|
1079
|
+
.rn-logo-copy {
|
|
1080
|
+
display: flex;
|
|
1081
|
+
min-width: 0;
|
|
1082
|
+
flex-direction: column;
|
|
1083
|
+
line-height: 1.15;
|
|
1084
|
+
}
|
|
1085
|
+
.rn-logo-subtitle {
|
|
1086
|
+
margin-top: 0.2rem;
|
|
1087
|
+
color: var(--rn-text-muted);
|
|
1088
|
+
font-size: 0.625rem;
|
|
1089
|
+
font-weight: 500;
|
|
1090
|
+
letter-spacing: 0.04em;
|
|
1091
|
+
text-transform: uppercase;
|
|
1068
1092
|
}
|
|
1069
1093
|
|
|
1070
1094
|
.rn-sidebar-nav {
|
|
1071
1095
|
flex: 1;
|
|
1072
|
-
padding: 0.75rem;
|
|
1096
|
+
padding: 0.75rem 0.625rem;
|
|
1073
1097
|
display: flex;
|
|
1074
1098
|
flex-direction: column;
|
|
1075
|
-
gap: 0.
|
|
1099
|
+
gap: 0.375rem;
|
|
1076
1100
|
overflow-y: auto;
|
|
1077
1101
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1102
|
+
.rn-nav-section {
|
|
1103
|
+
display: block;
|
|
1104
|
+
border: 1px solid transparent;
|
|
1105
|
+
border-radius: 0.625rem;
|
|
1106
|
+
transition: background var(--rn-transition), border-color var(--rn-transition);
|
|
1107
|
+
}
|
|
1108
|
+
.rn-nav-section[open] {
|
|
1109
|
+
border-color: var(--rn-border-light);
|
|
1110
|
+
background: color-mix(in srgb, var(--rn-bg-secondary) 55%, transparent);
|
|
1111
|
+
}
|
|
1112
|
+
.rn-nav-section-toggle {
|
|
1113
|
+
min-height: 2.5rem;
|
|
1114
|
+
padding: 0.375rem 0.5rem;
|
|
1115
|
+
border-radius: 0.5625rem;
|
|
1116
|
+
display: flex;
|
|
1117
|
+
align-items: center;
|
|
1118
|
+
justify-content: space-between;
|
|
1119
|
+
gap: 0.5rem;
|
|
1120
|
+
color: var(--rn-text-secondary);
|
|
1121
|
+
cursor: pointer;
|
|
1122
|
+
font-size: 0.75rem;
|
|
1123
|
+
font-weight: 650;
|
|
1124
|
+
letter-spacing: 0.01em;
|
|
1125
|
+
list-style: none;
|
|
1126
|
+
transition: color var(--rn-transition), background var(--rn-transition);
|
|
1127
|
+
user-select: none;
|
|
1128
|
+
}
|
|
1129
|
+
.rn-nav-section-toggle::-webkit-details-marker { display: none; }
|
|
1130
|
+
.rn-nav-section-toggle:hover,
|
|
1131
|
+
.rn-nav-section-toggle:focus-visible {
|
|
1132
|
+
background: var(--rn-surface-hover);
|
|
1133
|
+
color: var(--rn-text);
|
|
1134
|
+
outline: none;
|
|
1135
|
+
}
|
|
1136
|
+
.rn-nav-section-toggle:focus-visible { box-shadow: inset 0 0 0 2px var(--rn-primary-ring); }
|
|
1137
|
+
.rn-nav-section-title {
|
|
1138
|
+
min-width: 0;
|
|
1080
1139
|
display: flex;
|
|
1081
1140
|
align-items: center;
|
|
1082
1141
|
gap: 0.625rem;
|
|
1083
|
-
|
|
1142
|
+
}
|
|
1143
|
+
.rn-nav-section-icon {
|
|
1144
|
+
width: 1.625rem;
|
|
1145
|
+
height: 1.625rem;
|
|
1146
|
+
border: 1px solid var(--rn-border);
|
|
1084
1147
|
border-radius: 0.5rem;
|
|
1085
|
-
|
|
1148
|
+
display: inline-flex;
|
|
1149
|
+
align-items: center;
|
|
1150
|
+
justify-content: center;
|
|
1151
|
+
flex-shrink: 0;
|
|
1152
|
+
background: var(--rn-surface);
|
|
1153
|
+
color: var(--rn-text-muted);
|
|
1154
|
+
box-shadow: var(--rn-shadow-sm);
|
|
1155
|
+
transition: color var(--rn-transition), border-color var(--rn-transition), background var(--rn-transition);
|
|
1156
|
+
}
|
|
1157
|
+
.rn-nav-section-chevron {
|
|
1158
|
+
width: 1.125rem;
|
|
1159
|
+
height: 1.125rem;
|
|
1160
|
+
padding: 0.125rem;
|
|
1161
|
+
border-radius: 999px;
|
|
1162
|
+
flex-shrink: 0;
|
|
1163
|
+
color: var(--rn-text-muted);
|
|
1164
|
+
transition: transform var(--rn-transition);
|
|
1165
|
+
}
|
|
1166
|
+
.rn-nav-section[open] > .rn-nav-section-toggle { color: var(--rn-text); }
|
|
1167
|
+
.rn-nav-section[open] > .rn-nav-section-toggle .rn-nav-section-icon {
|
|
1168
|
+
border-color: color-mix(in srgb, var(--rn-primary) 25%, var(--rn-border));
|
|
1169
|
+
background: var(--rn-primary-light);
|
|
1170
|
+
color: var(--rn-primary);
|
|
1171
|
+
box-shadow: none;
|
|
1172
|
+
}
|
|
1173
|
+
.rn-nav-section[open] > .rn-nav-section-toggle .rn-nav-section-chevron { transform: rotate(90deg); }
|
|
1174
|
+
.rn-nav-section-items {
|
|
1175
|
+
margin: 0.125rem 0.5rem 0.5rem 1.3125rem;
|
|
1176
|
+
padding-left: 0.6875rem;
|
|
1177
|
+
border-left: 1px solid var(--rn-border);
|
|
1178
|
+
display: grid;
|
|
1179
|
+
gap: 0.125rem;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
.rn-nav-item {
|
|
1183
|
+
position: relative;
|
|
1184
|
+
min-height: 2.125rem;
|
|
1185
|
+
display: flex;
|
|
1186
|
+
align-items: center;
|
|
1187
|
+
gap: 0.5625rem;
|
|
1188
|
+
padding: 0.375rem 0.625rem;
|
|
1189
|
+
border-radius: 0.4375rem;
|
|
1190
|
+
font-size: 0.8125rem;
|
|
1086
1191
|
font-weight: 500;
|
|
1087
|
-
color: var(--rn-
|
|
1088
|
-
transition:
|
|
1192
|
+
color: var(--rn-text-secondary);
|
|
1193
|
+
transition: color var(--rn-transition), background var(--rn-transition);
|
|
1089
1194
|
text-decoration: none;
|
|
1090
1195
|
}
|
|
1091
1196
|
.rn-nav-item:hover {
|
|
1092
|
-
color: var(--rn-
|
|
1093
|
-
background: var(--rn-
|
|
1197
|
+
color: var(--rn-text);
|
|
1198
|
+
background: var(--rn-surface-hover);
|
|
1094
1199
|
text-decoration: none;
|
|
1095
1200
|
}
|
|
1096
1201
|
.rn-nav-item.active {
|
|
1097
|
-
color:
|
|
1098
|
-
background: var(--rn-
|
|
1202
|
+
color: var(--rn-primary);
|
|
1203
|
+
background: var(--rn-primary-light);
|
|
1204
|
+
}
|
|
1205
|
+
.rn-nav-item.active::before {
|
|
1206
|
+
position: absolute;
|
|
1207
|
+
top: 0.5rem;
|
|
1208
|
+
bottom: 0.5rem;
|
|
1209
|
+
left: -0.75rem;
|
|
1210
|
+
width: 2px;
|
|
1211
|
+
border-radius: 999px;
|
|
1212
|
+
background: var(--rn-primary);
|
|
1213
|
+
content: "";
|
|
1099
1214
|
}
|
|
1215
|
+
.rn-nav-item svg { flex-shrink: 0; }
|
|
1100
1216
|
.rn-nav-item--muted {
|
|
1101
|
-
font-size: 0.8125rem;
|
|
1102
1217
|
opacity: 0.8;
|
|
1103
1218
|
}
|
|
1104
1219
|
.rn-nav-item--muted:hover {
|
|
@@ -1110,22 +1225,42 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1110
1225
|
border-radius: 0.625rem;
|
|
1111
1226
|
font-size: 0.625rem;
|
|
1112
1227
|
font-weight: 600;
|
|
1113
|
-
background: var(--rn-
|
|
1114
|
-
color: var(--rn-
|
|
1228
|
+
background: var(--rn-bg-secondary);
|
|
1229
|
+
color: var(--rn-text-secondary);
|
|
1115
1230
|
line-height: 1.25;
|
|
1116
1231
|
}
|
|
1232
|
+
.rn-nav-item.active .rn-nav-badge {
|
|
1233
|
+
background: color-mix(in srgb, var(--rn-primary) 12%, var(--rn-surface));
|
|
1234
|
+
color: var(--rn-primary);
|
|
1235
|
+
}
|
|
1117
1236
|
.rn-sidebar-section-divider {
|
|
1118
1237
|
height: 1px;
|
|
1119
|
-
background: var(--rn-
|
|
1238
|
+
background: var(--rn-border);
|
|
1120
1239
|
margin: 0.5rem 0;
|
|
1121
1240
|
opacity: 0.5;
|
|
1122
1241
|
}
|
|
1123
1242
|
|
|
1124
1243
|
.rn-sidebar-footer {
|
|
1125
1244
|
padding: 0.75rem 1rem;
|
|
1126
|
-
border-top: 1px solid var(--rn-
|
|
1245
|
+
border-top: 1px solid var(--rn-border);
|
|
1246
|
+
display: flex;
|
|
1247
|
+
align-items: center;
|
|
1248
|
+
justify-content: space-between;
|
|
1249
|
+
gap: 0.75rem;
|
|
1127
1250
|
font-size: 0.6875rem;
|
|
1128
|
-
color:
|
|
1251
|
+
color: var(--rn-text-muted);
|
|
1252
|
+
}
|
|
1253
|
+
.rn-sidebar-status {
|
|
1254
|
+
display: inline-flex;
|
|
1255
|
+
align-items: center;
|
|
1256
|
+
gap: 0.375rem;
|
|
1257
|
+
}
|
|
1258
|
+
.rn-status-dot {
|
|
1259
|
+
width: 0.375rem;
|
|
1260
|
+
height: 0.375rem;
|
|
1261
|
+
border-radius: 999px;
|
|
1262
|
+
background: var(--rn-success);
|
|
1263
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rn-success) 14%, transparent);
|
|
1129
1264
|
}
|
|
1130
1265
|
|
|
1131
1266
|
/* ─── Mobile Sidebar ────────────────────────────────────────── */
|
|
@@ -1134,7 +1269,9 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1134
1269
|
inset-y: 0;
|
|
1135
1270
|
left: 0;
|
|
1136
1271
|
width: 16rem;
|
|
1137
|
-
background: var(--rn-
|
|
1272
|
+
background: var(--rn-surface);
|
|
1273
|
+
display: flex;
|
|
1274
|
+
flex-direction: column;
|
|
1138
1275
|
z-index: 50;
|
|
1139
1276
|
transform: translateX(-100%);
|
|
1140
1277
|
transition: transform 200ms ease-out;
|
|
@@ -1168,6 +1305,61 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1168
1305
|
backdrop-filter: blur(8px);
|
|
1169
1306
|
background: color-mix(in srgb, var(--rn-surface) 85%, transparent);
|
|
1170
1307
|
}
|
|
1308
|
+
.rn-topbar-title,
|
|
1309
|
+
.rn-topbar-actions {
|
|
1310
|
+
display: flex;
|
|
1311
|
+
align-items: center;
|
|
1312
|
+
}
|
|
1313
|
+
.rn-topbar-title { gap: 0.625rem; }
|
|
1314
|
+
.rn-topbar-actions { gap: 0.375rem; }
|
|
1315
|
+
.rn-topbar-heading {
|
|
1316
|
+
display: block;
|
|
1317
|
+
color: var(--rn-text);
|
|
1318
|
+
font-size: 0.875rem;
|
|
1319
|
+
font-weight: 650;
|
|
1320
|
+
line-height: 1.1;
|
|
1321
|
+
}
|
|
1322
|
+
.rn-topbar-subtitle {
|
|
1323
|
+
display: block;
|
|
1324
|
+
margin-top: 0.15rem;
|
|
1325
|
+
color: var(--rn-text-muted);
|
|
1326
|
+
font-size: 0.6875rem;
|
|
1327
|
+
line-height: 1.1;
|
|
1328
|
+
}
|
|
1329
|
+
.rn-sidebar-toggle { display: inline-flex; }
|
|
1330
|
+
|
|
1331
|
+
/* ─── Shell Footer ─────────────────────────────────────────── */
|
|
1332
|
+
.rn-footer {
|
|
1333
|
+
position: static;
|
|
1334
|
+
min-height: 3.25rem;
|
|
1335
|
+
padding: 0.75rem 1.5rem;
|
|
1336
|
+
border-top: 1px solid var(--rn-border-light);
|
|
1337
|
+
display: flex;
|
|
1338
|
+
align-items: center;
|
|
1339
|
+
justify-content: space-between;
|
|
1340
|
+
gap: 1rem;
|
|
1341
|
+
background: var(--rn-surface);
|
|
1342
|
+
color: var(--rn-text-muted);
|
|
1343
|
+
font-size: 0.75rem;
|
|
1344
|
+
}
|
|
1345
|
+
.rn-footer-version { color: var(--rn-text-secondary); }
|
|
1346
|
+
.rn-footer-credit,
|
|
1347
|
+
.rn-footer-github {
|
|
1348
|
+
display: inline-flex;
|
|
1349
|
+
align-items: center;
|
|
1350
|
+
gap: 0.375rem;
|
|
1351
|
+
}
|
|
1352
|
+
.rn-footer a { font-weight: 600; }
|
|
1353
|
+
@media (max-width: 639px) {
|
|
1354
|
+
.rn-topbar { padding: 0 1rem; }
|
|
1355
|
+
.rn-topbar-subtitle { display: none; }
|
|
1356
|
+
.rn-footer {
|
|
1357
|
+
align-items: flex-start;
|
|
1358
|
+
flex-direction: column;
|
|
1359
|
+
gap: 0.25rem;
|
|
1360
|
+
padding: 0.75rem 1rem;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1171
1363
|
|
|
1172
1364
|
.rn-icon-btn {
|
|
1173
1365
|
display: inline-flex;
|
|
@@ -1594,26 +1786,82 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1594
1786
|
white-space: nowrap;
|
|
1595
1787
|
}
|
|
1596
1788
|
|
|
1789
|
+
.rn-exceptions-panel { overflow: hidden; }
|
|
1790
|
+
.rn-exceptions-main {
|
|
1791
|
+
min-width: 0;
|
|
1792
|
+
display: flex;
|
|
1793
|
+
flex-direction: column;
|
|
1794
|
+
gap: 0;
|
|
1795
|
+
}
|
|
1796
|
+
.rn-exceptions-results { min-width: 0; }
|
|
1797
|
+
.rn-filter-disclosure { overflow: hidden; }
|
|
1798
|
+
.rn-filter-summary {
|
|
1799
|
+
padding: 0.75rem 0.875rem;
|
|
1800
|
+
display: flex;
|
|
1801
|
+
align-items: center;
|
|
1802
|
+
justify-content: space-between;
|
|
1803
|
+
gap: 0.75rem;
|
|
1804
|
+
color: var(--rn-text-muted);
|
|
1805
|
+
cursor: pointer;
|
|
1806
|
+
list-style: none;
|
|
1807
|
+
user-select: none;
|
|
1808
|
+
}
|
|
1809
|
+
.rn-filter-summary::-webkit-details-marker { display: none; }
|
|
1810
|
+
.rn-filter-summary:hover { background: var(--rn-surface-hover); }
|
|
1811
|
+
.rn-filter-count {
|
|
1812
|
+
padding: 0.1rem 0.4rem;
|
|
1813
|
+
border-radius: 999px;
|
|
1814
|
+
background: var(--rn-primary-light);
|
|
1815
|
+
color: var(--rn-primary);
|
|
1816
|
+
font-size: 0.625rem;
|
|
1817
|
+
font-weight: 700;
|
|
1818
|
+
}
|
|
1819
|
+
.rn-filter-chevron { flex-shrink: 0; transition: transform var(--rn-transition); }
|
|
1820
|
+
.rn-filter-disclosure[open] .rn-filter-chevron { transform: rotate(180deg); }
|
|
1821
|
+
.rn-filter-disclosure[open] .rn-filter-summary { border-bottom: 1px solid var(--rn-border-light); }
|
|
1822
|
+
.rn-exceptions-toolbar {
|
|
1823
|
+
min-height: 3.25rem;
|
|
1824
|
+
padding: 0.625rem 1rem;
|
|
1825
|
+
display: flex;
|
|
1826
|
+
align-items: center;
|
|
1827
|
+
justify-content: space-between;
|
|
1828
|
+
gap: 1rem;
|
|
1829
|
+
}
|
|
1830
|
+
.rn-exceptions-toolbar-summary,
|
|
1831
|
+
.rn-exceptions-toolbar-actions {
|
|
1832
|
+
display: flex;
|
|
1833
|
+
align-items: center;
|
|
1834
|
+
gap: 0.75rem;
|
|
1835
|
+
}
|
|
1836
|
+
.rn-exceptions-toolbar-actions { margin-left: auto; }
|
|
1597
1837
|
.rn-exceptions-table {
|
|
1598
1838
|
table-layout: fixed;
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
.rn-exceptions-table
|
|
1603
|
-
.rn-exceptions-table
|
|
1604
|
-
.rn-exceptions-table .rn-col-
|
|
1605
|
-
.rn-exceptions-table .rn-col-
|
|
1606
|
-
.rn-exceptions-table .rn-col-
|
|
1607
|
-
.rn-exceptions-table .rn-col-
|
|
1608
|
-
.rn-exceptions-table .rn-col-time { width:
|
|
1609
|
-
.rn-exceptions-table .rn-col-actions { width:
|
|
1839
|
+
width: 100%;
|
|
1840
|
+
min-width: 0;
|
|
1841
|
+
}
|
|
1842
|
+
.rn-exceptions-table th,
|
|
1843
|
+
.rn-exceptions-table td { padding-right: 0.75rem; padding-left: 0.75rem; }
|
|
1844
|
+
.rn-exceptions-table .rn-col-exception { width: 11rem; }
|
|
1845
|
+
.rn-exceptions-table .rn-col-source { width: 9rem; }
|
|
1846
|
+
.rn-exceptions-table .rn-col-message { width: auto; }
|
|
1847
|
+
.rn-exceptions-table .rn-col-count { width: 4rem; text-align: center; }
|
|
1848
|
+
.rn-exceptions-table .rn-col-time { width: 6rem; }
|
|
1849
|
+
.rn-exceptions-table .rn-col-actions { width: 4rem; }
|
|
1850
|
+
.rn-exceptions-table .rn-exception-cell {
|
|
1851
|
+
min-width: 0;
|
|
1852
|
+
display: flex;
|
|
1853
|
+
align-items: flex-start;
|
|
1854
|
+
gap: 0.625rem;
|
|
1855
|
+
}
|
|
1610
1856
|
.rn-exceptions-table .rn-exception-name {
|
|
1611
1857
|
display: block;
|
|
1612
1858
|
overflow: hidden;
|
|
1859
|
+
color: var(--rn-text);
|
|
1613
1860
|
text-overflow: ellipsis;
|
|
1614
1861
|
white-space: nowrap;
|
|
1615
|
-
font-weight:
|
|
1862
|
+
font-weight: 650;
|
|
1616
1863
|
font-size: 0.8125rem;
|
|
1864
|
+
line-height: 1.35;
|
|
1617
1865
|
}
|
|
1618
1866
|
.rn-exceptions-table .rn-source {
|
|
1619
1867
|
display: flex;
|
|
@@ -1631,7 +1879,7 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1631
1879
|
overflow: hidden;
|
|
1632
1880
|
color: var(--rn-text-muted);
|
|
1633
1881
|
font-size: 0.6875rem;
|
|
1634
|
-
line-height: 1;
|
|
1882
|
+
line-height: 1.2;
|
|
1635
1883
|
text-overflow: ellipsis;
|
|
1636
1884
|
white-space: nowrap;
|
|
1637
1885
|
}
|
|
@@ -1642,18 +1890,141 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1642
1890
|
gap: 0.25rem;
|
|
1643
1891
|
}
|
|
1644
1892
|
.rn-exceptions-table .rn-workflow-state .rn-row-meta { flex-basis: 100%; }
|
|
1893
|
+
.rn-exceptions-table .rn-msg {
|
|
1894
|
+
max-width: none;
|
|
1895
|
+
display: -webkit-box;
|
|
1896
|
+
overflow: hidden;
|
|
1897
|
+
white-space: normal;
|
|
1898
|
+
line-height: 1.35;
|
|
1899
|
+
-webkit-box-orient: vertical;
|
|
1900
|
+
-webkit-line-clamp: 2;
|
|
1901
|
+
}
|
|
1902
|
+
.rn-severity-dot {
|
|
1903
|
+
display: block;
|
|
1904
|
+
width: 0.5rem;
|
|
1905
|
+
height: 0.5rem;
|
|
1906
|
+
margin-top: 0.3rem;
|
|
1907
|
+
border-radius: 999px;
|
|
1908
|
+
background: var(--rn-danger);
|
|
1909
|
+
box-shadow: 0 0 0 3px var(--rn-danger-light);
|
|
1910
|
+
}
|
|
1911
|
+
.rn-severity-warning { background: var(--rn-warning); box-shadow: 0 0 0 3px var(--rn-warning-light); }
|
|
1912
|
+
.rn-severity-info { background: var(--rn-info); box-shadow: 0 0 0 3px var(--rn-info-light); }
|
|
1913
|
+
.rn-occurrence-count {
|
|
1914
|
+
display: inline-flex;
|
|
1915
|
+
min-width: 1.75rem;
|
|
1916
|
+
height: 1.5rem;
|
|
1917
|
+
padding: 0 0.4rem;
|
|
1918
|
+
border: 1px solid var(--rn-border);
|
|
1919
|
+
border-radius: 999px;
|
|
1920
|
+
align-items: center;
|
|
1921
|
+
justify-content: center;
|
|
1922
|
+
background: var(--rn-bg-secondary);
|
|
1923
|
+
color: var(--rn-text-secondary);
|
|
1924
|
+
font-size: 0.6875rem;
|
|
1925
|
+
font-weight: 700;
|
|
1926
|
+
}
|
|
1927
|
+
.rn-row-actions {
|
|
1928
|
+
display: flex;
|
|
1929
|
+
align-items: center;
|
|
1930
|
+
justify-content: flex-end;
|
|
1931
|
+
gap: 0.125rem;
|
|
1932
|
+
opacity: 0.35;
|
|
1933
|
+
transition: opacity var(--rn-transition);
|
|
1934
|
+
}
|
|
1935
|
+
.rn-exceptions-table tr:hover .rn-row-actions,
|
|
1936
|
+
.rn-exceptions-table tr:focus-within .rn-row-actions,
|
|
1937
|
+
.rn-exceptions-table tr.selected .rn-row-actions { opacity: 1; }
|
|
1645
1938
|
.rn-exceptions-table tbody tr {
|
|
1646
1939
|
position: relative;
|
|
1647
1940
|
border-left: 3px solid transparent;
|
|
1941
|
+
outline: none;
|
|
1648
1942
|
}
|
|
1649
1943
|
.rn-exceptions-table tbody tr:hover {
|
|
1650
1944
|
border-left-color: var(--rn-primary);
|
|
1651
1945
|
}
|
|
1946
|
+
.rn-exceptions-table tbody tr:focus-visible {
|
|
1947
|
+
border-left-color: var(--rn-primary);
|
|
1948
|
+
box-shadow: inset 0 0 0 2px var(--rn-primary-ring);
|
|
1949
|
+
}
|
|
1652
1950
|
.rn-exceptions-table tbody tr.selected {
|
|
1653
1951
|
border-left-color: var(--rn-primary);
|
|
1654
1952
|
background: var(--rn-primary-light);
|
|
1655
1953
|
}
|
|
1656
1954
|
|
|
1955
|
+
@media (max-width: 1100px) {
|
|
1956
|
+
.rn-exceptions-main { width: 100%; }
|
|
1957
|
+
.rn-exceptions-toolbar { flex-wrap: wrap; }
|
|
1958
|
+
.rn-exceptions-toolbar-actions { flex-wrap: wrap; }
|
|
1959
|
+
.rn-exceptions-table { display: block; min-width: 0; }
|
|
1960
|
+
.rn-exceptions-table thead { display: none; }
|
|
1961
|
+
.rn-exceptions-table tbody {
|
|
1962
|
+
padding: 0.75rem;
|
|
1963
|
+
display: grid;
|
|
1964
|
+
gap: 0.625rem;
|
|
1965
|
+
}
|
|
1966
|
+
.rn-exceptions-table tbody tr {
|
|
1967
|
+
padding: 0.875rem 0.875rem 0.875rem 0.75rem;
|
|
1968
|
+
border: 1px solid var(--rn-border-light);
|
|
1969
|
+
border-left: 3px solid transparent;
|
|
1970
|
+
border-radius: 0.625rem;
|
|
1971
|
+
display: grid;
|
|
1972
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
1973
|
+
grid-template-areas:
|
|
1974
|
+
"exception exception actions"
|
|
1975
|
+
"message message message"
|
|
1976
|
+
"source count time";
|
|
1977
|
+
column-gap: 0.75rem;
|
|
1978
|
+
row-gap: 0.5rem;
|
|
1979
|
+
background: var(--rn-surface);
|
|
1980
|
+
box-shadow: var(--rn-shadow-sm);
|
|
1981
|
+
}
|
|
1982
|
+
.rn-exceptions-table tbody tr:hover,
|
|
1983
|
+
.rn-exceptions-table tbody tr.selected {
|
|
1984
|
+
border-left-color: var(--rn-primary);
|
|
1985
|
+
background: var(--rn-surface-hover);
|
|
1986
|
+
}
|
|
1987
|
+
.rn-exceptions-table tbody tr.selected { box-shadow: 0 0 0 2px var(--rn-primary-ring); }
|
|
1988
|
+
.rn-exceptions-table td {
|
|
1989
|
+
width: auto !important;
|
|
1990
|
+
padding: 0;
|
|
1991
|
+
border: 0;
|
|
1992
|
+
min-width: 0;
|
|
1993
|
+
}
|
|
1994
|
+
.rn-exceptions-table .rn-col-exception { grid-area: exception; }
|
|
1995
|
+
.rn-exceptions-table .rn-col-source { grid-area: source; }
|
|
1996
|
+
.rn-exceptions-table .rn-col-message { grid-area: message; }
|
|
1997
|
+
.rn-exceptions-table .rn-col-count { grid-area: count; text-align: left; }
|
|
1998
|
+
.rn-exceptions-table .rn-col-time { grid-area: time; text-align: right; }
|
|
1999
|
+
.rn-exceptions-table .rn-col-actions { grid-area: actions; }
|
|
2000
|
+
.rn-exceptions-table .rn-exception-name { font-size: 0.875rem; }
|
|
2001
|
+
.rn-exceptions-table .rn-source::before {
|
|
2002
|
+
margin-right: 0.375rem;
|
|
2003
|
+
color: var(--rn-text-muted);
|
|
2004
|
+
content: "Source";
|
|
2005
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
2006
|
+
font-size: 0.6875rem;
|
|
2007
|
+
}
|
|
2008
|
+
.rn-exceptions-table .rn-row-actions { opacity: 1; }
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
@media (max-width: 639px) {
|
|
2012
|
+
.rn-exceptions-toolbar { align-items: flex-start; flex-direction: column; }
|
|
2013
|
+
.rn-exceptions-toolbar-summary { width: 100%; justify-content: space-between; }
|
|
2014
|
+
.rn-exceptions-toolbar-actions { width: 100%; margin-left: 0; justify-content: flex-end; }
|
|
2015
|
+
.rn-shortcuts-hint { display: none; }
|
|
2016
|
+
.rn-exceptions-table tbody { padding: 0.5rem; }
|
|
2017
|
+
.rn-exceptions-table tbody tr {
|
|
2018
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
2019
|
+
grid-template-areas:
|
|
2020
|
+
"exception actions actions"
|
|
2021
|
+
"message message message"
|
|
2022
|
+
"source source source"
|
|
2023
|
+
"count time time";
|
|
2024
|
+
}
|
|
2025
|
+
.rn-exceptions-table .rn-col-time { text-align: left; }
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1657
2028
|
/* ─── Row count indicator ───────────────────────────────────── */
|
|
1658
2029
|
.rn-count-indicator {
|
|
1659
2030
|
width: 1.5rem;
|
|
@@ -1956,9 +2327,14 @@ code, pre { font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", mon
|
|
|
1956
2327
|
.rn-font-semibold { font-weight: 600; }
|
|
1957
2328
|
|
|
1958
2329
|
/* ─── Layout (override host app resets) ─────────────────── */
|
|
1959
|
-
html { height: 100%; }
|
|
1960
|
-
body { height:
|
|
1961
|
-
body > div.min-h-full { height: 100vh;
|
|
2330
|
+
html { min-height: 100%; }
|
|
2331
|
+
body { min-height: 100%; height: auto; overflow: visible; margin: 0; }
|
|
2332
|
+
body > div.min-h-full { min-height: 100vh; height: auto; }
|
|
1962
2333
|
body > div.min-h-full > .rn-sidebar { height: 100vh; min-height: 100vh; }
|
|
1963
|
-
body > div.min-h-full > div:last-child {
|
|
1964
|
-
|
|
2334
|
+
body > div.min-h-full > div:last-child {
|
|
2335
|
+
min-height: 100vh;
|
|
2336
|
+
display: flex;
|
|
2337
|
+
flex-direction: column;
|
|
2338
|
+
overflow: visible;
|
|
2339
|
+
}
|
|
2340
|
+
body > div.min-h-full > div:last-child > main { flex: 1; overflow: visible; }
|
|
@@ -9,14 +9,6 @@ module RailsNexus
|
|
|
9
9
|
# GET /rails_nexus/backup
|
|
10
10
|
def index
|
|
11
11
|
@configs = RailsNexus::BackupConfig.order(:name)
|
|
12
|
-
@records = RailsNexus::Backup.order(started_at: :desc).limit(20)
|
|
13
|
-
@stats = {
|
|
14
|
-
total_configs: RailsNexus::BackupConfig.count,
|
|
15
|
-
enabled_configs: RailsNexus::BackupConfig.enabled.count,
|
|
16
|
-
total_backups: RailsNexus::Backup.count,
|
|
17
|
-
successful: RailsNexus::Backup.successful.count,
|
|
18
|
-
failed: RailsNexus::Backup.failed.count
|
|
19
|
-
}
|
|
20
12
|
end
|
|
21
13
|
|
|
22
14
|
# GET /rails_nexus/backup/new
|
|
@@ -47,7 +39,13 @@ module RailsNexus
|
|
|
47
39
|
|
|
48
40
|
# PATCH /rails_nexus/backup/:id
|
|
49
41
|
def update
|
|
50
|
-
|
|
42
|
+
# Strip blank password fields so they don't overwrite stored values
|
|
43
|
+
filtered = config_params
|
|
44
|
+
%w[password encryption_password gpg_password].each do |attr|
|
|
45
|
+
filtered = filtered.except(attr) if filtered[attr].blank?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if @config.update(filtered)
|
|
51
49
|
redirect_to backup_path, notice: "Backup config '#{@config.name}' updated."
|
|
52
50
|
else
|
|
53
51
|
render :edit, status: :unprocessable_entity
|
|
@@ -63,19 +61,24 @@ module RailsNexus
|
|
|
63
61
|
|
|
64
62
|
# POST /rails_nexus/backup/:id/trigger
|
|
65
63
|
def trigger
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
begin
|
|
65
|
+
RailsNexus::BackupJob.perform_later(@config.id)
|
|
66
|
+
redirect_to backup_path, notice: "Backup '#{@config.name}' started in background."
|
|
67
|
+
rescue => e
|
|
68
|
+
# Fallback to synchronous if Active Job is not configured
|
|
69
|
+
result = RailsNexus::BackupService.run(@config)
|
|
70
|
+
if result[:success]
|
|
71
|
+
redirect_to backup_path, notice: "Backup '#{@config.name}' completed successfully."
|
|
72
|
+
else
|
|
73
|
+
redirect_to backup_path, alert: "Backup failed: #{result[:error]}"
|
|
74
|
+
end
|
|
72
75
|
end
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
# GET /rails_nexus/backup/:id/history
|
|
76
79
|
def history
|
|
77
80
|
@config = RailsNexus::BackupConfig.find(params[:id])
|
|
78
|
-
@records = RailsNexus::Backup.where(
|
|
81
|
+
@records = RailsNexus::Backup.where(config_name: @config.name)
|
|
79
82
|
.order(started_at: :desc)
|
|
80
83
|
.limit(50)
|
|
81
84
|
end
|
|
@@ -83,6 +86,7 @@ module RailsNexus
|
|
|
83
86
|
# GET /rails_nexus/backup/history
|
|
84
87
|
def all_history
|
|
85
88
|
@records = RailsNexus::Backup.order(started_at: :desc).limit(100)
|
|
89
|
+
render :history
|
|
86
90
|
end
|
|
87
91
|
|
|
88
92
|
private
|
|
@@ -95,11 +99,15 @@ module RailsNexus
|
|
|
95
99
|
params.require(:backup_config).permit(
|
|
96
100
|
:name, :description, :database_name, :adapter, :host, :port,
|
|
97
101
|
:username, :password, :storage_path, :keep_count,
|
|
98
|
-
:compress, :
|
|
99
|
-
:rsync_enabled, :rsync_host, :rsync_port, :rsync_user, :rsync_path, :rsync_mirror,
|
|
102
|
+
:compress, :encrypted, :encryption_password,
|
|
103
|
+
:rsync_enabled, :rsync_host, :rsync_port, :rsync_user, :rsync_path, :rsync_mirror, :rsync_archive, :rsync_directories, :rsync_excludes,
|
|
100
104
|
:notify_command, :notify_on_success, :notify_on_failure,
|
|
101
105
|
:schedule_cron, :enabled,
|
|
102
|
-
|
|
106
|
+
:s3_enabled, :s3_access_key, :s3_secret_key, :s3_bucket, :s3_region, :s3_prefix,
|
|
107
|
+
:gpg_enabled, :gpg_password,
|
|
108
|
+
:email_notify, :email_to,
|
|
109
|
+
:archive_enabled, :bzip2_compress, :split_chunks, :encrypt_base64, :mysql_additional_options,
|
|
110
|
+
skip_tables: [], archive_paths: [], archive_excludes: []
|
|
103
111
|
)
|
|
104
112
|
end
|
|
105
113
|
|