lesli_audit 1.1.0 → 1.1.2
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/Rakefile +6 -1
- data/app/assets/stylesheets/lesli_audit/application.css +366 -35
- data/app/controllers/lesli_audit/accounts_controller.rb +7 -3
- data/app/controllers/lesli_audit/logs_controller.rb +3 -8
- data/app/controllers/lesli_audit/visitors_controller.rb +1 -1
- data/app/interfaces/lesli_audit/logger_interface.rb +56 -59
- data/app/jobs/lesli_audit/retention_cleanup_job.rb +9 -0
- data/app/models/concerns/lesli_audit/append_only.rb +12 -0
- data/app/models/lesli_audit/account.rb +26 -10
- data/app/models/lesli_audit/account_device.rb +3 -1
- data/app/models/lesli_audit/account_log.rb +12 -7
- data/app/models/lesli_audit/account_request.rb +3 -1
- data/app/models/lesli_audit/user_journal.rb +16 -3
- data/app/models/lesli_audit/user_log.rb +12 -3
- data/app/models/lesli_audit/user_request.rb +7 -3
- data/app/services/lesli_audit/activity_audit.rb +130 -0
- data/app/services/lesli_audit/request_service.rb +2 -35
- data/app/services/lesli_audit/retention_cleanup.rb +146 -0
- data/app/services/lesli_audit/user_agent_parser.rb +51 -0
- data/app/services/lesli_audit/user_service.rb +18 -16
- data/app/services/lesli_audit/visitor_service.rb +22 -49
- data/app/views/lesli_audit/logs/show.html.erb +14 -8
- data/app/views/lesli_audit/partials/_navigation.html.erb +1 -1
- data/app/views/lesli_audit/users/index.html.erb +67 -64
- data/app/views/lesli_audit/visitors/index.html.erb +126 -80
- data/config/routes.rb +3 -2
- data/db/migrate/v1.0/0501000110_create_lesli_audit_accounts.rb +14 -1
- data/db/migrate/v1.0/0501110110_create_lesli_audit_account_logs.rb +10 -6
- data/db/migrate/v1.0/0501110210_create_lesli_audit_account_devices.rb +10 -7
- data/db/migrate/v1.0/0501110310_create_lesli_audit_account_requests.rb +10 -7
- data/db/migrate/v1.0/0501120110_create_lesli_audit_user_logs.rb +11 -7
- data/db/migrate/v1.0/0501120210_create_lesli_audit_user_journals.rb +19 -8
- data/db/migrate/v1.0/0501120310_create_lesli_audit_user_requests.rb +13 -10
- data/db/seed/devices.rb +1 -1
- data/db/seed/requests.rb +3 -3
- data/lib/lesli_audit/engine.rb +1 -6
- data/lib/lesli_audit/version.rb +2 -2
- data/lib/tasks/lesli_audit_tasks.rake +12 -4
- data/readme.md +127 -50
- metadata +14 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f3d2dc51c06a85eb50d460ee02c27930bc2de5765e4745c73ddbd7cdc83d789
|
|
4
|
+
data.tar.gz: e3e6036dc2db7b777081398d4ee9bcde2a6707095bd7c30215956f98da6c6f82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec1b7c2ef3957736a5832dee9c2a20f627e809c59e698401d44fdc1c356f6e5ba46d85b33e456446b036b7d83d70f97eb2f0a7bfea5fb8a48340893643fa3e40
|
|
7
|
+
data.tar.gz: d772a35fca2f2595305125b65e51b1f825df8c51ae5e020b553d5aabaf0cdfb62d7aa5c78586bf50945ebf54fa2265ddc4c3e7d7b9cfaa9da60f17e3dabef2cd
|
data/Rakefile
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
require "bundler/setup"
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
|
4
|
+
|
|
5
|
+
load "rails/tasks/engine.rake"
|
|
4
6
|
|
|
5
7
|
require "bundler/gem_tasks"
|
|
8
|
+
|
|
9
|
+
desc "Run engine tests through the dummy Rails application"
|
|
10
|
+
task test: "app:test"
|
|
@@ -1,35 +1,366 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties;
|
|
3
|
+
@layer theme, utilities;
|
|
4
|
+
@layer theme {
|
|
5
|
+
:root, :host {
|
|
6
|
+
--color-amber-600: oklch(66.6% 0.179 58.318);
|
|
7
|
+
--color-sky-600: oklch(58.8% 0.158 241.966);
|
|
8
|
+
--color-sky-700: oklch(50% 0.134 242.749);
|
|
9
|
+
--color-sky-900: oklch(39.1% 0.09 240.876);
|
|
10
|
+
--color-rose-600: oklch(58.6% 0.253 17.585);
|
|
11
|
+
--color-slate-100: oklch(96.8% 0.007 247.896);
|
|
12
|
+
--color-slate-200: oklch(92.9% 0.013 255.508);
|
|
13
|
+
--color-slate-500: oklch(55.4% 0.046 257.417);
|
|
14
|
+
--color-slate-700: oklch(37.2% 0.044 257.287);
|
|
15
|
+
--color-slate-900: oklch(20.8% 0.042 265.755);
|
|
16
|
+
--color-white: #fff;
|
|
17
|
+
--spacing: 0.25rem;
|
|
18
|
+
--text-sm: 0.875rem;
|
|
19
|
+
--text-sm--line-height: calc(1.25 / 0.875);
|
|
20
|
+
--text-lg: 1.125rem;
|
|
21
|
+
--text-lg--line-height: calc(1.75 / 1.125);
|
|
22
|
+
--text-xl: 1.25rem;
|
|
23
|
+
--text-xl--line-height: calc(1.75 / 1.25);
|
|
24
|
+
--text-3xl: 1.875rem;
|
|
25
|
+
--text-3xl--line-height: calc(2.25 / 1.875);
|
|
26
|
+
--font-weight-medium: 500;
|
|
27
|
+
--font-weight-semibold: 600;
|
|
28
|
+
--tracking-tight: -0.025em;
|
|
29
|
+
--radius-2xl: 1rem;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
@layer utilities {
|
|
33
|
+
.absolute {
|
|
34
|
+
position: absolute;
|
|
35
|
+
}
|
|
36
|
+
.relative {
|
|
37
|
+
position: relative;
|
|
38
|
+
}
|
|
39
|
+
.inset-y-0 {
|
|
40
|
+
inset-block: 0px;
|
|
41
|
+
}
|
|
42
|
+
.left-0 {
|
|
43
|
+
left: 0px;
|
|
44
|
+
}
|
|
45
|
+
.isolate {
|
|
46
|
+
isolation: isolate;
|
|
47
|
+
}
|
|
48
|
+
.-z-10 {
|
|
49
|
+
z-index: calc(10 * -1);
|
|
50
|
+
}
|
|
51
|
+
.mt-1 {
|
|
52
|
+
margin-top: var(--spacing);
|
|
53
|
+
}
|
|
54
|
+
.mt-3 {
|
|
55
|
+
margin-top: calc(var(--spacing) * 3);
|
|
56
|
+
}
|
|
57
|
+
.mt-6 {
|
|
58
|
+
margin-top: calc(var(--spacing) * 6);
|
|
59
|
+
}
|
|
60
|
+
.flex {
|
|
61
|
+
display: flex;
|
|
62
|
+
}
|
|
63
|
+
.grid {
|
|
64
|
+
display: grid;
|
|
65
|
+
}
|
|
66
|
+
.inline-flex {
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
}
|
|
69
|
+
.h-7 {
|
|
70
|
+
height: calc(var(--spacing) * 7);
|
|
71
|
+
}
|
|
72
|
+
.h-8 {
|
|
73
|
+
height: calc(var(--spacing) * 8);
|
|
74
|
+
}
|
|
75
|
+
.w-7 {
|
|
76
|
+
width: calc(var(--spacing) * 7);
|
|
77
|
+
}
|
|
78
|
+
.w-8 {
|
|
79
|
+
width: calc(var(--spacing) * 8);
|
|
80
|
+
}
|
|
81
|
+
.w-full {
|
|
82
|
+
width: 100%;
|
|
83
|
+
}
|
|
84
|
+
.\!min-w-0 {
|
|
85
|
+
min-width: 0px !important;
|
|
86
|
+
}
|
|
87
|
+
.min-w-0 {
|
|
88
|
+
min-width: 0px;
|
|
89
|
+
}
|
|
90
|
+
.shrink-0 {
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
}
|
|
93
|
+
.grid-cols-1 {
|
|
94
|
+
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
95
|
+
}
|
|
96
|
+
.items-center {
|
|
97
|
+
align-items: center;
|
|
98
|
+
}
|
|
99
|
+
.justify-center {
|
|
100
|
+
justify-content: center;
|
|
101
|
+
}
|
|
102
|
+
.gap-2 {
|
|
103
|
+
gap: calc(var(--spacing) * 2);
|
|
104
|
+
}
|
|
105
|
+
.gap-4 {
|
|
106
|
+
gap: calc(var(--spacing) * 4);
|
|
107
|
+
}
|
|
108
|
+
.gap-6 {
|
|
109
|
+
gap: calc(var(--spacing) * 6);
|
|
110
|
+
}
|
|
111
|
+
.truncate {
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
text-overflow: ellipsis;
|
|
114
|
+
white-space: nowrap;
|
|
115
|
+
}
|
|
116
|
+
.overflow-hidden {
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
}
|
|
119
|
+
.rounded-2xl {
|
|
120
|
+
border-radius: var(--radius-2xl);
|
|
121
|
+
}
|
|
122
|
+
.border {
|
|
123
|
+
border-style: var(--tw-border-style);
|
|
124
|
+
border-width: 1px;
|
|
125
|
+
}
|
|
126
|
+
.border-slate-200 {
|
|
127
|
+
border-color: var(--color-slate-200);
|
|
128
|
+
}
|
|
129
|
+
.bg-slate-100 {
|
|
130
|
+
background-color: var(--color-slate-100);
|
|
131
|
+
}
|
|
132
|
+
.bg-white {
|
|
133
|
+
background-color: var(--color-white);
|
|
134
|
+
}
|
|
135
|
+
.\!p-0 {
|
|
136
|
+
padding: 0px !important;
|
|
137
|
+
}
|
|
138
|
+
.p-5 {
|
|
139
|
+
padding: calc(var(--spacing) * 5);
|
|
140
|
+
}
|
|
141
|
+
.px-3 {
|
|
142
|
+
padding-inline: calc(var(--spacing) * 3);
|
|
143
|
+
}
|
|
144
|
+
.py-2\.5 {
|
|
145
|
+
padding-block: calc(var(--spacing) * 2.5);
|
|
146
|
+
}
|
|
147
|
+
.pb-8 {
|
|
148
|
+
padding-bottom: calc(var(--spacing) * 8);
|
|
149
|
+
}
|
|
150
|
+
.text-3xl {
|
|
151
|
+
font-size: var(--text-3xl);
|
|
152
|
+
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
|
153
|
+
}
|
|
154
|
+
.text-lg {
|
|
155
|
+
font-size: var(--text-lg);
|
|
156
|
+
line-height: var(--tw-leading, var(--text-lg--line-height));
|
|
157
|
+
}
|
|
158
|
+
.text-sm {
|
|
159
|
+
font-size: var(--text-sm);
|
|
160
|
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
|
161
|
+
}
|
|
162
|
+
.text-xl {
|
|
163
|
+
font-size: var(--text-xl);
|
|
164
|
+
line-height: var(--tw-leading, var(--text-xl--line-height));
|
|
165
|
+
}
|
|
166
|
+
.leading-none {
|
|
167
|
+
--tw-leading: 1;
|
|
168
|
+
line-height: 1;
|
|
169
|
+
}
|
|
170
|
+
.font-medium {
|
|
171
|
+
--tw-font-weight: var(--font-weight-medium);
|
|
172
|
+
font-weight: var(--font-weight-medium);
|
|
173
|
+
}
|
|
174
|
+
.font-semibold {
|
|
175
|
+
--tw-font-weight: var(--font-weight-semibold);
|
|
176
|
+
font-weight: var(--font-weight-semibold);
|
|
177
|
+
}
|
|
178
|
+
.tracking-tight {
|
|
179
|
+
--tw-tracking: var(--tracking-tight);
|
|
180
|
+
letter-spacing: var(--tracking-tight);
|
|
181
|
+
}
|
|
182
|
+
.break-words {
|
|
183
|
+
overflow-wrap: break-word;
|
|
184
|
+
}
|
|
185
|
+
.text-amber-600 {
|
|
186
|
+
color: var(--color-amber-600);
|
|
187
|
+
}
|
|
188
|
+
.text-rose-600 {
|
|
189
|
+
color: var(--color-rose-600);
|
|
190
|
+
}
|
|
191
|
+
.text-sky-600 {
|
|
192
|
+
color: var(--color-sky-600);
|
|
193
|
+
}
|
|
194
|
+
.text-sky-700 {
|
|
195
|
+
color: var(--color-sky-700);
|
|
196
|
+
}
|
|
197
|
+
.text-slate-500 {
|
|
198
|
+
color: var(--color-slate-500);
|
|
199
|
+
}
|
|
200
|
+
.text-slate-700 {
|
|
201
|
+
color: var(--color-slate-700);
|
|
202
|
+
}
|
|
203
|
+
.text-slate-900 {
|
|
204
|
+
color: var(--color-slate-900);
|
|
205
|
+
}
|
|
206
|
+
.tabular-nums {
|
|
207
|
+
--tw-numeric-spacing: tabular-nums;
|
|
208
|
+
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
|
|
209
|
+
}
|
|
210
|
+
.shadow-sm {
|
|
211
|
+
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
212
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
213
|
+
}
|
|
214
|
+
@media (hover: hover) {
|
|
215
|
+
.hover\:text-sky-900:hover {
|
|
216
|
+
color: var(--color-sky-900);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
@media (width >= 40rem) {
|
|
220
|
+
.sm\:grid-cols-2 {
|
|
221
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
@media (width >= 64rem) {
|
|
225
|
+
.lg\:w-2\/5 {
|
|
226
|
+
width: calc(2 / 5 * 100%);
|
|
227
|
+
}
|
|
228
|
+
.lg\:grid-cols-3 {
|
|
229
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
230
|
+
}
|
|
231
|
+
.lg\:grid-cols-5 {
|
|
232
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
@property --tw-border-style {
|
|
237
|
+
syntax: "*";
|
|
238
|
+
inherits: false;
|
|
239
|
+
initial-value: solid;
|
|
240
|
+
}
|
|
241
|
+
@property --tw-leading {
|
|
242
|
+
syntax: "*";
|
|
243
|
+
inherits: false;
|
|
244
|
+
}
|
|
245
|
+
@property --tw-font-weight {
|
|
246
|
+
syntax: "*";
|
|
247
|
+
inherits: false;
|
|
248
|
+
}
|
|
249
|
+
@property --tw-tracking {
|
|
250
|
+
syntax: "*";
|
|
251
|
+
inherits: false;
|
|
252
|
+
}
|
|
253
|
+
@property --tw-ordinal {
|
|
254
|
+
syntax: "*";
|
|
255
|
+
inherits: false;
|
|
256
|
+
}
|
|
257
|
+
@property --tw-slashed-zero {
|
|
258
|
+
syntax: "*";
|
|
259
|
+
inherits: false;
|
|
260
|
+
}
|
|
261
|
+
@property --tw-numeric-figure {
|
|
262
|
+
syntax: "*";
|
|
263
|
+
inherits: false;
|
|
264
|
+
}
|
|
265
|
+
@property --tw-numeric-spacing {
|
|
266
|
+
syntax: "*";
|
|
267
|
+
inherits: false;
|
|
268
|
+
}
|
|
269
|
+
@property --tw-numeric-fraction {
|
|
270
|
+
syntax: "*";
|
|
271
|
+
inherits: false;
|
|
272
|
+
}
|
|
273
|
+
@property --tw-shadow {
|
|
274
|
+
syntax: "*";
|
|
275
|
+
inherits: false;
|
|
276
|
+
initial-value: 0 0 #0000;
|
|
277
|
+
}
|
|
278
|
+
@property --tw-shadow-color {
|
|
279
|
+
syntax: "*";
|
|
280
|
+
inherits: false;
|
|
281
|
+
}
|
|
282
|
+
@property --tw-shadow-alpha {
|
|
283
|
+
syntax: "<percentage>";
|
|
284
|
+
inherits: false;
|
|
285
|
+
initial-value: 100%;
|
|
286
|
+
}
|
|
287
|
+
@property --tw-inset-shadow {
|
|
288
|
+
syntax: "*";
|
|
289
|
+
inherits: false;
|
|
290
|
+
initial-value: 0 0 #0000;
|
|
291
|
+
}
|
|
292
|
+
@property --tw-inset-shadow-color {
|
|
293
|
+
syntax: "*";
|
|
294
|
+
inherits: false;
|
|
295
|
+
}
|
|
296
|
+
@property --tw-inset-shadow-alpha {
|
|
297
|
+
syntax: "<percentage>";
|
|
298
|
+
inherits: false;
|
|
299
|
+
initial-value: 100%;
|
|
300
|
+
}
|
|
301
|
+
@property --tw-ring-color {
|
|
302
|
+
syntax: "*";
|
|
303
|
+
inherits: false;
|
|
304
|
+
}
|
|
305
|
+
@property --tw-ring-shadow {
|
|
306
|
+
syntax: "*";
|
|
307
|
+
inherits: false;
|
|
308
|
+
initial-value: 0 0 #0000;
|
|
309
|
+
}
|
|
310
|
+
@property --tw-inset-ring-color {
|
|
311
|
+
syntax: "*";
|
|
312
|
+
inherits: false;
|
|
313
|
+
}
|
|
314
|
+
@property --tw-inset-ring-shadow {
|
|
315
|
+
syntax: "*";
|
|
316
|
+
inherits: false;
|
|
317
|
+
initial-value: 0 0 #0000;
|
|
318
|
+
}
|
|
319
|
+
@property --tw-ring-inset {
|
|
320
|
+
syntax: "*";
|
|
321
|
+
inherits: false;
|
|
322
|
+
}
|
|
323
|
+
@property --tw-ring-offset-width {
|
|
324
|
+
syntax: "<length>";
|
|
325
|
+
inherits: false;
|
|
326
|
+
initial-value: 0px;
|
|
327
|
+
}
|
|
328
|
+
@property --tw-ring-offset-color {
|
|
329
|
+
syntax: "*";
|
|
330
|
+
inherits: false;
|
|
331
|
+
initial-value: #fff;
|
|
332
|
+
}
|
|
333
|
+
@property --tw-ring-offset-shadow {
|
|
334
|
+
syntax: "*";
|
|
335
|
+
inherits: false;
|
|
336
|
+
initial-value: 0 0 #0000;
|
|
337
|
+
}
|
|
338
|
+
@layer properties {
|
|
339
|
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
340
|
+
*, ::before, ::after, ::backdrop {
|
|
341
|
+
--tw-border-style: solid;
|
|
342
|
+
--tw-leading: initial;
|
|
343
|
+
--tw-font-weight: initial;
|
|
344
|
+
--tw-tracking: initial;
|
|
345
|
+
--tw-ordinal: initial;
|
|
346
|
+
--tw-slashed-zero: initial;
|
|
347
|
+
--tw-numeric-figure: initial;
|
|
348
|
+
--tw-numeric-spacing: initial;
|
|
349
|
+
--tw-numeric-fraction: initial;
|
|
350
|
+
--tw-shadow: 0 0 #0000;
|
|
351
|
+
--tw-shadow-color: initial;
|
|
352
|
+
--tw-shadow-alpha: 100%;
|
|
353
|
+
--tw-inset-shadow: 0 0 #0000;
|
|
354
|
+
--tw-inset-shadow-color: initial;
|
|
355
|
+
--tw-inset-shadow-alpha: 100%;
|
|
356
|
+
--tw-ring-color: initial;
|
|
357
|
+
--tw-ring-shadow: 0 0 #0000;
|
|
358
|
+
--tw-inset-ring-color: initial;
|
|
359
|
+
--tw-inset-ring-shadow: 0 0 #0000;
|
|
360
|
+
--tw-ring-inset: initial;
|
|
361
|
+
--tw-ring-offset-width: 0px;
|
|
362
|
+
--tw-ring-offset-color: #fff;
|
|
363
|
+
--tw-ring-offset-shadow: 0 0 #0000;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
@@ -25,7 +25,7 @@ module LesliAudit
|
|
|
25
25
|
@account = Account.new(account_params)
|
|
26
26
|
|
|
27
27
|
if @account.save
|
|
28
|
-
redirect_to @account, notice: "Account was successfully created."
|
|
28
|
+
redirect_to({ action: :show, id: @account.id }, notice: "Account was successfully created.")
|
|
29
29
|
else
|
|
30
30
|
render :new, status: :unprocessable_entity
|
|
31
31
|
end
|
|
@@ -34,7 +34,11 @@ module LesliAudit
|
|
|
34
34
|
# PATCH/PUT /accounts/1
|
|
35
35
|
def update
|
|
36
36
|
if @account.update(account_params)
|
|
37
|
-
redirect_to
|
|
37
|
+
redirect_to(
|
|
38
|
+
{ action: :show, id: @account.id },
|
|
39
|
+
notice: "Account was successfully updated.",
|
|
40
|
+
status: :see_other
|
|
41
|
+
)
|
|
38
42
|
else
|
|
39
43
|
render :edit, status: :unprocessable_entity
|
|
40
44
|
end
|
|
@@ -43,7 +47,7 @@ module LesliAudit
|
|
|
43
47
|
# DELETE /accounts/1
|
|
44
48
|
def destroy
|
|
45
49
|
@account.destroy
|
|
46
|
-
redirect_to
|
|
50
|
+
redirect_to({ action: :index }, notice: "Account was successfully destroyed.", status: :see_other)
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
private
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
module LesliAudit
|
|
2
2
|
class LogsController < ApplicationController
|
|
3
|
-
before_action :set_log, only:
|
|
3
|
+
before_action :set_log, only: :show
|
|
4
4
|
|
|
5
5
|
# GET /logs
|
|
6
6
|
def index
|
|
7
7
|
@logs = respond_with_pagination(LesliAudit::LogService.new(current_user, query).index)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# GET /logs
|
|
10
|
+
# GET /logs/:id
|
|
11
11
|
def show
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def set_log
|
|
17
|
-
@log =
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Only allow a list of trusted parameters through.
|
|
21
|
-
def log_params
|
|
22
|
-
params.fetch(:log, {})
|
|
17
|
+
@log = current_user.logs.find(params[:id])
|
|
23
18
|
end
|
|
24
19
|
end
|
|
25
20
|
end
|
|
@@ -4,7 +4,7 @@ module LesliAudit
|
|
|
4
4
|
# GET /analytics
|
|
5
5
|
def index
|
|
6
6
|
@visits = VisitorService.new(current_user, query).visits
|
|
7
|
-
@
|
|
7
|
+
@traffic = VisitorService.new(current_user, query).traffic
|
|
8
8
|
@requests = VisitorService.new(current_user, query).requests
|
|
9
9
|
@browsers = VisitorService.new(current_user, query).browsers
|
|
10
10
|
@devices = VisitorService.new(current_user, query).devices
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lesli
|
|
4
4
|
|
|
5
|
-
Copyright (c)
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
6
|
|
|
7
7
|
This program is free software: you can redistribute it and/or modify
|
|
8
8
|
it under the terms of the GNU General Public License as published by
|
|
@@ -19,7 +19,7 @@ along with this program. If not, see http://www.gnu.org/licenses/.
|
|
|
19
19
|
|
|
20
20
|
Lesli · Ruby on Rails SaaS Development Framework.
|
|
21
21
|
|
|
22
|
-
Made with ♥ by
|
|
22
|
+
Made with ♥ by LesliTech
|
|
23
23
|
Building a better future, one line of code at a time.
|
|
24
24
|
|
|
25
25
|
@contact hello@lesli.tech
|
|
@@ -29,57 +29,78 @@ Building a better future, one line of code at a time.
|
|
|
29
29
|
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
30
|
// ·
|
|
31
31
|
=end
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
module LesliAudit
|
|
34
34
|
module LoggerInterface
|
|
35
|
-
def get_user_agent
|
|
36
|
-
|
|
37
|
-
# parse user agent
|
|
38
|
-
user_agent = DeviceDetector.new(request.env["HTTP_USER_AGENT"])
|
|
39
|
-
|
|
40
|
-
#user_agent_version = user_agent.version.to_a.first(2).join(".")
|
|
41
|
-
|
|
42
|
-
# return user agent as object
|
|
43
|
-
{
|
|
44
|
-
platform: user_agent.os_name,
|
|
45
|
-
browser: user_agent.name,
|
|
46
|
-
device: user_agent.device_type
|
|
47
|
-
}
|
|
35
|
+
def get_user_agent
|
|
36
|
+
LesliAudit::UserAgentParser.parse(request.user_agent)
|
|
48
37
|
end
|
|
49
38
|
|
|
50
39
|
def log_requests
|
|
40
|
+
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
41
|
+
return unless current_user
|
|
51
42
|
log_account_requests
|
|
52
43
|
log_user_requests
|
|
53
|
-
|
|
54
|
-
end
|
|
44
|
+
log_user_journal
|
|
45
|
+
end
|
|
55
46
|
|
|
56
|
-
def
|
|
47
|
+
def log_devices
|
|
57
48
|
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
58
49
|
return unless current_user
|
|
59
50
|
|
|
51
|
+
user_agent = get_user_agent()
|
|
52
|
+
|
|
53
|
+
# Try to save a unique record for this request configuration
|
|
54
|
+
current_user.account.audit.account_devices.upsert(
|
|
55
|
+
{
|
|
56
|
+
date: Date2.new.date.to_s,
|
|
57
|
+
:agent_platform => user_agent&.dig(:platform) || "unknown",
|
|
58
|
+
:agent_browser => user_agent&.dig(:browser) || "unknown",
|
|
59
|
+
:agent_device => user_agent&.dig(:device) || "unknown",
|
|
60
|
+
:agent_count => 1
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
# group of columns to consider a request as unique
|
|
64
|
+
unique_by: :lesli_audit_devices_index,
|
|
65
|
+
|
|
66
|
+
# if request id is not unique
|
|
67
|
+
# - increase the counter for this configuration
|
|
68
|
+
# - update the datetime of the last request
|
|
69
|
+
on_duplicate: Arel.sql(
|
|
70
|
+
"agent_count = lesli_audit_account_devices.agent_count + 1, " \
|
|
71
|
+
"updated_at = CURRENT_TIMESTAMP"
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def log_account_requests
|
|
79
|
+
|
|
60
80
|
# Try to save a unique record for this request configuration
|
|
61
81
|
current_user.account.audit.account_requests.upsert(
|
|
62
82
|
{
|
|
63
83
|
request_controller: controller_path,
|
|
64
84
|
request_action: action_name,
|
|
65
85
|
request_count: 1,
|
|
66
|
-
|
|
86
|
+
date: Date2.new.date.to_s
|
|
67
87
|
},
|
|
68
|
-
|
|
88
|
+
|
|
69
89
|
# group of columns to consider a request as unique
|
|
70
|
-
unique_by:
|
|
90
|
+
unique_by: :lesli_audit_account_requests_index,
|
|
71
91
|
|
|
72
92
|
# if request id is not unique, increase the counter for this configuration
|
|
73
|
-
on_duplicate: Arel.sql(
|
|
93
|
+
on_duplicate: Arel.sql(
|
|
94
|
+
"request_count = lesli_audit_account_requests.request_count + 1, " \
|
|
95
|
+
"updated_at = CURRENT_TIMESTAMP"
|
|
96
|
+
)
|
|
74
97
|
)
|
|
75
98
|
end
|
|
76
99
|
|
|
77
100
|
# Track all user activity
|
|
78
101
|
# this is disabled by default in the settings file
|
|
79
102
|
def log_user_requests
|
|
80
|
-
return unless current_user
|
|
81
103
|
return unless session[:user_session_id]
|
|
82
|
-
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
83
104
|
|
|
84
105
|
# Try to save a unique record for this request configuration
|
|
85
106
|
current_user.account.audit.user_requests.upsert(
|
|
@@ -91,54 +112,30 @@ module LesliAudit
|
|
|
91
112
|
date: Date2.new.date.to_s,
|
|
92
113
|
request_count: 1
|
|
93
114
|
},
|
|
94
|
-
|
|
115
|
+
|
|
95
116
|
# group of columns to consider a request as unique
|
|
96
|
-
unique_by:
|
|
117
|
+
unique_by: :lesli_audit_user_requests_index,
|
|
97
118
|
|
|
98
119
|
# if request id is not unique
|
|
99
120
|
# - increase the counter for this configuration
|
|
100
121
|
# - update the datetime of the last request
|
|
101
122
|
on_duplicate: Arel.sql(
|
|
102
|
-
"request_count = lesli_audit_user_requests.request_count + 1,
|
|
123
|
+
"request_count = lesli_audit_user_requests.request_count + 1, " \
|
|
124
|
+
"updated_at = CURRENT_TIMESTAMP"
|
|
103
125
|
)
|
|
104
|
-
)
|
|
126
|
+
)
|
|
127
|
+
end
|
|
105
128
|
|
|
129
|
+
def log_user_journal
|
|
106
130
|
return unless Lesli.config.audit.dig(:enable_journals)
|
|
107
131
|
current_user.account.audit.user_journals.create({
|
|
108
132
|
request_controller: controller_path,
|
|
109
133
|
request_action: action_name,
|
|
110
134
|
session_id: session[:user_session_id],
|
|
111
135
|
user_id: current_user.id,
|
|
112
|
-
date: Date2.new.date.to_s
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def log_devices
|
|
117
|
-
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
118
|
-
return unless current_user
|
|
119
|
-
|
|
120
|
-
user_agent = get_user_agent(false)
|
|
121
|
-
|
|
122
|
-
# Try to save a unique record for this request configuration
|
|
123
|
-
current_user.account.audit.account_devices.upsert(
|
|
124
|
-
{
|
|
125
|
-
:created_at => Date2.new.date.to_s,
|
|
126
|
-
:agent_platform => user_agent&.dig(:platform) || "unknown",
|
|
127
|
-
:agent_browser => user_agent&.dig(:browser) || "unknown",
|
|
128
|
-
:agent_device => user_agent&.dig(:device) || "unknown",
|
|
129
|
-
:agent_count => 1
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
# group of columns to consider a request as unique
|
|
133
|
-
unique_by: %i[agent_platform agent_browser agent_device created_at account_id],
|
|
134
|
-
|
|
135
|
-
# if request id is not unique
|
|
136
|
-
# - increase the counter for this configuration
|
|
137
|
-
# - update the datetime of the last request
|
|
138
|
-
on_duplicate: Arel.sql(
|
|
139
|
-
'agent_count = lesli_audit_account_devices.agent_count + 1'
|
|
140
|
-
)
|
|
141
|
-
)
|
|
136
|
+
date: Date2.new.date.to_s,
|
|
137
|
+
processed_at: Time.current
|
|
138
|
+
})
|
|
142
139
|
end
|
|
143
140
|
|
|
144
141
|
# Track specific account activity
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module LesliAudit
|
|
2
|
+
# Scheduling belongs to the host application. This job only provides a
|
|
3
|
+
# backend-agnostic Active Job entry point for its scheduler.
|
|
4
|
+
class RetentionCleanupJob < ApplicationJob
|
|
5
|
+
def perform(batch_size: LesliAudit::RetentionCleanup::DEFAULT_BATCH_SIZE)
|
|
6
|
+
LesliAudit::RetentionCleanup.call(batch_size: batch_size)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|