lesli_audit 1.0.1 → 1.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/images/lesli_audit/audit-logo.svg +1 -72
- data/app/controllers/lesli_audit/dashboards_controller.rb +0 -2
- data/app/controllers/lesli_audit/logs_controller.rb +25 -0
- data/app/controllers/lesli_audit/requests_controller.rb +1 -1
- data/app/controllers/lesli_audit/users_controller.rb +2 -1
- data/app/controllers/lesli_audit/visitors_controller.rb +14 -0
- data/app/helpers/lesli_audit/{analytics_helper.rb → logs_helper.rb} +1 -1
- data/app/helpers/lesli_audit/user_journals_helper.rb +4 -0
- data/app/interfaces/lesli_audit/logger_interface.rb +53 -25
- data/app/models/lesli_audit/account.rb +7 -1
- data/app/models/lesli_audit/account_device.rb +5 -0
- data/app/models/lesli_audit/account_log.rb +11 -0
- data/app/models/lesli_audit/dashboard.rb +1 -2
- data/app/models/lesli_audit/user_journal.rb +7 -0
- data/app/models/lesli_audit/user_log.rb +7 -0
- data/app/models/lesli_audit/user_request.rb +1 -1
- data/app/{assets/config/lesli_audit_manifest.js → services/lesli_audit/log_service.rb} +14 -8
- data/app/services/lesli_audit/request_service.rb +1 -2
- data/app/services/lesli_audit/user_service.rb +146 -2
- data/app/services/lesli_audit/visitor_service.rb +239 -0
- data/app/views/lesli_audit/dashboards/_component-calendar.html.erb +0 -0
- data/app/views/lesli_audit/dashboards/show.html.erb +1 -1
- data/app/views/lesli_audit/logs/index.html.erb +23 -0
- data/app/views/lesli_audit/logs/show.html.erb +10 -0
- data/app/views/lesli_audit/partials/_navigation.html.erb +4 -5
- data/app/views/lesli_audit/requests/index.html.erb +1 -1
- data/app/views/lesli_audit/users/index.html.erb +66 -5
- data/app/views/lesli_audit/visitors/index.html.erb +118 -0
- data/config/routes.rb +8 -27
- data/{lib/vue/apps/dashboards/components/users.vue → db/migrate/v1.0/0501110110_create_lesli_audit_account_logs.rb} +20 -38
- data/{lib/vue/stores/users.js → db/migrate/v1.0/0501110210_create_lesli_audit_account_devices.rb} +18 -28
- data/{lib/vue/apps/dashboards/components/roles.vue → db/migrate/v1.0/0501120110_create_lesli_audit_user_logs.rb} +28 -45
- data/db/migrate/v1.0/{0503050110_create_lesli_audit_dashboards.rb → 0501120210_create_lesli_audit_user_journals.rb} +12 -4
- data/db/migrate/v1.0/{0503110010_create_lesli_audit_user_requests.rb → 0501120310_create_lesli_audit_user_requests.rb} +4 -3
- data/db/seed/devices.rb +76 -0
- data/db/seed/users.rb +117 -0
- data/db/seeds.rb +3 -2
- data/db/version_structure.rb +53 -0
- data/lib/lesli_audit/engine.rb +1 -0
- data/lib/lesli_audit/version.rb +2 -2
- data/readme.md +78 -25
- metadata +46 -94
- data/app/controllers/lesli_audit/analytics_controller.rb +0 -26
- data/app/helpers/lesli_audit/dashboard/components_helper.rb +0 -4
- data/app/models/lesli_audit/account/request.rb +0 -4
- data/app/models/lesli_audit/analytic.rb +0 -4
- data/app/models/lesli_audit/dashboard/component.rb +0 -18
- data/app/models/lesli_audit/request.rb +0 -4
- data/app/models/lesli_audit/user.rb +0 -4
- data/app/services/lesli_audit/account/activity_services.rb +0 -69
- data/app/services/lesli_audit/analytic_service.rb +0 -130
- data/app/services/lesli_audit/analytics/trend_services.rb +0 -84
- data/app/services/lesli_audit/analytics/visitor_service.rb +0 -172
- data/app/services/lesli_audit/users/activity_services.rb +0 -61
- data/app/services/lesli_audit/users/log_services.rb +0 -62
- data/app/services/lesli_audit/users/registration_services.rb +0 -62
- data/app/services/lesli_audit/users/role_services.rb +0 -61
- data/app/services/lesli_audit/users/working_hour_services.rb +0 -78
- data/app/views/lesli_audit/accounts/_account.html.erb +0 -2
- data/app/views/lesli_audit/accounts/_form.html.erb +0 -17
- data/app/views/lesli_audit/accounts/edit.html.erb +0 -10
- data/app/views/lesli_audit/accounts/index.html.erb +0 -14
- data/app/views/lesli_audit/accounts/new.html.erb +0 -9
- data/app/views/lesli_audit/accounts/show.html.erb +0 -10
- data/app/views/lesli_audit/analytics/_analytic.html.erb +0 -2
- data/app/views/lesli_audit/analytics/_form.html.erb +0 -17
- data/app/views/lesli_audit/analytics/edit.html.erb +0 -10
- data/app/views/lesli_audit/analytics/index.html.erb +0 -42
- data/app/views/lesli_audit/analytics/new.html.erb +0 -9
- data/app/views/lesli_audit/analytics/show.html.erb +0 -10
- data/app/views/lesli_audit/dashboard/components/_component.html.erb +0 -2
- data/app/views/lesli_audit/dashboard/components/_form.html.erb +0 -17
- data/app/views/lesli_audit/dashboard/components/edit.html.erb +0 -1
- data/app/views/lesli_audit/dashboard/components/index.html.erb +0 -14
- data/app/views/lesli_audit/dashboard/components/new.html.erb +0 -9
- data/app/views/lesli_audit/dashboard/components/show.html.erb +0 -1
- data/app/views/lesli_audit/dashboards/_dashboard.html.erb +0 -2
- data/app/views/lesli_audit/dashboards/_form.html.erb +0 -17
- data/app/views/lesli_audit/requests/_form.html.erb +0 -17
- data/app/views/lesli_audit/requests/_request.html.erb +0 -2
- data/app/views/lesli_audit/requests/edit.html.erb +0 -10
- data/app/views/lesli_audit/requests/new.html.erb +0 -9
- data/app/views/lesli_audit/requests/show.html.erb +0 -10
- data/app/views/lesli_audit/users/_form.html.erb +0 -17
- data/app/views/lesli_audit/users/_user.html.erb +0 -2
- data/app/views/lesli_audit/users/edit.html.erb +0 -10
- data/app/views/lesli_audit/users/new.html.erb +0 -9
- 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/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/db/migrate/v1.0/{0503000110_create_lesli_audit_accounts.rb → 0501000110_create_lesli_audit_accounts.rb} +0 -0
- /data/db/migrate/v1.0/{0503100010_create_lesli_audit_account_requests.rb → 0501110310_create_lesli_audit_account_requests.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a84c6870b0006a5609054e1c8a311a0b9d57a5220dc8c75c7841eae7ffd1e8c
|
|
4
|
+
data.tar.gz: 882412cf089844f7c36bb58d492396c521e673374e2a13da8dcd466c23a5dc7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3d60b2ebcd66549072ace0988c2c4ca40b832c2c50173960b9db99b9c4184d4c65488197633718aacaa568b10144d592d842f667430732cc57d8665e1055fc9
|
|
7
|
+
data.tar.gz: 7f2c3f56961cb0aaf60dab530de6a7fd30eaf29a64cfafbc84623ee96c50e7162581c9728adf5894fd68f1d290946f49e1136456a913c00cf80cd8ebe4128fd4
|
|
@@ -1,72 +1 @@
|
|
|
1
|
-
|
|
2
|
-
<svg
|
|
3
|
-
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
4
|
-
xmlns:cc="http://creativecommons.org/ns#"
|
|
5
|
-
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
6
|
-
xmlns:svg="http://www.w3.org/2000/svg"
|
|
7
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
-
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
9
|
-
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
10
|
-
inkscape:version="1.0 (4035a4f, 2020-05-01)"
|
|
11
|
-
height="48.025002"
|
|
12
|
-
width="43.825001"
|
|
13
|
-
sodipodi:docname="iconfinder_186_2508414.svg"
|
|
14
|
-
xml:space="preserve"
|
|
15
|
-
viewBox="0 0 43.825001 48.025002"
|
|
16
|
-
version="1.1"
|
|
17
|
-
id="Layer_1"
|
|
18
|
-
enable-background="new 0 0 48 48"><metadata
|
|
19
|
-
id="metadata860"><rdf:RDF><cc:Work
|
|
20
|
-
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
21
|
-
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
22
|
-
id="defs858" /><sodipodi:namedview
|
|
23
|
-
inkscape:current-layer="Layer_1"
|
|
24
|
-
inkscape:window-maximized="1"
|
|
25
|
-
inkscape:window-y="23"
|
|
26
|
-
inkscape:window-x="0"
|
|
27
|
-
inkscape:cy="24"
|
|
28
|
-
inkscape:cx="18.731461"
|
|
29
|
-
inkscape:zoom="9.7781858"
|
|
30
|
-
showgrid="false"
|
|
31
|
-
id="namedview856"
|
|
32
|
-
inkscape:window-height="953"
|
|
33
|
-
inkscape:window-width="1680"
|
|
34
|
-
inkscape:pageshadow="2"
|
|
35
|
-
inkscape:pageopacity="0"
|
|
36
|
-
guidetolerance="10"
|
|
37
|
-
gridtolerance="10"
|
|
38
|
-
objecttolerance="10"
|
|
39
|
-
borderopacity="1"
|
|
40
|
-
bordercolor="#666666"
|
|
41
|
-
pagecolor="#ffffff" /><g
|
|
42
|
-
transform="translate(-2.1)"
|
|
43
|
-
id="g853"><g
|
|
44
|
-
id="g837"><path
|
|
45
|
-
id="path833"
|
|
46
|
-
fill="#e6e6e5"
|
|
47
|
-
d="M 26.4,0 H 6.5 C 4.1,0 2.1,2 2.1,4.4 v 39 c 0,2.4 2,4.4 4.4,4.4 h 30.7 c 2.4,0 4.4,-2 4.4,-4.4 V 15.3 C 36.7,10.3 30.3,3.9 26.4,0 Z" /><path
|
|
48
|
-
id="path835"
|
|
49
|
-
fill="#98d0f1"
|
|
50
|
-
d="m 26.4,0 v 10.8 c 0,2.4 2,4.4 4.4,4.4 H 41.6 C 37.6,11.3 31.2,4.9 26.4,0 Z" /></g><g
|
|
51
|
-
id="g841"><path
|
|
52
|
-
id="path839"
|
|
53
|
-
fill="#ec7b72"
|
|
54
|
-
d="m 21.2,40.7 c -0.2,0 -0.4,-0.2 -0.5,-0.4 l -5.1,-21.4 -4.1,8.4 c -0.1,0.2 -0.3,0.3 -0.4,0.3 h -8 c -0.3,0 -0.5,-0.2 -0.5,-0.5 0,-0.3 0.2,-0.5 0.5,-0.5 h 7.7 l 4.6,-9.4 c 0.1,-0.2 0.3,-0.3 0.5,-0.3 0.2,0 0.4,0.2 0.4,0.4 l 5,20.7 2.6,-11.1 c 0.1,-0.2 0.3,-0.4 0.5,-0.4 h 4.1 l 1.7,-4.4 c 0.1,-0.2 0.3,-0.3 0.5,-0.3 0.2,0 0.4,0.2 0.5,0.4 l 3.5,12 L 36,27 c 0,-0.2 0.2,-0.4 0.5,-0.4 h 4.3 c 0.3,0 0.5,0.2 0.5,0.5 0,0.3 -0.2,0.5 -0.5,0.5 h -3.9 l -1.6,8.9 c 0,0.2 -0.2,0.4 -0.5,0.4 -0.2,0 -0.4,-0.1 -0.5,-0.4 l -3.7,-12.6 -1.3,3.4 c -0.1,0.2 -0.3,0.3 -0.5,0.3 h -4 l -3,12.8 c -0.1,0.1 -0.3,0.3 -0.6,0.3 z" /></g><g
|
|
55
|
-
id="g851"><rect
|
|
56
|
-
id="rect843"
|
|
57
|
-
y="33.900002"
|
|
58
|
-
x="33.700001"
|
|
59
|
-
width="2.5"
|
|
60
|
-
transform="matrix(0.7071,-0.7071,0.7071,0.7071,-15.9478,35.568)"
|
|
61
|
-
height="6.3000002"
|
|
62
|
-
fill="#333333" /><path
|
|
63
|
-
id="path845"
|
|
64
|
-
fill="#525252"
|
|
65
|
-
d="m 18.5,20.6 c -4.2,4.2 -4.2,11 0,15.1 4.2,4.1 11,4.2 15.1,0 4.2,-4.2 4.2,-10.9 0,-15.1 -4.2,-4.2 -10.9,-4.2 -15.1,0 z m 13.2,13.2 c -3.1,3.1 -8.2,3.1 -11.3,0 -3.1,-3.1 -3.1,-8.2 0,-11.4 3.1,-3.1 8.2,-3.1 11.3,0 3.2,3.2 3.2,8.3 0,11.4 z" /><path
|
|
66
|
-
id="path847"
|
|
67
|
-
opacity="0.25"
|
|
68
|
-
fill="#ffffff"
|
|
69
|
-
d="m 30.6,23.9 c -2.4,-2.4 -6.3,-2.4 -8.7,0 0,0 0,0 0,0 l 1.7,1.7 c 0,0 0,0 0,0 2.1,-2.1 5.6,-2.1 7.7,0 0.4,0.4 0.8,0.9 1,1.4 -0.3,-1.1 -0.9,-2.2 -1.7,-3.1 z" /><path
|
|
70
|
-
id="path849"
|
|
71
|
-
fill="#ffcc67"
|
|
72
|
-
d="m 37.5,34.5 c -0.3,-0.3 -0.8,-0.3 -1.1,0 v 0 l -4,4 v 0 c -0.3,0.3 -0.3,0.8 0,1.1 0.3,0.3 0.8,0.3 1.1,0 l 7.6,7.6 c 1.1,1.1 2.9,1.1 4,0 1.1,-1.1 1.1,-2.9 0,-4 l -7.6,-7.6 c 0.3,-0.3 0.3,-0.8 0,-1.1 z" /></g></g></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 43.8 48.09" fill="#0d52bf"><path stroke="#000" stroke-miterlimit="10" stroke-width=".25" d="M36.5 12.29c-4.3-4.3-9-9.1-12.2-12.2 3.7 3.8 8.4 8.5 12.2 12.2Z"/><path d="m19.2 38.09.1-.3q-.1 0-.2-.1z"/><path stroke="#000" stroke-miterlimit="10" stroke-width=".25" d="m39.5 15.29-3-3z"/><path d="m33.3 35.89.1-.4-.2.2z"/><path d="M31.26 40.61c-.13.04-.27.05-.41.05-.41 0-.79-.16-1.08-.45s-.44-.67-.44-1.08c0-.39.15-.75.4-1.03a11.2 11.2 0 0 1-5.7 1.55c-1.31 0-2.62-.24-3.88-.68l-.35 1.61c-.1.1-.3.3-.6.3-.2 0-.4-.2-.5-.4l-.56-2.45c-.8-.47-1.57-1.04-2.26-1.72a11.17 11.17 0 0 1-3.38-7.98c-.01-2.25.62-4.4 1.82-6.25l-.72-3.01-4.1 8.4c-.1.2-.3.3-.4.3H1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h7.7l4.6-9.4c.1-.2.3-.3.5-.3s.4.2.4.4l.86 3.58c.25-.31.52-.62.81-.91 4.45-4.46 11.7-4.46 16.16 0 1.82 1.82 2.95 4.14 3.27 6.63h3.4c.3 0 .5.2.5.5s-.2.5-.5.5h-3.32c0 .15.02.3.02.45 0 2.07-.57 4.06-1.61 5.81.6-.58 1.55-.58 2.14.01.29.29.44.67.44 1.08 0 .14-.02.28-.05.41l3.18 3.18V15.39l-.1-.1H28.7c-2.4 0-4.4-2-4.4-4.4V.09H4.4C2 .09 0 2.09 0 4.49v39c0 2.4 2 4.4 4.4 4.4h30.7c.95 0 1.82-.32 2.55-.89z"/><path d="m29.718 35.787 1.768-1.768 4.454 4.455-1.767 1.768z"/><path d="M16.37 20.66c-4.2 4.2-4.2 11 0 15.1s11 4.2 15.1 0c4.2-4.2 4.2-10.9 0-15.1s-10.9-4.2-15.1 0m13.2 13.2c-3.1 3.1-8.2 3.1-11.3 0s-3.1-8.2 0-11.4c3.1-3.1 8.2-3.1 11.3 0 3.2 3.2 3.2 8.3 0 11.4"/><path d="M28.47 23.96c-2.4-2.4-6.3-2.4-8.7 0l1.7 1.7c2.1-2.1 5.6-2.1 7.7 0 .4.4.8.9 1 1.4-.3-1.1-.9-2.2-1.7-3.1" style="isolation:isolate"/><path d="M35.37 34.56c-.3-.3-.8-.3-1.1 0l-4 4c-.3.3-.3.8 0 1.1s.8.3 1.1 0l7.6 7.6c1.1 1.1 2.9 1.1 4 0s1.1-2.9 0-4l-7.6-7.6c.3-.3.3-.8 0-1.1"/></svg>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module LesliAudit
|
|
2
|
+
class LogsController < ApplicationController
|
|
3
|
+
before_action :set_log, only: %i[ show ]
|
|
4
|
+
|
|
5
|
+
# GET /logs
|
|
6
|
+
def index
|
|
7
|
+
@logs = respond_with_pagination(LesliAudit::LogService.new(current_user, query).index)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# GET /logs/1
|
|
11
|
+
def show
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
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, {})
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -35,7 +35,7 @@ module LesliAudit
|
|
|
35
35
|
|
|
36
36
|
# GET /requests
|
|
37
37
|
def index
|
|
38
|
-
@requests =
|
|
38
|
+
@requests = respond_with_pagination(LesliAudit::RequestService.new(current_user, query).index)
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
end
|
|
@@ -34,7 +34,8 @@ module LesliAudit
|
|
|
34
34
|
|
|
35
35
|
# GET /users
|
|
36
36
|
def index
|
|
37
|
-
@users =
|
|
37
|
+
@users = UserService.new(current_user, query).users
|
|
38
|
+
@working_hours = UserService.new(current_user, query).working_hours
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module LesliAudit
|
|
2
|
+
class VisitorsController < ApplicationController
|
|
3
|
+
|
|
4
|
+
# GET /analytics
|
|
5
|
+
def index
|
|
6
|
+
@visits = VisitorService.new(current_user, query).visits
|
|
7
|
+
@visitors = VisitorService.new(current_user, query).visitors
|
|
8
|
+
@requests = VisitorService.new(current_user, query).requests
|
|
9
|
+
@browsers = VisitorService.new(current_user, query).browsers
|
|
10
|
+
@devices = VisitorService.new(current_user, query).devices
|
|
11
|
+
@os = VisitorService.new(current_user, query).os
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -29,40 +29,32 @@ Building a better future, one line of code at a time.
|
|
|
29
29
|
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
30
|
// ·
|
|
31
31
|
=end
|
|
32
|
-
|
|
32
|
+
require 'device_detector'
|
|
33
33
|
module LesliAudit
|
|
34
34
|
module LoggerInterface
|
|
35
35
|
def get_user_agent(as_string=true)
|
|
36
36
|
|
|
37
|
-
http_user_agent = request.env["HTTP_USER_AGENT"]
|
|
38
|
-
|
|
39
37
|
# parse user agent
|
|
40
|
-
user_agent =
|
|
38
|
+
user_agent = DeviceDetector.new(request.env["HTTP_USER_AGENT"])
|
|
41
39
|
|
|
42
|
-
user_agent_version = user_agent.version.to_a.first(2).join(".")
|
|
40
|
+
#user_agent_version = user_agent.version.to_a.first(2).join(".")
|
|
43
41
|
|
|
44
42
|
# return user agent as object
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
version: user_agent_version
|
|
51
|
-
}
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# return user agent info as string
|
|
55
|
-
"#{user_agent.platform} #{user_agent.os} - #{user_agent.browser} #{user_agent_version}"
|
|
43
|
+
{
|
|
44
|
+
platform: user_agent.os_name,
|
|
45
|
+
browser: user_agent.name,
|
|
46
|
+
device: user_agent.device_type
|
|
47
|
+
}
|
|
56
48
|
end
|
|
57
49
|
|
|
58
50
|
def log_requests
|
|
59
51
|
log_account_requests
|
|
60
52
|
log_user_requests
|
|
53
|
+
log_devices
|
|
61
54
|
end
|
|
62
55
|
|
|
63
56
|
def log_account_requests
|
|
64
|
-
return unless Lesli.config.
|
|
65
|
-
return unless defined?(LesliAudit)
|
|
57
|
+
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
66
58
|
return unless current_user
|
|
67
59
|
|
|
68
60
|
# Try to save a unique record for this request configuration
|
|
@@ -85,11 +77,10 @@ module LesliAudit
|
|
|
85
77
|
# Track all user activity
|
|
86
78
|
# this is disabled by default in the settings file
|
|
87
79
|
def log_user_requests
|
|
88
|
-
return unless Lesli.config.security.dig(:enable_analytics)
|
|
89
|
-
return unless defined?(LesliAudit)
|
|
90
80
|
return unless current_user
|
|
91
81
|
return unless session[:user_session_id]
|
|
92
|
-
|
|
82
|
+
return unless Lesli.config.audit.dig(:enable_analytics)
|
|
83
|
+
|
|
93
84
|
# Try to save a unique record for this request configuration
|
|
94
85
|
current_user.account.audit.user_requests.upsert(
|
|
95
86
|
{
|
|
@@ -97,18 +88,55 @@ module LesliAudit
|
|
|
97
88
|
request_action: action_name,
|
|
98
89
|
session_id: session[:user_session_id],
|
|
99
90
|
user_id: current_user.id,
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
date: Date2.new.date.to_s,
|
|
92
|
+
request_count: 1
|
|
102
93
|
},
|
|
103
94
|
|
|
104
95
|
# group of columns to consider a request as unique
|
|
105
|
-
unique_by: %i[request_controller request_action
|
|
96
|
+
unique_by: %i[request_controller request_action date user_id session_id],
|
|
97
|
+
|
|
98
|
+
# if request id is not unique
|
|
99
|
+
# - increase the counter for this configuration
|
|
100
|
+
# - update the datetime of the last request
|
|
101
|
+
on_duplicate: Arel.sql(
|
|
102
|
+
"request_count = lesli_audit_user_requests.request_count + 1,updated_at = #{LesliDate::Compatibility.db_now}"
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return unless Lesli.config.audit.dig(:enable_journals)
|
|
107
|
+
current_user.account.audit.user_journals.create({
|
|
108
|
+
request_controller: controller_path,
|
|
109
|
+
request_action: action_name,
|
|
110
|
+
session_id: session[:user_session_id],
|
|
111
|
+
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],
|
|
106
134
|
|
|
107
135
|
# if request id is not unique
|
|
108
136
|
# - increase the counter for this configuration
|
|
109
137
|
# - update the datetime of the last request
|
|
110
138
|
on_duplicate: Arel.sql(
|
|
111
|
-
'
|
|
139
|
+
'agent_count = lesli_audit_account_devices.agent_count + 1'
|
|
112
140
|
)
|
|
113
141
|
)
|
|
114
142
|
end
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
module LesliAudit
|
|
2
2
|
class Account < ApplicationRecord
|
|
3
3
|
belongs_to :account, class_name: "Lesli::Account"
|
|
4
|
+
has_many :dashboards
|
|
5
|
+
|
|
6
|
+
has_many :account_logs
|
|
7
|
+
has_many :account_devices
|
|
4
8
|
has_many :account_requests
|
|
9
|
+
|
|
10
|
+
has_many :user_logs
|
|
11
|
+
has_many :user_journals
|
|
5
12
|
has_many :user_requests
|
|
6
|
-
has_many :dashboards
|
|
7
13
|
|
|
8
14
|
after_create :initialize_account
|
|
9
15
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module LesliAudit
|
|
2
|
+
class AccountLog < ApplicationRecord
|
|
3
|
+
belongs_to :account, optional: true
|
|
4
|
+
belongs_to :user, class_name: "Lesli::User", optional: true
|
|
5
|
+
|
|
6
|
+
enum :operation, {
|
|
7
|
+
:account_creation => 'account_creation',
|
|
8
|
+
:account_initialization => 'account_initialization'
|
|
9
|
+
}
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
=begin
|
|
2
2
|
|
|
3
3
|
Lesli
|
|
4
4
|
|
|
@@ -28,10 +28,16 @@ Building a better future, one line of code at a time.
|
|
|
28
28
|
|
|
29
29
|
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
30
|
// ·
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
=end
|
|
32
|
+
|
|
33
|
+
module LesliAudit
|
|
34
|
+
class LogService < Lesli::ApplicationLesliService
|
|
35
|
+
|
|
36
|
+
def index
|
|
37
|
+
current_user.logs.all
|
|
38
|
+
.order(created_at: :desc)
|
|
39
|
+
.page(query[:pagination][:page])
|
|
40
|
+
.per(query[:pagination][:perPage])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -33,6 +33,143 @@ Building a better future, one line of code at a time.
|
|
|
33
33
|
module LesliAudit
|
|
34
34
|
class UserService < Lesli::ApplicationLesliService
|
|
35
35
|
|
|
36
|
+
def users
|
|
37
|
+
users = current_user.account.users
|
|
38
|
+
|
|
39
|
+
now = Time.current
|
|
40
|
+
last_30_days = 30.days.ago
|
|
41
|
+
|
|
42
|
+
stats = users.pick(
|
|
43
|
+
# total users
|
|
44
|
+
Arel.sql("COUNT(*)"),
|
|
45
|
+
|
|
46
|
+
# total deactivated users
|
|
47
|
+
Arel.sql("SUM(CASE WHEN active = 0 AND deleted_at IS NULL THEN 1 ELSE 0 END)"),
|
|
48
|
+
|
|
49
|
+
# total locked users
|
|
50
|
+
Arel.sql(
|
|
51
|
+
"SUM(CASE WHEN (
|
|
52
|
+
locked_at IS NOT NULL
|
|
53
|
+
OR locked_until > #{ActiveRecord::Base.connection.quote(Time.current)}
|
|
54
|
+
) AND deleted_at IS NULL THEN 1 ELSE 0 END)"
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
# total not confirmed users
|
|
58
|
+
Arel.sql("SUM(CASE WHEN confirmed_at IS NULL AND deleted_at IS NULL THEN 1 ELSE 0 END)"),
|
|
59
|
+
|
|
60
|
+
# total deleted users
|
|
61
|
+
Arel.sql("SUM(CASE WHEN deleted_at IS NOT NULL THEN 1 ELSE 0 END)"),
|
|
62
|
+
|
|
63
|
+
# users that never signed in
|
|
64
|
+
Arel.sql("SUM(CASE WHEN last_sign_in_at IS NULL AND deleted_at IS NULL THEN 1 ELSE 0 END)"),
|
|
65
|
+
|
|
66
|
+
# total active users for the last 30 days
|
|
67
|
+
Arel.sql("SUM(CASE WHEN last_sign_in_at >= #{ActiveRecord::Base.connection.quote(last_30_days)} AND deleted_at IS NULL THEN 1 ELSE 0 END)"),
|
|
68
|
+
|
|
69
|
+
# users with failed logins
|
|
70
|
+
Arel.sql("SUM(CASE WHEN failed_attempts > 0 AND deleted_at IS NULL THEN 1 ELSE 0 END)"),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
summary = {
|
|
74
|
+
total_users: stats[0].to_i,
|
|
75
|
+
total_users_deactivated: stats[1].to_i,
|
|
76
|
+
total_users_locked: stats[2].to_i,
|
|
77
|
+
total_users_unconfirmed: stats[3].to_i,
|
|
78
|
+
total_users_deleted: stats[4].to_i,
|
|
79
|
+
|
|
80
|
+
total_users_never_signed_in: stats[5].to_i,
|
|
81
|
+
total_users_active_last_30_days: stats[6].to_i,
|
|
82
|
+
total_users_with_failed_attempts: stats[7].to_i
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
overview: [{
|
|
87
|
+
label: "Total users",
|
|
88
|
+
value: summary[:total_users],
|
|
89
|
+
percentage: 100,
|
|
90
|
+
color: "info"
|
|
91
|
+
}, {
|
|
92
|
+
label: "Deactivated users",
|
|
93
|
+
value: summary[:total_users_deactivated],
|
|
94
|
+
percentage: percentage(summary[:total_users_deactivated], summary[:total_users]),
|
|
95
|
+
color: "warning"
|
|
96
|
+
}, {
|
|
97
|
+
label: "Locked users",
|
|
98
|
+
value: summary[:total_users_locked],
|
|
99
|
+
percentage: percentage(summary[:total_users_locked], summary[:total_users]),
|
|
100
|
+
color: "danger"
|
|
101
|
+
}, {
|
|
102
|
+
label: "Unconfirmed users",
|
|
103
|
+
value: summary[:total_users_unconfirmed],
|
|
104
|
+
percentage: percentage(summary[:total_users_unconfirmed], summary[:total_users]),
|
|
105
|
+
color: "warning",
|
|
106
|
+
}, {
|
|
107
|
+
label: "Deleted users",
|
|
108
|
+
value: summary[:total_users_deleted],
|
|
109
|
+
percentage: percentage(summary[:total_users_deleted], summary[:total_users]),
|
|
110
|
+
color: "danger"
|
|
111
|
+
}],
|
|
112
|
+
attention_needed: [{
|
|
113
|
+
label: "Unconfirmed users",
|
|
114
|
+
value: summary[:total_users_unconfirmed],
|
|
115
|
+
color: "info",
|
|
116
|
+
icon: "user-add-line"
|
|
117
|
+
}, {
|
|
118
|
+
label: "Never signed in",
|
|
119
|
+
value: summary[:total_users_never_signed_in],
|
|
120
|
+
color: "warning",
|
|
121
|
+
icon: "time-line"
|
|
122
|
+
}, {
|
|
123
|
+
label: "Failed login attempts",
|
|
124
|
+
value: summary[:total_users_with_failed_attempts],
|
|
125
|
+
color: "danger",
|
|
126
|
+
icon: "shield-keyhole-line"
|
|
127
|
+
}, {
|
|
128
|
+
label: "Locked users",
|
|
129
|
+
value: summary[:total_users_locked],
|
|
130
|
+
color: "danger",
|
|
131
|
+
icon: "lock-line"
|
|
132
|
+
}]
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def working_hours
|
|
137
|
+
|
|
138
|
+
group = 'day'
|
|
139
|
+
|
|
140
|
+
group_by = "DATE_TRUNC('month', lesli_audit_user_requests.date)" if group == 'month'
|
|
141
|
+
group_by = "DATE_TRUNC('week', lesli_audit_user_requests.date)" if group == 'week'
|
|
142
|
+
group_by = "DATE_TRUNC('day', lesli_audit_user_requests.date)" if group == 'day'
|
|
143
|
+
|
|
144
|
+
# compatibility for SQLite
|
|
145
|
+
if ActiveRecord::Base.connection.adapter_name == "SQLite"
|
|
146
|
+
group_by = "strftime('%Y-%m-%d', lesli_audit_user_requests.date)"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
user_requests = current_user.account.users.joins(:requests)
|
|
150
|
+
|
|
151
|
+
user_requests = user_requests.select(
|
|
152
|
+
:id,
|
|
153
|
+
"lesli_users.id as user_id",
|
|
154
|
+
"lesli_users.email as user_email",
|
|
155
|
+
"#{group_by} date",
|
|
156
|
+
"min(lesli_audit_user_requests.created_at) as first_activity",
|
|
157
|
+
"max(lesli_audit_user_requests.updated_at) as last_activity",
|
|
158
|
+
"count(lesli_audit_user_requests.id) resources",
|
|
159
|
+
"sum(lesli_audit_user_requests.request_count) requests"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
user_requests = user_requests.group(:user_id, group_by)
|
|
163
|
+
|
|
164
|
+
user_requests.map do |request|
|
|
165
|
+
request[:first_activity] = Date2.new(request[:first_activity]).time
|
|
166
|
+
request[:last_activity] = Date2.new(request[:last_activity]).time
|
|
167
|
+
request
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
user_requests.reverse
|
|
171
|
+
end
|
|
172
|
+
|
|
36
173
|
def registrations
|
|
37
174
|
|
|
38
175
|
#Get filters from the request
|
|
@@ -59,13 +196,20 @@ module LesliAudit
|
|
|
59
196
|
.group(group_by)
|
|
60
197
|
.count.map do |request|
|
|
61
198
|
{
|
|
62
|
-
:
|
|
63
|
-
:
|
|
199
|
+
:xaxiskey => request[0],
|
|
200
|
+
:yaxiskey => request[1]
|
|
64
201
|
}
|
|
65
202
|
end
|
|
66
203
|
end
|
|
67
204
|
|
|
68
205
|
registrations
|
|
69
206
|
end
|
|
207
|
+
|
|
208
|
+
private
|
|
209
|
+
|
|
210
|
+
def percentage(value, total)
|
|
211
|
+
return 0 if total.to_i.zero?
|
|
212
|
+
((value.to_f / total) * 100).round
|
|
213
|
+
end
|
|
70
214
|
end
|
|
71
215
|
end
|