lesli_audit 1.1.1 → 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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +6 -1
  3. data/app/assets/stylesheets/lesli_audit/application.css +366 -35
  4. data/app/controllers/lesli_audit/accounts_controller.rb +7 -3
  5. data/app/controllers/lesli_audit/logs_controller.rb +3 -8
  6. data/app/controllers/lesli_audit/visitors_controller.rb +1 -1
  7. data/app/interfaces/lesli_audit/logger_interface.rb +56 -59
  8. data/app/jobs/lesli_audit/retention_cleanup_job.rb +9 -0
  9. data/app/models/concerns/lesli_audit/append_only.rb +12 -0
  10. data/app/models/lesli_audit/account.rb +26 -10
  11. data/app/models/lesli_audit/account_device.rb +3 -1
  12. data/app/models/lesli_audit/account_log.rb +12 -7
  13. data/app/models/lesli_audit/account_request.rb +3 -1
  14. data/app/models/lesli_audit/user_journal.rb +16 -3
  15. data/app/models/lesli_audit/user_log.rb +12 -3
  16. data/app/models/lesli_audit/user_request.rb +7 -3
  17. data/app/services/lesli_audit/activity_audit.rb +130 -0
  18. data/app/services/lesli_audit/request_service.rb +2 -35
  19. data/app/services/lesli_audit/retention_cleanup.rb +146 -0
  20. data/app/services/lesli_audit/user_agent_parser.rb +51 -0
  21. data/app/services/lesli_audit/user_service.rb +18 -16
  22. data/app/services/lesli_audit/visitor_service.rb +22 -49
  23. data/app/views/lesli_audit/logs/show.html.erb +14 -8
  24. data/app/views/lesli_audit/partials/_navigation.html.erb +1 -1
  25. data/app/views/lesli_audit/users/index.html.erb +0 -3
  26. data/app/views/lesli_audit/visitors/index.html.erb +12 -12
  27. data/config/routes.rb +3 -2
  28. data/db/migrate/v1.0/0501000110_create_lesli_audit_accounts.rb +14 -1
  29. data/db/migrate/v1.0/0501110110_create_lesli_audit_account_logs.rb +10 -6
  30. data/db/migrate/v1.0/0501110210_create_lesli_audit_account_devices.rb +10 -7
  31. data/db/migrate/v1.0/0501110310_create_lesli_audit_account_requests.rb +10 -7
  32. data/db/migrate/v1.0/0501120110_create_lesli_audit_user_logs.rb +11 -7
  33. data/db/migrate/v1.0/0501120210_create_lesli_audit_user_journals.rb +19 -8
  34. data/db/migrate/v1.0/0501120310_create_lesli_audit_user_requests.rb +13 -10
  35. data/db/seed/devices.rb +1 -1
  36. data/db/seed/requests.rb +3 -3
  37. data/lib/lesli_audit/engine.rb +1 -12
  38. data/lib/lesli_audit/version.rb +2 -2
  39. data/lib/tasks/lesli_audit_tasks.rake +12 -4
  40. data/readme.md +18 -5
  41. metadata +14 -6
  42. data/app/assets/stylesheets/lesli_audit/application.tailwind.css +0 -2
  43. data/app/models/concerns/lesli_audit/user_associations.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ebf3748b965f8b36dcd0861b281f5fabf373d59e71661651b01cece8bdc4c18
4
- data.tar.gz: 712eefe307ebef94bbc2b03f94ecfa608303d30d58688c99ed303fa248b7b8d0
3
+ metadata.gz: 0f3d2dc51c06a85eb50d460ee02c27930bc2de5765e4745c73ddbd7cdc83d789
4
+ data.tar.gz: e3e6036dc2db7b777081398d4ee9bcde2a6707095bd7c30215956f98da6c6f82
5
5
  SHA512:
6
- metadata.gz: 372f1ff92fd88a88d80ae01a94fe8d2f5f5c754922cc7bbe596f6207cac738fe7799762b9393457c2b6806c5dafd0fb1d0fa04bc9e88ce2eddb6c804d25a19a8
7
- data.tar.gz: ab4e0943748344c2fedeb6b32f70e241f710a2ec28f95b730d986a0a486da83ca95ac2f6d81aa9fabd18ba8f41f6f9cd3cf658696a6d418df58e293f95344958
6
+ metadata.gz: ec1b7c2ef3957736a5832dee9c2a20f627e809c59e698401d44fdc1c356f6e5ba46d85b33e456446b036b7d83d70f97eb2f0a7bfea5fb8a48340893643fa3e40
7
+ data.tar.gz: d772a35fca2f2595305125b65e51b1f825df8c51ae5e020b553d5aabaf0cdfb62d7aa5c78586bf50945ebf54fa2265ddc4c3e7d7b9cfaa9da60f17e3dabef2cd
data/Rakefile CHANGED
@@ -1,5 +1,10 @@
1
1
  require "bundler/setup"
2
2
 
3
- load "rails/tasks/statistics.rake"
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
- !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./node_modules/sass-loader/dist/cjs.js!../LesliAudit/lib/scss/application.scss ***!
3
- \************************************************************************************************************************************************************/
4
- @charset "UTF-8";
5
- /*
6
-
7
- Lesli
8
-
9
- Copyright (c) 2023, Lesli Technologies, S. A.
10
-
11
- This program is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- This program is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU General Public License for more details.
20
-
21
- You should have received a copy of the GNU General Public License
22
- along with this program. If not, see http://www.gnu.org/licenses/.
23
-
24
- Lesli · Ruby on Rails SaaS Development Framework.
25
-
26
- Made with ♥ by https://www.lesli.tech
27
- Building a better future, one line of code at a time.
28
-
29
- @contact hello@lesli.tech
30
- @website https://www.lesli.tech
31
- @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
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 @account, notice: "Account was successfully updated.", status: :see_other
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 accounts_url, notice: "Account was successfully destroyed.", status: :see_other
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: %i[ show ]
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/1
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 = Log.find(params.expect(:id))
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
- @visitors = VisitorService.new(current_user, query).visitors
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) 2023, Lesli Technologies, S. A.
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 https://www.lesli.tech
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
- require 'device_detector'
32
+
33
33
  module LesliAudit
34
34
  module LoggerInterface
35
- def get_user_agent(as_string=true)
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
- log_devices
54
- end
44
+ log_user_journal
45
+ end
55
46
 
56
- def log_account_requests
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
- created_at: Date2.new.date.to_s
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: %i[request_controller request_action created_at account_id],
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("request_count = lesli_audit_account_requests.request_count + 1")
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: %i[request_controller request_action date user_id session_id],
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,updated_at = #{LesliDate::Compatibility.db_now}"
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
- end
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