the_audit 0.0.1 → 0.0.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 +7 -0
- data/.gitignore +1 -0
- data/README.md +172 -6
- data/app/assets/javascripts/the_audit.js.coffee +23 -0
- data/app/controllers/_templates_/audits_controller.rb +3 -0
- data/app/controllers/concerns/controller.rb +48 -0
- data/app/helpers/the_audit_helper.rb +26 -0
- data/app/models/_templates_/audit.rb +3 -0
- data/app/models/concerns/base.rb +132 -0
- data/app/views/audits/_form.html.haml +79 -0
- data/app/views/audits/edit.html.haml +6 -0
- data/app/views/audits/index.html.haml +119 -0
- data/app/views/audits/show.html.haml +73 -0
- data/config/locales/en.yml +7 -0
- data/config/locales/ru.yml +7 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20130130163149_create_audits.rb +4 -1
- data/gem_version.rb +3 -0
- data/lib/generators/the_audit/USAGE +29 -0
- data/lib/generators/the_audit/the_audit_generator.rb +38 -0
- data/lib/tasks/bots.rake +24 -0
- data/lib/the_audit.rb +20 -2
- data/lib/the_audit/version.rb +1 -3
- data/pic.png +0 -0
- data/the_audit.gemspec +3 -0
- metadata +55 -13
- data/app/models/audit.rb +0 -24
- data/lib/the_audit/engine.rb +0 -10
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2675cbddec68c11daf30a5a64d9b50fce3929b5d
|
4
|
+
data.tar.gz: bed7b04919327366a9da14728f5ead1cf627f630
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c01d9037b95c209176380f43dc9ac56e50f193f379153975e310774db8ae828aef22c5c1c3a7030b40eaa53a97f68fadd534b0cc14157ade3b6a77ecab469008
|
7
|
+
data.tar.gz: bc48eda5bf8ef0ee65efb2fefb76e82333ecd20ee329c445016e44c6cc3899ac26c2bc0bcec429ab5c69a3bb04c26d2b4941f927a259ce0dd45c81f956f55fdf
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,174 @@
|
|
1
|
-
# TheAudit
|
1
|
+
# TheAudit - collect user's request info
|
2
2
|
|
3
|
-
|
4
|
-
#
|
3
|
+
### Простой, расширяемый
|
4
|
+
[#TODO clarify this sentence]
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
TheAudit - делает очень простую вещь - логгирует все базовые данные входящих запросов от пользователя и сохраняет эти данные в БД приложения. Предполагается использовать эти данные для мониторинга входящих запросов в приложение и в каждый отдельный контроллер/действие.
|
7
|
+
|
8
|
+
Work with Rails 4. This gem collects the following fields:
|
9
|
+
User, Obj, Controller/Action, IP,
|
10
|
+
Fullpath, Referer, User agent,
|
11
|
+
Remote addr, Data.
|
12
|
+
[#TODO] clarify this fields and check in Rails 3 version
|
13
|
+
|
14
|
+
Администратор может просматривать все записи аудита, а так-же редактировать и удалять их. По ссылкам можно переходить на конкретные страницы просмотренные пользователем.
|
15
|
+
|
16
|
+
## GUI
|
17
|
+
<table>
|
18
|
+
<tr>
|
19
|
+
<td>TheAudit management web interface => localhost:3000/admin/audits</td>
|
20
|
+
</tr>
|
21
|
+
<tr>
|
22
|
+
<td><img src="https://github.com/the-teacher/the_audit/raw/master/pic.png" alt="TheAudit"></td>
|
23
|
+
</tr>
|
24
|
+
</table>
|
25
|
+
|
26
|
+
## Install
|
27
|
+
**Gemfile**
|
28
|
+
```ruby
|
29
|
+
gem "the_audit"
|
30
|
+
|
31
|
+
# You can use any Bootstrap 3 version (CSS, LESS, SCSS)
|
32
|
+
gem 'bootstrap-sass', github: 'thomas-mcdonald/bootstrap-sass'
|
33
|
+
```
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
bundle
|
37
|
+
```
|
38
|
+
**Generators**
|
39
|
+
|
40
|
+
Show generator note
|
41
|
+
```
|
42
|
+
rails g the_audit --help
|
43
|
+
```
|
44
|
+
|
45
|
+
Run main generator
|
46
|
+
```
|
47
|
+
rails g the_audit install
|
48
|
+
```
|
49
|
+
|
50
|
+
This will create:
|
51
|
+
<pre>
|
52
|
+
app/controllers/admin/audits_controller.rb
|
53
|
+
app/models/audit.rb
|
54
|
+
</pre>
|
55
|
+
|
56
|
+
install TheAudit migration
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
rake the_audit_engine:install:migrations
|
60
|
+
```
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
rake db:migrate
|
64
|
+
```
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
Generated classes:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class Admin::AuditsController < ApplicationController
|
72
|
+
include TheAudit::Controller
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
class Audit < ActiveRecord::Base
|
78
|
+
include TheAudit::Base
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class CreateAudits < ActiveRecord::Migration
|
84
|
+
def change
|
85
|
+
create_table :audits do |t|
|
86
|
+
t.integer :user_id
|
87
|
+
|
88
|
+
t.string :obj_id
|
89
|
+
t.string :obj_type
|
90
|
+
|
91
|
+
t.string :controller_name
|
92
|
+
t.string :action_name
|
93
|
+
|
94
|
+
t.string :ip
|
95
|
+
t.string :remote_ip
|
96
|
+
t.string :fullpath
|
97
|
+
t.string :referer
|
98
|
+
t.string :user_agent
|
99
|
+
t.string :remote_addr
|
100
|
+
t.string :remote_host
|
101
|
+
|
102
|
+
t.text :data
|
103
|
+
|
104
|
+
# add_index :the_audits, :referer
|
105
|
+
# add_index :the_audits, :user_agent
|
106
|
+
# add_index :the_audits, [:controller_name, :action_name]
|
107
|
+
|
108
|
+
t.timestamps
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
## Integration
|
115
|
+
|
116
|
+
#### Change controllers
|
117
|
+
|
118
|
+
Add to
|
119
|
+
|
120
|
+
**ApplicationController**
|
121
|
+
```ruby
|
122
|
+
class ApplicationController < ActionController::Base
|
123
|
+
after_action :save_audit
|
124
|
+
|
125
|
+
private
|
126
|
+
def save_audit
|
127
|
+
(@audit || Audit.new.init(self)).save unless controller_name == 'audits'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
**any audited controllers**
|
133
|
+
```ruby
|
134
|
+
class UsersController < ApplicationController
|
135
|
+
...
|
136
|
+
before_action :set_audit, only: %w[create show update edit destroy]
|
137
|
+
|
138
|
+
def set_audit
|
139
|
+
@audit = Audit.new.init(self, @user)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
#### GUI
|
145
|
+
|
146
|
+
Assets and Bootstrap
|
147
|
+
|
148
|
+
**application.css**
|
149
|
+
|
150
|
+
```
|
151
|
+
//= require bootstrap
|
152
|
+
```
|
153
|
+
|
154
|
+
**application.js**
|
155
|
+
|
156
|
+
```
|
157
|
+
//= require jquery
|
158
|
+
//= require jquery_ujs
|
159
|
+
|
160
|
+
//= require bootstrap
|
161
|
+
```
|
162
|
+
|
163
|
+
Change your layout
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
= yield :the_audit_main
|
167
|
+
```
|
168
|
+
|
169
|
+
## Use
|
170
|
+
|
171
|
+
http://localhost:3000/admin/audits
|
172
|
+
|
173
|
+
## Locales
|
174
|
+
en, ru
|
@@ -0,0 +1,23 @@
|
|
1
|
+
@TheAudit = do ->
|
2
|
+
init: ->
|
3
|
+
do @init_controller_action_select
|
4
|
+
do @init_datapickers
|
5
|
+
|
6
|
+
init_datapickers: ->
|
7
|
+
now = new Date
|
8
|
+
plus_12 = new Date now.getFullYear(), (now.getMonth()+12), now.getDate()
|
9
|
+
minus_12 = new Date now.getFullYear(), (now.getMonth()-12), now.getDate()
|
10
|
+
|
11
|
+
$("[data-role='date_start'], [data-role='date_end']").datepicker
|
12
|
+
minDate: minus_12,
|
13
|
+
maxDate: plus_12
|
14
|
+
dateFormat: 'yy-mm-dd'
|
15
|
+
|
16
|
+
init_controller_action_select: ->
|
17
|
+
$('@ctrl_acts select').on 'change', (e) ->
|
18
|
+
select = $ e.target
|
19
|
+
value = select.val()
|
20
|
+
|
21
|
+
path = '?'
|
22
|
+
path = "?controller_action=#{ $(e.target).val() }" if value.length
|
23
|
+
location.href path
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TheAudit
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_action :set_audit, only: %w[ show edit update destroy ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def index
|
10
|
+
@ctrl_acts = Audit
|
11
|
+
.audit_scope(params)
|
12
|
+
.select('DISTINCT controller_name, action_name, COUNT(*) as count')
|
13
|
+
.group('controller_name, action_name')
|
14
|
+
.reorder('count DESC').to_a
|
15
|
+
|
16
|
+
@audits_count = Audit.audit_scope(params).count
|
17
|
+
@audits = Audit.audit_scope(params).pagination(params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def show; end
|
21
|
+
|
22
|
+
def edit; end
|
23
|
+
|
24
|
+
def update
|
25
|
+
if @audit.update(audit_params)
|
26
|
+
redirect_to audit_path(@audit), notice: 'Audit was successfully updated.'
|
27
|
+
else
|
28
|
+
render action: 'edit'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def destroy
|
33
|
+
@audit.destroy
|
34
|
+
redirect_to audits_url
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
# Use callbacks to share common setup or constraints between actions.
|
39
|
+
def set_audit
|
40
|
+
@audit = Audit.find(params[:id])
|
41
|
+
end
|
42
|
+
|
43
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
44
|
+
def audit_params
|
45
|
+
params.require(:audit).permit(:user_id, :obj_id, :obj_type, :controller_name, :action_name, :ip, :remote_ip, :fullpath, :referer, :user_agent, :remote_addr, :remote_host, :data)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TheAuditHelper
|
2
|
+
def bot_marker agent
|
3
|
+
return nil unless TheAudit.is_bot?(agent)
|
4
|
+
|
5
|
+
name = case agent
|
6
|
+
when /yandex/mix
|
7
|
+
:Yandex
|
8
|
+
when /google/mix
|
9
|
+
:Google
|
10
|
+
when /ahrefs/mix
|
11
|
+
:Arefs
|
12
|
+
when /exabot/mix
|
13
|
+
:Exa
|
14
|
+
when /interfax/mix
|
15
|
+
:Interfax
|
16
|
+
when /bing/mix
|
17
|
+
:Bing
|
18
|
+
when /riddler/mix
|
19
|
+
:Riddler
|
20
|
+
else
|
21
|
+
:Bot
|
22
|
+
end
|
23
|
+
|
24
|
+
content_tag :span, name, class: 'btn btn-warning btn-xs'
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module TheAudit
|
2
|
+
module Base
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include ThePagination::Concern
|
7
|
+
|
8
|
+
belongs_to :user
|
9
|
+
belongs_to :obj, polymorphic: true
|
10
|
+
|
11
|
+
include BaseSorts
|
12
|
+
|
13
|
+
scope :audit_scope, ->(params){
|
14
|
+
by_ip(params)
|
15
|
+
.by_holder(params)
|
16
|
+
.by_referer(params)
|
17
|
+
.by_user_id(params)
|
18
|
+
.by_date_range(params)
|
19
|
+
.by_user_agent(params)
|
20
|
+
.by_controller_action(params)
|
21
|
+
.simple_sort(params)
|
22
|
+
}
|
23
|
+
|
24
|
+
scope :by_user_id, ->(params){
|
25
|
+
uid = params[:user_id]
|
26
|
+
return nil if uid.blank?
|
27
|
+
where(user_id: uid)
|
28
|
+
}
|
29
|
+
|
30
|
+
scope :by_referer, ->(params){
|
31
|
+
referer = params[:referer]
|
32
|
+
return nil if referer.blank?
|
33
|
+
where(referer: referer)
|
34
|
+
}
|
35
|
+
|
36
|
+
scope :by_ip, ->(params){
|
37
|
+
ip = params[:ip]
|
38
|
+
return nil if ip.blank?
|
39
|
+
where(ip: ip)
|
40
|
+
}
|
41
|
+
|
42
|
+
scope :by_user_agent, ->(params){
|
43
|
+
ua = params[:user_agent]
|
44
|
+
return nil if ua.blank?
|
45
|
+
where(user_agent: ua)
|
46
|
+
}
|
47
|
+
|
48
|
+
scope :by_holder, ->(params){
|
49
|
+
return nil if params[:obj_id].blank? && params[:obj_type].blank?
|
50
|
+
_type, id = params[:obj_type], params[:obj_id]
|
51
|
+
|
52
|
+
return where(obj_type: _type) if id.blank?
|
53
|
+
return where(obj_id: id) if _type.blank?
|
54
|
+
|
55
|
+
where(obj_type: _type).where(obj_id: id)
|
56
|
+
}
|
57
|
+
|
58
|
+
scope :by_date_range, ->(params){
|
59
|
+
from, to = params[:date_start], params[:date_end]
|
60
|
+
return nil if from.blank? && to.blank?
|
61
|
+
return where("audits.created_at > ?", from.to_datetime) if to.blank?
|
62
|
+
return where("audits.created_at < ?", to.to_datetime) if from.blank?
|
63
|
+
return where("audits.created_at > ? AND audits.created_at < ?", from.to_datetime, to.to_datetime)
|
64
|
+
}
|
65
|
+
|
66
|
+
scope :by_controller_action, ->(params){
|
67
|
+
ca = params[:controller_action]
|
68
|
+
return nil if ca.blank?
|
69
|
+
|
70
|
+
ctrl, act = ca.split '-'
|
71
|
+
return nil if act.blank?
|
72
|
+
|
73
|
+
where(controller_name: ctrl).where(action_name: act)
|
74
|
+
}
|
75
|
+
|
76
|
+
scope :with_users, ->{ includes(:user) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def user_name
|
80
|
+
user.try(:email) || user.try(:login) || user.try(:username)
|
81
|
+
end
|
82
|
+
|
83
|
+
def user_path context
|
84
|
+
"/users/#{ user.to_param }"
|
85
|
+
end
|
86
|
+
|
87
|
+
def user_id_builder controller
|
88
|
+
controller.try(:current_user).try(:id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def unescape str
|
92
|
+
return nil if str.blank?
|
93
|
+
res_str = CGI::unescape str
|
94
|
+
|
95
|
+
# try to catch:
|
96
|
+
# invalid byte sequence in UTF-8
|
97
|
+
# for search requests form old IE6-8 with cp-1251
|
98
|
+
begin
|
99
|
+
res_str =~ //
|
100
|
+
rescue ArgumentError
|
101
|
+
res_str = CGI.unescape(str).force_encoding('windows-1251').encode
|
102
|
+
end
|
103
|
+
|
104
|
+
res_str
|
105
|
+
end
|
106
|
+
|
107
|
+
def init controller, object = nil, data = {}
|
108
|
+
self.user_id = self.user_id_builder(controller)
|
109
|
+
|
110
|
+
self.obj = object
|
111
|
+
self.action_name = controller.action_name
|
112
|
+
self.controller_name = controller.controller_name
|
113
|
+
|
114
|
+
self.data = data.to_json unless data.blank?
|
115
|
+
|
116
|
+
if r = controller.request
|
117
|
+
self.ip = r.ip
|
118
|
+
self.user_agent = r.user_agent
|
119
|
+
self.bot = TheAudit.is_bot?(r.user_agent)
|
120
|
+
|
121
|
+
self.remote_ip = r.remote_ip
|
122
|
+
self.remote_addr = r.remote_addr
|
123
|
+
self.remote_host = r.remote_host
|
124
|
+
|
125
|
+
self.fullpath = self.unescape(r.fullpath) || ''
|
126
|
+
self.referer = self.unescape(r.referer) || :direct_visit
|
127
|
+
end
|
128
|
+
|
129
|
+
self
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
= form_for([:admin, @audit]) do |f|
|
2
|
+
-if @audit.errors.any?
|
3
|
+
#error_explanation
|
4
|
+
%h2= "#{pluralize(@audit.errors.count, "error")} prohibited this audit from being saved:"
|
5
|
+
%ul
|
6
|
+
- @audit.errors.full_messages.each do |msg|
|
7
|
+
.alert.alert-danger
|
8
|
+
%li= msg
|
9
|
+
%table.table.table-striped.table-hover
|
10
|
+
%tr
|
11
|
+
%td
|
12
|
+
/ #TODO Place the button to better place
|
13
|
+
= f.submit t('the_audit.save'), class: 'btn btn-info'
|
14
|
+
%td
|
15
|
+
%tr
|
16
|
+
%td
|
17
|
+
=f.label :user_id
|
18
|
+
%td
|
19
|
+
=f.label "#{@audit.user_id.nil? ? 'Guest' : @audit.user_id}", class: 'form-control'
|
20
|
+
%tr
|
21
|
+
%td
|
22
|
+
=f.label :obj_id
|
23
|
+
%td
|
24
|
+
=f.text_field :obj_id, class: 'form-control'
|
25
|
+
%tr
|
26
|
+
%td
|
27
|
+
=f.label :obj_type
|
28
|
+
%td
|
29
|
+
=f.text_field :obj_type, class: 'form-control'
|
30
|
+
%tr
|
31
|
+
%td
|
32
|
+
=f.label :controller_name
|
33
|
+
%td
|
34
|
+
=f.text_field :controller_name, class: 'form-control'
|
35
|
+
%tr
|
36
|
+
%td
|
37
|
+
=f.label :action_name
|
38
|
+
%td
|
39
|
+
=f.text_field :action_name, class: 'form-control'
|
40
|
+
%tr
|
41
|
+
%td
|
42
|
+
=f.label :ip, 'IP'
|
43
|
+
%td
|
44
|
+
=f.text_field :ip, class: 'form-control'
|
45
|
+
%tr
|
46
|
+
%td
|
47
|
+
=f.label :remote_ip, 'Remote IP'
|
48
|
+
%td
|
49
|
+
=f.text_field :remote_ip, class: 'form-control'
|
50
|
+
%tr
|
51
|
+
%td
|
52
|
+
=f.label :fullpath
|
53
|
+
%td
|
54
|
+
=f.text_field :fullpath, class: 'form-control'
|
55
|
+
%tr
|
56
|
+
%td
|
57
|
+
=f.label :referer
|
58
|
+
%td
|
59
|
+
=f.text_field :referer, class: 'form-control'
|
60
|
+
%tr
|
61
|
+
%td
|
62
|
+
=f.label :user_agent
|
63
|
+
%td
|
64
|
+
=f.text_field :user_agent, class: 'form-control'
|
65
|
+
%tr
|
66
|
+
%td
|
67
|
+
=f.label :remote_addr
|
68
|
+
%td
|
69
|
+
=f.text_field :remote_addr, class: 'form-control'
|
70
|
+
%tr
|
71
|
+
%td
|
72
|
+
=f.label :remote_host
|
73
|
+
%td
|
74
|
+
=f.text_field :remote_host, class: 'form-control'
|
75
|
+
%tr
|
76
|
+
%td
|
77
|
+
=f.label :data
|
78
|
+
%td
|
79
|
+
=f.text_area :data, class: 'form-control'
|
@@ -0,0 +1,119 @@
|
|
1
|
+
- content_for :the_audit_main do
|
2
|
+
%h1= t('the_audit.list_title')
|
3
|
+
|
4
|
+
.alert.alert-warning
|
5
|
+
%p Данная страница позволяет наблюдать за заходами на различные страницы сайта.
|
6
|
+
%p Здесь можно проследить IP адрес посетителя, название его браузера и некоторую другую информацию.
|
7
|
+
|
8
|
+
.alert.alert-info
|
9
|
+
%p Основная задача сбора этой информации - отслеживание заходов Поисковых ботов на конкретную страницу.
|
10
|
+
|
11
|
+
%p
|
12
|
+
Записей: #{ @audits_count }
|
13
|
+
|
14
|
+
= paginate @audits
|
15
|
+
|
16
|
+
.panel.panel-success
|
17
|
+
.panel-heading
|
18
|
+
Фильтры
|
19
|
+
= form_tag '', method: :get, role: :form do
|
20
|
+
.panel-body
|
21
|
+
.form-group
|
22
|
+
.row
|
23
|
+
.col-xs-4
|
24
|
+
%label{ for: :user_id }
|
25
|
+
user_id
|
26
|
+
= link_to "X", params.except(:user_id)
|
27
|
+
= text_field_tag :user_id, params[:user_id], placeholder: "user_id", class: 'form-control'
|
28
|
+
.col-xs-4
|
29
|
+
%label{ for: :ip }
|
30
|
+
IP адрес
|
31
|
+
= link_to "X", params.except(:ip)
|
32
|
+
= text_field_tag :ip, params[:ip], placeholder: "IP адрес", class: 'form-control'
|
33
|
+
.col-xs-4
|
34
|
+
%label{ for: :user_agent }
|
35
|
+
User Agent
|
36
|
+
= link_to "X", params.except(:user_agent)
|
37
|
+
= text_field_tag :user_agent, params[:user_agent], placeholder: "User agent", class: 'form-control'
|
38
|
+
.form-group
|
39
|
+
.row
|
40
|
+
.col-xs-4
|
41
|
+
%label{ for: :obj_type }
|
42
|
+
obj_type
|
43
|
+
= link_to "X", params.except(:obj_type)
|
44
|
+
= text_field_tag :obj_type, params[:obj_type], placeholder: "obj_type", class: 'form-control'
|
45
|
+
.col-xs-4
|
46
|
+
%label{ for: :obj_id }
|
47
|
+
obj_id
|
48
|
+
= link_to "X", params.except(:obj_id)
|
49
|
+
= text_field_tag :obj_id, params[:obj_id], placeholder: "obj_id", class: 'form-control'
|
50
|
+
.col-xs-4
|
51
|
+
%label{ for: :referer }
|
52
|
+
referer
|
53
|
+
= link_to "X", params.except(:referer)
|
54
|
+
= text_field_tag :referer, params[:referer], placeholder: "referer", class: 'form-control'
|
55
|
+
.form-group
|
56
|
+
.row
|
57
|
+
.col-xs-3
|
58
|
+
%label{ for: :date_start }
|
59
|
+
Дата (от)
|
60
|
+
= link_to "X", params.except(:date_start)
|
61
|
+
= text_field_tag :date_start, params[:date_start], data: { role: :date_start }, placeholder: "Дата (от)", class: 'form-control'
|
62
|
+
.col-xs-3
|
63
|
+
%label{ for: :date_end }
|
64
|
+
Дата (до)
|
65
|
+
= link_to "X", params.except(:date_end)
|
66
|
+
= text_field_tag :date_end, params[:date_end], data: { role: :date_end }, placeholder: "Дата (до)", class: 'form-control'
|
67
|
+
.panel-footer
|
68
|
+
= submit_tag "Выбор", name: nil, class: 'btn btn-primary'
|
69
|
+
= link_to "Сбросить фильтры", params.except(:user_id, :date_start, :date_end, :sort_column, :sort_type, :ip, :fullpath, :referer, :user_agent, :referer, :controller_action, :created_at, :obj_id), class: 'btn btn-default'
|
70
|
+
|
71
|
+
%table.table.table-striped.table-hover
|
72
|
+
%thead
|
73
|
+
%tr
|
74
|
+
%th User
|
75
|
+
%th Obj
|
76
|
+
%th@ctrl_acts
|
77
|
+
- opts = options_for_select @ctrl_acts.collect{|opt| base_name = [ opt.controller_name, opt.action_name ].join(':'); [ "#{ base_name } (#{ opt.count })", base_name ] }, selected: params[:controller_action]
|
78
|
+
= select_tag :controller_action, opts, prompt: "Controller/Action", class: 'form-control'
|
79
|
+
%th= link_to 'Fullpath↕', simple_sort_url(:fullpath, params), title: :fullpath
|
80
|
+
|
81
|
+
%th= link_to 'IP↕', simple_sort_url(:ip, params), title: :description
|
82
|
+
%th= link_to 'Referer↕', simple_sort_url(:referer, params), title: :referer
|
83
|
+
%th= link_to 'User agent↕', simple_sort_url(:user_agent, params), title: :user_agent
|
84
|
+
%th= link_to 'created_at↕', simple_sort_url(:created_at, params), title: :created_at
|
85
|
+
%th Action
|
86
|
+
|
87
|
+
%tbody
|
88
|
+
- @audits.each do |audit|
|
89
|
+
%tr
|
90
|
+
%td
|
91
|
+
- if audit.user
|
92
|
+
= link_to audit.user_name, params.merge(user_id: audit.user_id)
|
93
|
+
/ = link_to audit.user_name, audit.user_path(self)
|
94
|
+
- else
|
95
|
+
Guest
|
96
|
+
|
97
|
+
%td
|
98
|
+
- if audit.obj_type.present?
|
99
|
+
- l_1 = link_to audit.obj_type, params.merge(obj_type: audit.obj_type)
|
100
|
+
- l_2 = link_to audit.obj_id, params.merge(obj_id: audit.obj_id)
|
101
|
+
- l_3 = link_to '[all]', params.merge(obj_type: audit.obj_type, obj_id: audit.obj_id)
|
102
|
+
#{ l_1 }:#{ l_2 } #{ l_3 }
|
103
|
+
|
104
|
+
%td= [ audit.controller_name, audit.action_name ].join ':'
|
105
|
+
%td{ title: audit.fullpath }= audit.fullpath.to_s[0..30]
|
106
|
+
|
107
|
+
%td= link_to audit.ip, params.merge(ip: audit.ip)
|
108
|
+
|
109
|
+
%td{ title: audit.referer }
|
110
|
+
= link_to audit.referer.to_s[0..30], params.merge(referer: audit.referer)
|
111
|
+
|
112
|
+
%td{ title: audit.user_agent }
|
113
|
+
- agent = bot_marker(audit.user_agent) || audit.user_agent.to_s[0..30]
|
114
|
+
= link_to agent, params.merge(user_agent: audit.user_agent)
|
115
|
+
|
116
|
+
%td= audit.created_at.to_s(:db)
|
117
|
+
%td= link_to('Show', audit_path(audit))
|
118
|
+
|
119
|
+
= paginate @audits
|
@@ -0,0 +1,73 @@
|
|
1
|
+
%h1= t("the_audit.show_record")
|
2
|
+
|
3
|
+
- content_for :the_audit_main do
|
4
|
+
-# #TODO set bootstrap style for notice
|
5
|
+
%p#notice= notice
|
6
|
+
|
7
|
+
%table.table.table-striped
|
8
|
+
%tbody
|
9
|
+
%tr
|
10
|
+
%td
|
11
|
+
%strong User:
|
12
|
+
%td
|
13
|
+
-# #TODO show Guest unless User present
|
14
|
+
= @audit.user_id
|
15
|
+
%tr
|
16
|
+
%td
|
17
|
+
%strong Obj:
|
18
|
+
%td
|
19
|
+
= @audit.obj_id
|
20
|
+
%tr
|
21
|
+
%td
|
22
|
+
%strong Obj type:
|
23
|
+
%td
|
24
|
+
= @audit.obj_type
|
25
|
+
%tr
|
26
|
+
%td
|
27
|
+
%strong Controller name:
|
28
|
+
%td
|
29
|
+
= @audit.controller_name
|
30
|
+
%tr
|
31
|
+
%td
|
32
|
+
%strong Action name:
|
33
|
+
%td
|
34
|
+
= @audit.action_name
|
35
|
+
%tr
|
36
|
+
%td
|
37
|
+
%strong IP:
|
38
|
+
%td
|
39
|
+
= @audit.ip
|
40
|
+
%tr
|
41
|
+
%td
|
42
|
+
%strong Remote IP:
|
43
|
+
%td
|
44
|
+
= @audit.remote_ip
|
45
|
+
%tr
|
46
|
+
%td
|
47
|
+
%strong Fullpath:
|
48
|
+
%td
|
49
|
+
= @audit.fullpath
|
50
|
+
%tr
|
51
|
+
%td
|
52
|
+
%strong Referer:
|
53
|
+
%td
|
54
|
+
= @audit.referer
|
55
|
+
%tr
|
56
|
+
%td
|
57
|
+
%strong User agent:
|
58
|
+
%td
|
59
|
+
= @audit.user_agent
|
60
|
+
%tr
|
61
|
+
%td
|
62
|
+
%strong Remote addr:
|
63
|
+
%td
|
64
|
+
= @audit.remote_addr
|
65
|
+
%tr
|
66
|
+
%td
|
67
|
+
%strong Data:
|
68
|
+
%td
|
69
|
+
= @audit.data
|
70
|
+
/#TODO maybe show buttons insted links
|
71
|
+
= link_to t('the_audit.edit'), edit_audit_path(@audit)
|
72
|
+
|
|
73
|
+
= link_to t('the_audit.list'), audits_path
|
data/config/routes.rb
ADDED
data/gem_version.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Description:
|
2
|
+
This generators helps to install TheAudit gem into your Application
|
3
|
+
|
4
|
+
Usage: [bundle exec] rails g the_audit NAME
|
5
|
+
|
6
|
+
# This text:
|
7
|
+
> rails g the_audit --help
|
8
|
+
|
9
|
+
# Main generator:
|
10
|
+
> rails g the_audit install
|
11
|
+
|
12
|
+
This will create:
|
13
|
+
app/controllers/admin/audits_controller.rb
|
14
|
+
app/models/audit.rb
|
15
|
+
|
16
|
+
# Controller generators:
|
17
|
+
> rails g the_audit controller
|
18
|
+
|
19
|
+
This will create:
|
20
|
+
app/controllers/admin/audits_controller.rb
|
21
|
+
|
22
|
+
# Model generators:
|
23
|
+
> rails g the_audit model
|
24
|
+
|
25
|
+
This will create:
|
26
|
+
app/models/audit.rb
|
27
|
+
|
28
|
+
# Migrations:
|
29
|
+
> rake the_audit_engine:install:migrations
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class TheAuditGenerator < Rails::Generators::NamedBase
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
# argument :xname, type: :string, default: :xname
|
4
|
+
|
5
|
+
# > rails g the_comments NAME
|
6
|
+
def generate_controllers
|
7
|
+
case gen_name
|
8
|
+
when 'model'
|
9
|
+
cp_model
|
10
|
+
when 'controller'
|
11
|
+
cp_controller
|
12
|
+
when 'install'
|
13
|
+
cp_model
|
14
|
+
cp_controller
|
15
|
+
else
|
16
|
+
puts 'TheAudit Generator - wrong Name'
|
17
|
+
puts 'Try to use [ install | controller | model ]'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def root_path; TheAudit::Engine.root; end
|
24
|
+
|
25
|
+
def gen_name
|
26
|
+
name.to_s.downcase
|
27
|
+
end
|
28
|
+
|
29
|
+
def cp_model
|
30
|
+
copy_file "#{root_path}/app/models/_templates_/audit.rb",
|
31
|
+
"app/models/audit.rb"
|
32
|
+
end
|
33
|
+
|
34
|
+
def cp_controller
|
35
|
+
copy_file "#{root_path}/app/controllers/_templates_/audits_controller.rb",
|
36
|
+
"app/controllers/admin/audits_controller.rb"
|
37
|
+
end
|
38
|
+
end
|
data/lib/tasks/bots.rake
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# rake the_audit:bots:recalc
|
2
|
+
namespace :the_audit do
|
3
|
+
namespace :bots do
|
4
|
+
# rake bots:recalc
|
5
|
+
desc "Recalculate Audit Bot flags"
|
6
|
+
|
7
|
+
task recalc: :environment do
|
8
|
+
acount = Audit.count
|
9
|
+
bsize = 1000
|
10
|
+
Audit.find_in_batches(batch_size: bsize).with_index do |group, index|
|
11
|
+
group.each do |audit|
|
12
|
+
bot_flag = TheAudit.is_bot?(audit.user_agent)
|
13
|
+
|
14
|
+
if audit.bot != bot_flag
|
15
|
+
audit.update_column(:bot, bot_flag)
|
16
|
+
p "Updated: #{ audit.id }/#{ audit.user_agent }"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
p "#{ bsize * index.next }/#{ acount }"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/the_audit.rb
CHANGED
@@ -1,4 +1,22 @@
|
|
1
|
-
require "the_audit/engine"
|
2
1
|
require "the_audit/version"
|
3
2
|
|
4
|
-
module TheAudit
|
3
|
+
module TheAudit
|
4
|
+
def self.is_bot? user_agent
|
5
|
+
!!user_agent.to_s.match(/bot|riddler|crawler|spider|slurp|fetcher/mix)
|
6
|
+
end
|
7
|
+
|
8
|
+
class Engine < Rails::Engine; end
|
9
|
+
# initializer "Assets precompile hook", :group => :all do |app|
|
10
|
+
# app.config.assets.precompile += %w( file.js file.css )
|
11
|
+
# end
|
12
|
+
end
|
13
|
+
|
14
|
+
_root_ = File.expand_path('../../', __FILE__)
|
15
|
+
|
16
|
+
# Loading of concerns
|
17
|
+
require "#{_root_}/app/controllers/concerns/controller.rb"
|
18
|
+
require "#{_root_}/config/routes.rb"
|
19
|
+
|
20
|
+
%w[ base ].each do |concern|
|
21
|
+
require "#{_root_}/app/models/concerns/#{concern}.rb"
|
22
|
+
end
|
data/lib/the_audit/version.rb
CHANGED
data/pic.png
ADDED
Binary file
|
data/the_audit.gemspec
CHANGED
@@ -16,4 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'haml'
|
21
|
+
gem.add_dependency 'the_pagination'
|
19
22
|
end
|
metadata
CHANGED
@@ -1,16 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: the_audit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ilya N. Zykin
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
11
|
+
date: 2014-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: haml
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: the_pagination
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
14
41
|
description: Collect basic request info in Rails app
|
15
42
|
email:
|
16
43
|
- zykin-ilya@ya.ru
|
@@ -18,39 +45,54 @@ executables: []
|
|
18
45
|
extensions: []
|
19
46
|
extra_rdoc_files: []
|
20
47
|
files:
|
21
|
-
- .gitignore
|
48
|
+
- ".gitignore"
|
22
49
|
- Gemfile
|
23
50
|
- LICENSE.txt
|
24
51
|
- README.md
|
25
52
|
- Rakefile
|
26
|
-
- app/
|
53
|
+
- app/assets/javascripts/the_audit.js.coffee
|
54
|
+
- app/controllers/_templates_/audits_controller.rb
|
55
|
+
- app/controllers/concerns/controller.rb
|
56
|
+
- app/helpers/the_audit_helper.rb
|
57
|
+
- app/models/_templates_/audit.rb
|
58
|
+
- app/models/concerns/base.rb
|
59
|
+
- app/views/audits/_form.html.haml
|
60
|
+
- app/views/audits/edit.html.haml
|
61
|
+
- app/views/audits/index.html.haml
|
62
|
+
- app/views/audits/show.html.haml
|
63
|
+
- config/locales/en.yml
|
64
|
+
- config/locales/ru.yml
|
65
|
+
- config/routes.rb
|
27
66
|
- db/migrate/20130130163149_create_audits.rb
|
67
|
+
- gem_version.rb
|
68
|
+
- lib/generators/the_audit/USAGE
|
69
|
+
- lib/generators/the_audit/the_audit_generator.rb
|
70
|
+
- lib/tasks/bots.rake
|
28
71
|
- lib/the_audit.rb
|
29
|
-
- lib/the_audit/engine.rb
|
30
72
|
- lib/the_audit/version.rb
|
73
|
+
- pic.png
|
31
74
|
- the_audit.gemspec
|
32
75
|
homepage: https://github.com/the-teacher
|
33
76
|
licenses: []
|
77
|
+
metadata: {}
|
34
78
|
post_install_message:
|
35
79
|
rdoc_options: []
|
36
80
|
require_paths:
|
37
81
|
- lib
|
38
82
|
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
83
|
requirements:
|
41
|
-
- -
|
84
|
+
- - ">="
|
42
85
|
- !ruby/object:Gem::Version
|
43
86
|
version: '0'
|
44
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
-
none: false
|
46
88
|
requirements:
|
47
|
-
- -
|
89
|
+
- - ">="
|
48
90
|
- !ruby/object:Gem::Version
|
49
91
|
version: '0'
|
50
92
|
requirements: []
|
51
93
|
rubyforge_project:
|
52
|
-
rubygems_version:
|
94
|
+
rubygems_version: 2.2.2
|
53
95
|
signing_key:
|
54
|
-
specification_version:
|
96
|
+
specification_version: 4
|
55
97
|
summary: Collect basic request info in Rails app
|
56
98
|
test_files: []
|
data/app/models/audit.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
class Audit < ActiveRecord::Base
|
2
|
-
belongs_to :user
|
3
|
-
belongs_to :obj, polymorphic: true
|
4
|
-
|
5
|
-
def init controller, object = nil, data = {}
|
6
|
-
self.obj = object
|
7
|
-
self.action_name = controller.action_name
|
8
|
-
self.controller_name = controller.controller_name
|
9
|
-
|
10
|
-
self.data = data.to_json unless data.blank?
|
11
|
-
|
12
|
-
if r = controller.request
|
13
|
-
self.ip = r.ip
|
14
|
-
self.user_agent = r.user_agent
|
15
|
-
self.remote_ip = r.remote_ip
|
16
|
-
self.remote_addr = r.remote_addr
|
17
|
-
self.remote_host = r.remote_host
|
18
|
-
self.fullpath = CGI::unescape(r.fullpath || '')
|
19
|
-
self.referer = CGI::unescape(r.referer || 'direct_visit')
|
20
|
-
end
|
21
|
-
|
22
|
-
self
|
23
|
-
end
|
24
|
-
end
|
data/lib/the_audit/engine.rb
DELETED