lesli_audit 1.0.2 → 1.1.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/app/assets/stylesheets/lesli_audit/application.tailwind.css +2 -0
- data/app/controllers/lesli_audit/users_controller.rb +1 -2
- data/app/controllers/lesli_audit/visitors_controller.rb +14 -0
- data/app/interfaces/lesli_audit/logger_interface.rb +22 -29
- data/app/models/concerns/lesli_audit/user_associations.rb +11 -0
- data/app/services/lesli_audit/user_service.rb +105 -12
- data/app/services/lesli_audit/{analytic_service.rb → visitor_service.rb} +121 -24
- data/app/views/lesli_audit/partials/_navigation.html.erb +1 -1
- data/app/views/lesli_audit/requests/index.html.erb +1 -1
- data/app/views/lesli_audit/users/index.html.erb +75 -37
- data/app/views/lesli_audit/visitors/index.html.erb +164 -0
- data/config/routes.rb +1 -1
- data/db/migrate/v1.0/0501110210_create_lesli_audit_account_devices.rb +2 -1
- data/db/seed/devices.rb +23 -15
- data/db/seed/users.rb +117 -0
- data/db/seeds.rb +2 -2
- data/lib/lesli_audit/engine.rb +6 -0
- data/lib/lesli_audit/version.rb +2 -2
- data/readme.md +113 -49
- metadata +12 -50
- data/app/assets/config/lesli_audit_manifest.js +0 -37
- data/app/controllers/lesli_audit/analytics_controller.rb +0 -23
- data/app/controllers/lesli_audit/user_journals_controller.rb +0 -60
- data/app/helpers/lesli_audit/analytics_helper.rb +0 -4
- data/app/views/lesli_audit/analytics/index.html.erb +0 -44
- data/app/views/lesli_audit/user_journals/_form.html.erb +0 -17
- data/app/views/lesli_audit/user_journals/_user_journal.html.erb +0 -2
- data/app/views/lesli_audit/user_journals/edit.html.erb +0 -12
- data/app/views/lesli_audit/user_journals/index.html.erb +0 -16
- data/app/views/lesli_audit/user_journals/new.html.erb +0 -11
- data/app/views/lesli_audit/user_journals/show.html.erb +0 -10
- data/app/views/lesli_audit/users/show.html.erb +0 -10
- data/lib/scss/application.scss +0 -31
- data/lib/vue/application.js +0 -146
- data/lib/vue/apps/accounts/activities.vue +0 -48
- data/lib/vue/apps/analytics/index.vue +0 -56
- data/lib/vue/apps/analytics/trends.vue +0 -148
- data/lib/vue/apps/dashboards/components/roles.vue +0 -71
- data/lib/vue/apps/dashboards/components/users.vue +0 -71
- data/lib/vue/apps/dashboards/show.vue +0 -8
- data/lib/vue/apps/requests/index.vue +0 -125
- data/lib/vue/apps/security/passwords.vue +0 -52
- data/lib/vue/apps/security/sessions.vue +0 -63
- data/lib/vue/apps/users/activities.vue +0 -49
- data/lib/vue/apps/users/index.vue +0 -89
- data/lib/vue/apps/users/logs.vue +0 -49
- data/lib/vue/apps/users/registrations.vue +0 -73
- data/lib/vue/apps/users/roles.vue +0 -70
- data/lib/vue/apps/users/workingHours.vue +0 -52
- data/lib/vue/components/requests.vue +0 -63
- data/lib/vue/components/visitors.vue +0 -76
- data/lib/vue/stores/accounts/activities.js +0 -87
- data/lib/vue/stores/analytics.js +0 -127
- data/lib/vue/stores/request.js +0 -70
- data/lib/vue/stores/security/password.js +0 -75
- data/lib/vue/stores/security/session.js +0 -78
- data/lib/vue/stores/translations.json +0 -162
- data/lib/vue/stores/users/activities.js +0 -76
- data/lib/vue/stores/users/logs.js +0 -74
- data/lib/vue/stores/users/registrations.js +0 -61
- data/lib/vue/stores/users/roles.js +0 -52
- data/lib/vue/stores/users/working_hours.js +0 -92
- data/lib/vue/stores/users.js +0 -56
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
|
|
2
|
+
<%
|
|
3
|
+
visitors = @visitors.reverse
|
|
4
|
+
top_days = visitors.last(3)
|
|
5
|
+
%>
|
|
6
|
+
|
|
7
|
+
<%= render(LesliView::Layout::Container.new("audit-analytics")) do %>
|
|
8
|
+
<%= render(LesliView::Components::Header.new("Analytics")) %>
|
|
9
|
+
|
|
10
|
+
<section
|
|
11
|
+
class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-5"
|
|
12
|
+
aria-label="Visitor overview">
|
|
13
|
+
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
|
14
|
+
<h2 class="text-sm font-medium text-slate-500">Controllers</h2>
|
|
15
|
+
<p class="mt-3 text-3xl font-semibold tracking-tight text-slate-900 tabular-nums">
|
|
16
|
+
<%= @visits[:controllers] %>
|
|
17
|
+
</p>
|
|
18
|
+
</article>
|
|
19
|
+
|
|
20
|
+
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
|
21
|
+
<h2 class="text-sm font-medium text-slate-500">Requests</h2>
|
|
22
|
+
<p class="mt-3 text-3xl font-semibold tracking-tight text-slate-900 tabular-nums">
|
|
23
|
+
<%= @visits[:requests] %>
|
|
24
|
+
</p>
|
|
25
|
+
</article>
|
|
26
|
+
|
|
27
|
+
<% top_days.each do |day| %>
|
|
28
|
+
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
|
29
|
+
<h2 class="text-sm font-medium text-slate-500"><%= day["xaxiskey"] %></h2>
|
|
30
|
+
<p class="mt-3 text-3xl font-semibold tracking-tight text-slate-900 tabular-nums">
|
|
31
|
+
<%= day["yaxiskey"] %>
|
|
32
|
+
</p>
|
|
33
|
+
</article>
|
|
34
|
+
<% end %>
|
|
35
|
+
</section>
|
|
36
|
+
|
|
37
|
+
<section class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
|
38
|
+
<%= render(LesliView::Charts::Line.new(
|
|
39
|
+
title: "Visitors",
|
|
40
|
+
database_to_dataset: visitors
|
|
41
|
+
)) %>
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
<section class="mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3" aria-label="Visitor breakdown">
|
|
45
|
+
<article class="min-w-0">
|
|
46
|
+
<%= render(LesliView::Elements::Table.new(
|
|
47
|
+
columns: [
|
|
48
|
+
{ label: "Browsers", align: :left },
|
|
49
|
+
{ label: "Visitors", align: :center }
|
|
50
|
+
],
|
|
51
|
+
class_name: "!min-w-0",
|
|
52
|
+
compact: true,
|
|
53
|
+
caption: "Visitors by browser"
|
|
54
|
+
)) do |table| %>
|
|
55
|
+
<% @browsers.each do |browser| %>
|
|
56
|
+
<% percentage = browser[:percentage].to_i.clamp(0, 100) %>
|
|
57
|
+
<% table.with_row do |row| %>
|
|
58
|
+
<% row.with_cell(class_name: "!p-0") do %>
|
|
59
|
+
<div class="relative isolate overflow-hidden px-3 py-2.5">
|
|
60
|
+
<span
|
|
61
|
+
class="absolute inset-y-0 left-0 -z-10 bg-slate-100"
|
|
62
|
+
style="width: <%= percentage %>%"
|
|
63
|
+
aria-hidden="true"></span>
|
|
64
|
+
<span class="flex items-center gap-2 text-slate-700">
|
|
65
|
+
<span class="inline-flex h-7 w-7 shrink-0 items-center justify-center text-slate-500">
|
|
66
|
+
<span class="ri-<%= browser[:icon] %> text-lg leading-none" aria-hidden="true"></span>
|
|
67
|
+
</span>
|
|
68
|
+
<span class="truncate"><%= browser[:browser] %></span>
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
<% end %>
|
|
72
|
+
<% row.with_cell(class_name: "font-semibold tabular-nums text-slate-900", align: :center) do %>
|
|
73
|
+
<%= browser[:count] %>
|
|
74
|
+
<% end %>
|
|
75
|
+
<% end %>
|
|
76
|
+
<% end %>
|
|
77
|
+
<% end %>
|
|
78
|
+
</article>
|
|
79
|
+
|
|
80
|
+
<article class="min-w-0">
|
|
81
|
+
<%= render(LesliView::Elements::Table.new(
|
|
82
|
+
columns: [
|
|
83
|
+
{ label: "OS", align: :left },
|
|
84
|
+
{ label: "Visitors", align: :center }
|
|
85
|
+
],
|
|
86
|
+
class_name: "!min-w-0",
|
|
87
|
+
compact: true,
|
|
88
|
+
caption: "Visitors by operating system"
|
|
89
|
+
)) do |table| %>
|
|
90
|
+
<% @os.each do |device| %>
|
|
91
|
+
<% percentage = device[:percentage].to_i.clamp(0, 100) %>
|
|
92
|
+
<% table.with_row do |row| %>
|
|
93
|
+
<% row.with_cell(class_name: "!p-0") do %>
|
|
94
|
+
<div class="relative isolate overflow-hidden px-3 py-2.5">
|
|
95
|
+
<span
|
|
96
|
+
class="absolute inset-y-0 left-0 -z-10 bg-slate-100"
|
|
97
|
+
style="width: <%= percentage %>%"
|
|
98
|
+
aria-hidden="true"></span>
|
|
99
|
+
<span class="flex items-center gap-2 text-slate-700">
|
|
100
|
+
<span class="inline-flex h-7 w-7 shrink-0 items-center justify-center text-slate-500">
|
|
101
|
+
<span class="ri-<%= device[:icon].downcase %> text-lg leading-none" aria-hidden="true"></span>
|
|
102
|
+
</span>
|
|
103
|
+
<span class="truncate"><%= device[:device] %></span>
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
<% end %>
|
|
107
|
+
<% row.with_cell(class_name: "font-semibold tabular-nums text-slate-900", align: :center) do %>
|
|
108
|
+
<%= device[:count] %>
|
|
109
|
+
<% end %>
|
|
110
|
+
<% end %>
|
|
111
|
+
<% end %>
|
|
112
|
+
<% end %>
|
|
113
|
+
</article>
|
|
114
|
+
|
|
115
|
+
<article class="min-w-0">
|
|
116
|
+
<%= render(LesliView::Elements::Table.new(
|
|
117
|
+
columns: [
|
|
118
|
+
{ label: "Device", align: :left },
|
|
119
|
+
{ label: "Visitors", align: :center }
|
|
120
|
+
],
|
|
121
|
+
class_name: "!min-w-0",
|
|
122
|
+
compact: true,
|
|
123
|
+
caption: "Visitors by device"
|
|
124
|
+
)) do |table| %>
|
|
125
|
+
<% @devices.each do |device| %>
|
|
126
|
+
<% percentage = device[:percentage].to_i.clamp(0, 100) %>
|
|
127
|
+
<% table.with_row do |row| %>
|
|
128
|
+
<% row.with_cell(class_name: "!p-0") do %>
|
|
129
|
+
<div class="relative isolate overflow-hidden px-3 py-2.5">
|
|
130
|
+
<span
|
|
131
|
+
class="absolute inset-y-0 left-0 -z-10 bg-slate-100"
|
|
132
|
+
style="width: <%= percentage %>%"
|
|
133
|
+
aria-hidden="true"></span>
|
|
134
|
+
<span class="flex items-center gap-2 text-slate-700">
|
|
135
|
+
<span class="inline-flex h-7 w-7 shrink-0 items-center justify-center text-slate-500">
|
|
136
|
+
<span class="ri-<%= device[:icon] %> text-lg leading-none" aria-hidden="true"></span>
|
|
137
|
+
</span>
|
|
138
|
+
<span class="truncate"><%= device[:device] %></span>
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
<% end %>
|
|
142
|
+
<% row.with_cell(class_name: "font-semibold tabular-nums text-slate-900", align: :center) do %>
|
|
143
|
+
<%= device[:count] %>
|
|
144
|
+
<% end %>
|
|
145
|
+
<% end %>
|
|
146
|
+
<% end %>
|
|
147
|
+
<% end %>
|
|
148
|
+
</article>
|
|
149
|
+
</section>
|
|
150
|
+
|
|
151
|
+
<section class="mt-6 pb-8" aria-label="Top controllers">
|
|
152
|
+
<%= render(LesliView::Elements::Table.new(
|
|
153
|
+
columns: [{
|
|
154
|
+
label: "Top controllers",
|
|
155
|
+
field: "request_controller"
|
|
156
|
+
}, {
|
|
157
|
+
label: "Requests",
|
|
158
|
+
field: "requests"
|
|
159
|
+
}],
|
|
160
|
+
records: @requests,
|
|
161
|
+
caption: "Requests by controller"
|
|
162
|
+
)) %>
|
|
163
|
+
</section>
|
|
164
|
+
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -35,11 +35,12 @@ class CreateLesliAuditAccountDevices < ActiveRecord::Migration[8.1]
|
|
|
35
35
|
create_table :lesli_audit_account_devices do |t|
|
|
36
36
|
t.string :agent_platform
|
|
37
37
|
t.string :agent_browser
|
|
38
|
+
t.string :agent_device
|
|
38
39
|
t.integer :agent_count
|
|
39
40
|
t.date :created_at
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
add_reference(:lesli_audit_account_devices, :account, foreign_key: { to_table: :lesli_audit_accounts })
|
|
43
|
-
add_index(:lesli_audit_account_devices, %i[agent_platform agent_browser created_at account_id], unique: true, name: "lesli_audit_devices_index")
|
|
44
|
+
add_index(:lesli_audit_account_devices, %i[agent_platform agent_browser agent_device created_at account_id], unique: true, name: "lesli_audit_devices_index")
|
|
44
45
|
end
|
|
45
46
|
end
|
data/db/seed/devices.rb
CHANGED
|
@@ -44,25 +44,33 @@ current_user = Lesli::User.first
|
|
|
44
44
|
|
|
45
45
|
# controllers to seed
|
|
46
46
|
[
|
|
47
|
-
"
|
|
47
|
+
"Mac",
|
|
48
48
|
"Windows"
|
|
49
49
|
].each do |platform|
|
|
50
50
|
|
|
51
51
|
[
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
].each do |
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
"smartphone",
|
|
53
|
+
"tablet",
|
|
54
|
+
"desktop"
|
|
55
|
+
].each do |device|
|
|
56
|
+
|
|
57
|
+
[
|
|
58
|
+
"Firefox",
|
|
59
|
+
"Chrome",
|
|
60
|
+
"Safari"
|
|
61
|
+
].each do |browser|
|
|
62
|
+
# Iterate through the dates
|
|
63
|
+
(start_date..end_date).each do |date|
|
|
64
|
+
|
|
65
|
+
current_user.account.audit.account_devices.create_with(
|
|
66
|
+
:agent_count => rand(40..80),
|
|
67
|
+
).find_or_create_by(
|
|
68
|
+
:agent_platform => platform,
|
|
69
|
+
:agent_browser => browser,
|
|
70
|
+
:agent_device => device,
|
|
71
|
+
:created_at => date,
|
|
72
|
+
)
|
|
73
|
+
end
|
|
66
74
|
end
|
|
67
75
|
end
|
|
68
76
|
end
|
data/db/seed/users.rb
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
return unless Rails.env.development?
|
|
4
|
+
|
|
5
|
+
account = Lesli::Account.first!
|
|
6
|
+
|
|
7
|
+
password = if defined?(Devise)
|
|
8
|
+
Devise::Encryptor.digest(Lesli::User, "password123")
|
|
9
|
+
else
|
|
10
|
+
BCrypt::Password.create("password123")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
now = Time.current
|
|
14
|
+
|
|
15
|
+
# Adjust these numbers freely while building the dashboard.
|
|
16
|
+
TOTAL_USERS = 1_000
|
|
17
|
+
TOTAL_INACTIVE_USERS = 120
|
|
18
|
+
TOTAL_LOCKED_USERS = 45
|
|
19
|
+
TOTAL_UNCONFIRMED_USERS = 80
|
|
20
|
+
TOTAL_USERS_NEVER_SIGNED_IN = 140
|
|
21
|
+
TOTAL_USERS_ACTIVE_LAST_30_DAYS = 520
|
|
22
|
+
TOTAL_NEW_USERS_LAST_30_DAYS = 75
|
|
23
|
+
TOTAL_USERS_WITH_FAILED_ATTEMPTS = 60
|
|
24
|
+
TOTAL_DELETED_USERS = 25
|
|
25
|
+
|
|
26
|
+
users = []
|
|
27
|
+
|
|
28
|
+
TOTAL_USERS.times do |index|
|
|
29
|
+
uid = "DEV-USR-#{index.to_s.rjust(5, "0")}"
|
|
30
|
+
|
|
31
|
+
users << {
|
|
32
|
+
uid: uid,
|
|
33
|
+
account_id: account.id,
|
|
34
|
+
|
|
35
|
+
active: true,
|
|
36
|
+
|
|
37
|
+
first_name: "Demo",
|
|
38
|
+
last_name: "User #{index + 1}",
|
|
39
|
+
email: "demo.user.#{index + 1}@lesli.dev",
|
|
40
|
+
encrypted_password: password,
|
|
41
|
+
|
|
42
|
+
confirmed_at: now - rand(1..180).days,
|
|
43
|
+
confirmation_sent_at: nil,
|
|
44
|
+
confirmation_token: nil,
|
|
45
|
+
|
|
46
|
+
sign_in_count: rand(1..80),
|
|
47
|
+
current_sign_in_at: now - rand(1..90).days,
|
|
48
|
+
last_sign_in_at: now - rand(1..90).days,
|
|
49
|
+
current_sign_in_ip: "127.0.0.1",
|
|
50
|
+
last_sign_in_ip: "127.0.0.1",
|
|
51
|
+
|
|
52
|
+
failed_attempts: 0,
|
|
53
|
+
|
|
54
|
+
locked_at: nil,
|
|
55
|
+
locked_until: nil,
|
|
56
|
+
unlock_token: nil,
|
|
57
|
+
|
|
58
|
+
created_at: now - rand(31..365).days,
|
|
59
|
+
updated_at: now,
|
|
60
|
+
deleted_at: nil
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Inactive users
|
|
65
|
+
users.first(TOTAL_INACTIVE_USERS).each do |user|
|
|
66
|
+
user[:active] = false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Locked users
|
|
70
|
+
users.slice(120, TOTAL_LOCKED_USERS).each do |user|
|
|
71
|
+
user[:locked_at] = now - rand(1..15).days
|
|
72
|
+
user[:locked_until] = now + rand(1..15).days
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Unconfirmed users
|
|
76
|
+
users.slice(200, TOTAL_UNCONFIRMED_USERS).each do |user|
|
|
77
|
+
user[:confirmed_at] = nil
|
|
78
|
+
user[:confirmation_sent_at] = now - rand(1..10).days
|
|
79
|
+
user[:confirmation_token] = SecureRandom.hex(16)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Never signed in
|
|
83
|
+
users.slice(300, TOTAL_USERS_NEVER_SIGNED_IN).each do |user|
|
|
84
|
+
user[:sign_in_count] = 0
|
|
85
|
+
user[:current_sign_in_at] = nil
|
|
86
|
+
user[:last_sign_in_at] = nil
|
|
87
|
+
user[:current_sign_in_ip] = nil
|
|
88
|
+
user[:last_sign_in_ip] = nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Active in the last 30 days
|
|
92
|
+
users.slice(450, TOTAL_USERS_ACTIVE_LAST_30_DAYS).each do |user|
|
|
93
|
+
user[:sign_in_count] = rand(1..100)
|
|
94
|
+
user[:last_sign_in_at] = now - rand(0..29).days
|
|
95
|
+
user[:current_sign_in_at] = user[:last_sign_in_at]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# New users in the last 30 days
|
|
99
|
+
users.last(TOTAL_NEW_USERS_LAST_30_DAYS).each do |user|
|
|
100
|
+
user[:created_at] = now - rand(0..29).days
|
|
101
|
+
user[:updated_at] = now
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Users with failed attempts
|
|
105
|
+
users.slice(700, TOTAL_USERS_WITH_FAILED_ATTEMPTS).each do |user|
|
|
106
|
+
user[:failed_attempts] = rand(1..5)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Soft-deleted users
|
|
110
|
+
users.last(TOTAL_DELETED_USERS).each do |user|
|
|
111
|
+
user[:deleted_at] = now - rand(1..30).days
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Remove previous generated users only
|
|
115
|
+
Lesli::User.where("uid LIKE ?", "DEV-USR-%").delete_all
|
|
116
|
+
|
|
117
|
+
Lesli::User.insert_all!(users)
|
data/db/seeds.rb
CHANGED
|
@@ -30,12 +30,12 @@ Building a better future, one line of code at a time.
|
|
|
30
30
|
// ·
|
|
31
31
|
=end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
L2.msg("LesliAudit", "Version: #{LesliAudit::VERSION}", "Build: #{LesliAudit::BUILD}")
|
|
33
|
+
Termline.info("Loading seeds for: LesliAudit #{LesliAudit::VERSION} (#{LesliAudit::BUILD})")
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
# · load specific environment seeds
|
|
38
37
|
if Rails.env.development? || Lesli.config.demo
|
|
39
38
|
load LesliAudit::Engine.root.join("db", "seed", "requests.rb")
|
|
40
39
|
load LesliAudit::Engine.root.join("db", "seed", "devices.rb")
|
|
40
|
+
load LesliAudit::Engine.root.join("db", "seed", "users.rb")
|
|
41
41
|
end
|
data/lib/lesli_audit/engine.rb
CHANGED
|
@@ -3,6 +3,12 @@ module LesliAudit
|
|
|
3
3
|
class Engine < ::Rails::Engine
|
|
4
4
|
isolate_namespace LesliAudit
|
|
5
5
|
|
|
6
|
+
config.to_prepare do
|
|
7
|
+
unless Lesli::User < LesliAudit::UserAssociations
|
|
8
|
+
Lesli::User.include(LesliAudit::UserAssociations)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
6
12
|
initializer :lesli_admin do |app|
|
|
7
13
|
|
|
8
14
|
# register assets manifest
|
data/lib/lesli_audit/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<h1 align="center">
|
|
3
|
+
<img width="100" alt="LesliAudit" src="./app/assets/images/lesli_audit/audit-logo.svg" />
|
|
4
|
+
</h1>
|
|
5
|
+
<h3 align="center">Audit trails and analytics for the Lesli Framework.</h3>
|
|
4
6
|
</div>
|
|
5
7
|
|
|
6
8
|
<br />
|
|
7
|
-
<hr/>
|
|
8
9
|
|
|
9
10
|
<div align="center">
|
|
10
|
-
<a target="_blank" href="https://github.com/LesliTech/LesliAudit/actions">
|
|
11
|
-
<img alt="
|
|
11
|
+
<a target="_blank" href="https://github.com/LesliTech/LesliAudit/actions/workflows/lesli-ci-tests.yaml">
|
|
12
|
+
<img alt="LesliAudit test status" src="https://img.shields.io/github/actions/workflow/status/LesliTech/LesliAudit/lesli-ci-tests.yaml?branch=master&style=for-the-badge&logo=github&label=tests">
|
|
12
13
|
</a>
|
|
13
14
|
<a target="_blank" href="https://rubygems.org/gems/lesli_audit">
|
|
14
15
|
<img alt="Gem Version" src="https://img.shields.io/gem/v/lesli_audit?style=for-the-badge&logo=ruby">
|
|
@@ -16,83 +17,149 @@
|
|
|
16
17
|
<a target="_blank" href="https://codecov.io/github/LesliTech/LesliAudit">
|
|
17
18
|
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/LesliTech/LesliAudit?style=for-the-badge&logo=codecov">
|
|
18
19
|
</a>
|
|
20
|
+
<a target="_blank" href="https://sonarcloud.io/project/overview?id=LesliTech_LesliAudit">
|
|
21
|
+
<img alt="Sonar Quality Gate" src="https://img.shields.io/sonar/quality_gate/LesliTech_LesliAudit?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=Quality">
|
|
22
|
+
</a>
|
|
19
23
|
</div>
|
|
20
24
|
|
|
21
|
-
<hr/>
|
|
22
25
|
<br />
|
|
23
26
|
|
|
24
27
|
<div align="center">
|
|
25
28
|
<img
|
|
26
29
|
style="width:100%;max-width:800px;border-radius:6px;"
|
|
27
|
-
alt="
|
|
30
|
+
alt="LesliAudit activity and request history"
|
|
31
|
+
src="./docs/images/screenshot.png" />
|
|
28
32
|
</div>
|
|
29
33
|
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<br />
|
|
37
|
+
|
|
38
|
+
## Introduction
|
|
39
|
+
|
|
40
|
+
LesliAudit is the official audit trail and analytics engine for the [Lesli Framework](https://github.com/LesliTech/Lesli).
|
|
41
|
+
|
|
42
|
+
It records account activity and provides administrators with searchable operational history and usage insights.
|
|
43
|
+
|
|
30
44
|
<br />
|
|
31
|
-
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- Account activity and audit logs
|
|
49
|
+
- Request and navigation history
|
|
50
|
+
- Visitor and authenticated-user analytics
|
|
51
|
+
- Device and user-agent information
|
|
52
|
+
- Detailed views for investigating recorded activity
|
|
53
|
+
|
|
54
|
+
<br />
|
|
55
|
+
|
|
56
|
+
## Try LesliAudit
|
|
57
|
+
|
|
58
|
+
- [Try the online demo](https://demo.lesli.dev/)
|
|
59
|
+
- [Run the Docker demo](https://github.com/LesliTech/lesli-docker-demo)
|
|
60
|
+
|
|
32
61
|
<br />
|
|
33
62
|
|
|
34
|
-
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Requirements
|
|
66
|
+
|
|
67
|
+
- A Rails application with [Lesli](https://rubygems.org/gems/lesli)
|
|
68
|
+
- SQLite by default, or PostgreSQL when preferred by the host application
|
|
69
|
+
|
|
70
|
+
### Install LesliAudit
|
|
71
|
+
|
|
72
|
+
Add the engine to the host Rails application and prepare its database:
|
|
35
73
|
|
|
36
74
|
```shell
|
|
37
|
-
# Add LesliAudit engine gem
|
|
38
75
|
bundle add lesli_audit
|
|
76
|
+
bin/rails lesli:db:prepare
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Mount the engine
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
|
|
81
|
+
Applications using Lesli's standard router mount LesliAudit automatically at `/audit`:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# config/routes.rb
|
|
85
|
+
Rails.application.routes.draw do
|
|
86
|
+
Lesli::Router.mount(self)
|
|
87
|
+
end
|
|
42
88
|
```
|
|
43
89
|
|
|
90
|
+
If the application does not use the standard Lesli router, mount the engine directly:
|
|
91
|
+
|
|
44
92
|
```ruby
|
|
45
|
-
#
|
|
93
|
+
# config/routes.rb
|
|
46
94
|
Rails.application.routes.draw do
|
|
47
95
|
mount LesliAudit::Engine => "/audit"
|
|
48
96
|
end
|
|
49
97
|
```
|
|
50
98
|
|
|
51
|
-
|
|
52
|
-
|
|
99
|
+
Start Rails and visit [http://127.0.0.1:3000/audit](http://127.0.0.1:3000/audit):
|
|
100
|
+
|
|
101
|
+
```shell
|
|
102
|
+
bin/rails server
|
|
103
|
+
```
|
|
104
|
+
|
|
53
105
|
<br />
|
|
54
106
|
|
|
55
|
-
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
Clone LesliAudit into the host application's `engines` directory:
|
|
56
110
|
|
|
57
111
|
```shell
|
|
58
|
-
|
|
59
|
-
|
|
112
|
+
cd RailsApp
|
|
113
|
+
mkdir -p engines
|
|
114
|
+
git clone https://github.com/LesliTech/LesliAudit.git engines/LesliAudit
|
|
115
|
+
```
|
|
60
116
|
|
|
61
|
-
|
|
62
|
-
gem "lesli", path: "engines/LesliAudit"
|
|
117
|
+
Reference the local engine from the host application's `Gemfile`:
|
|
63
118
|
|
|
64
|
-
|
|
119
|
+
```ruby
|
|
120
|
+
gem "lesli_audit", path: "engines/LesliAudit"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Install dependencies, prepare the host database, and start Rails:
|
|
124
|
+
|
|
125
|
+
```shell
|
|
65
126
|
bundle install
|
|
127
|
+
bin/rails lesli:db:prepare
|
|
128
|
+
bin/rails server
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Tests
|
|
66
132
|
|
|
67
|
-
|
|
68
|
-
rake lesli:db:setup
|
|
133
|
+
From a complete Lesli development workspace, run the engine test suite from the LesliAudit directory:
|
|
69
134
|
|
|
135
|
+
```shell
|
|
136
|
+
cd engines/LesliAudit
|
|
137
|
+
bin/rails test
|
|
70
138
|
```
|
|
71
139
|
|
|
72
140
|
<br />
|
|
73
|
-
<hr/>
|
|
74
|
-
<br />
|
|
75
|
-
|
|
76
|
-
### Demo
|
|
77
141
|
|
|
78
|
-
|
|
79
|
-
* [docker demo](https://github.com/LesliTech/lesli-docker-demo)
|
|
142
|
+
## Documentation
|
|
80
143
|
|
|
144
|
+
- [Lesli website](https://www.lesli.dev/)
|
|
145
|
+
- [Documentation](https://www.lesli.dev/engines/audit)
|
|
146
|
+
- [Frontend development](./docs/about/frontend.md)
|
|
147
|
+
- [Release notes](https://github.com/LesliTech/LesliAudit/releases)
|
|
148
|
+
- [Issue tracker](https://github.com/LesliTech/LesliAudit/issues)
|
|
149
|
+
- [Source code](https://github.com/LesliTech/LesliAudit)
|
|
81
150
|
|
|
82
|
-
|
|
83
|
-
* [website](https://www.lesli.dev/)
|
|
84
|
-
* [documentation](https://www.lesli.dev/engines/audit)
|
|
151
|
+
<br />
|
|
85
152
|
|
|
153
|
+
## Community
|
|
86
154
|
|
|
87
|
-
|
|
155
|
+
- [X: @LesliTech](https://x.com/LesliTech)
|
|
156
|
+
- [hello@lesli.tech](mailto:hello@lesli.tech)
|
|
157
|
+
- [https://www.lesli.tech](https://www.lesli.tech)
|
|
88
158
|
|
|
89
|
-
|
|
90
|
-
* [Email: hello@lesli.tech](hello@lesli.tech)
|
|
91
|
-
* [Website: https://www.lesli.tech](https://www.lesli.tech)
|
|
159
|
+
<br />
|
|
92
160
|
|
|
161
|
+
## License
|
|
93
162
|
|
|
94
|
-
### License
|
|
95
|
-
-------
|
|
96
163
|
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
97
164
|
|
|
98
165
|
This program is free software: you can redistribute it and/or modify
|
|
@@ -106,19 +173,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
106
173
|
GNU General Public License for more details.
|
|
107
174
|
|
|
108
175
|
You should have received a copy of the GNU General Public License
|
|
109
|
-
along with this program. If not, see
|
|
176
|
+
along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
|
|
110
177
|
|
|
111
|
-
|
|
112
|
-
<hr />
|
|
113
|
-
<br />
|
|
114
|
-
<br />
|
|
178
|
+
The complete license text is available in the [license file](./license).
|
|
115
179
|
|
|
116
|
-
|
|
117
|
-
<img width="200" alt="Lesli logo" src="https://cdn.lesli.tech/lesli/brand/app-logo.svg" />
|
|
118
|
-
<h3 align="center" class="mt-0">
|
|
119
|
-
The Open-Source SaaS Development Framework for Ruby on Rails.
|
|
120
|
-
</h3>
|
|
121
|
-
</div>
|
|
180
|
+
---
|
|
122
181
|
|
|
123
182
|
<br />
|
|
124
183
|
<br />
|
|
184
|
+
|
|
185
|
+
<div align="center">
|
|
186
|
+
<img width="80" alt="Lesli icon" src="https://cdn.lesli.tech/lesli/brand/app-icon.svg" />
|
|
187
|
+
<h3 align="center">The Open-Source SaaS Development Framework for Ruby on Rails.</h3>
|
|
188
|
+
</div>
|