osa 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0fbdd1dceb350e48a1d593ec7590e5c1caba49136ae6c6a1afb3a41a86683d8
4
- data.tar.gz: 962578a1b23e0879a5175f21a6c617812cec82e3f6577b58f401dc0cd78becc8
3
+ metadata.gz: c74cb930be462e745a7682afed8e2715e10bfb391f188d5f0f885c3b8150d435
4
+ data.tar.gz: e41a5966566a34b0f48f5eb40c9df7742fe56a8aa75299ce5db1cd0e64aeaa76
5
5
  SHA512:
6
- metadata.gz: aabca2784a303f54c43eda8ef7f1491438c65658184eebc6821574785057b2b3ced909787cd66dca29b8db38aa85fdf60959041884373eb84c6bbeb72204d99e
7
- data.tar.gz: befb3a9c9ac8ca252c5e23c84bb7fdb709be6765e092d2315472baccfda59736949d4bfcb1fa6e78b3216e09d638798c344150b18af26147c7eddd2a2307dd73
6
+ metadata.gz: 2a7fd8af344acc7f7839f4b9165cc39c913a9edca0e94155a68b231d773b918da69637a1c79dcd292654adf8248f30991c630f31b03588641ff1a9ca9e9bdc85
7
+ data.tar.gz: 9cc8eab12bacc5010f712ed0dbb5a2bbdf056c16ec97b98e1a90bcb93e9eede72af274bb91379ef3552a11022894dd2c4da0b853d843e43a9f2e6658e086e7bb
data/Gemfile.lock CHANGED
@@ -1,44 +1,52 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osa (0.1.3)
4
+ osa (0.2.0)
5
5
  activerecord (~> 6.0)
6
6
  faraday (~> 1.1)
7
7
  public_suffix (~> 4.0)
8
+ sinatra (~> 2.1.0)
9
+ sinatra-contrib (~> 2.1.0)
8
10
  sqlite3 (~> 1.4)
9
11
  tty-prompt (~> 0.22)
10
12
 
11
13
  GEM
12
14
  remote: https://rubygems.org/
13
15
  specs:
14
- activemodel (6.1.0)
15
- activesupport (= 6.1.0)
16
- activerecord (6.1.0)
17
- activemodel (= 6.1.0)
18
- activesupport (= 6.1.0)
19
- activesupport (6.1.0)
16
+ activemodel (6.1.2.1)
17
+ activesupport (= 6.1.2.1)
18
+ activerecord (6.1.2.1)
19
+ activemodel (= 6.1.2.1)
20
+ activesupport (= 6.1.2.1)
21
+ activesupport (6.1.2.1)
20
22
  concurrent-ruby (~> 1.0, >= 1.0.2)
21
23
  i18n (>= 1.6, < 2)
22
24
  minitest (>= 5.1)
23
25
  tzinfo (~> 2.0)
24
26
  zeitwerk (~> 2.3)
25
27
  ast (2.4.1)
26
- concurrent-ruby (1.1.7)
28
+ concurrent-ruby (1.1.8)
27
29
  faraday (1.3.0)
28
30
  faraday-net_http (~> 1.0)
29
31
  multipart-post (>= 1.2, < 3)
30
32
  ruby2_keywords
31
- faraday-net_http (1.0.0)
32
- i18n (1.8.6)
33
+ faraday-net_http (1.0.1)
34
+ i18n (1.8.9)
33
35
  concurrent-ruby (~> 1.0)
34
- minitest (5.14.2)
36
+ minitest (5.14.3)
37
+ multi_json (1.15.0)
35
38
  multipart-post (2.1.1)
39
+ mustermann (1.1.1)
40
+ ruby2_keywords (~> 0.0.1)
36
41
  parallel (1.20.0)
37
42
  parser (2.7.2.0)
38
43
  ast (~> 2.4.1)
39
44
  pastel (0.8.0)
40
45
  tty-color (~> 0.5)
41
46
  public_suffix (4.0.6)
47
+ rack (2.2.3)
48
+ rack-protection (2.1.0)
49
+ rack
42
50
  rainbow (3.0.0)
43
51
  regexp_parser (1.8.2)
44
52
  rexml (3.2.4)
@@ -57,8 +65,20 @@ GEM
57
65
  rubocop (>= 0.90.0, < 2.0)
58
66
  rubocop-ast (>= 0.4.0)
59
67
  ruby-progressbar (1.10.1)
60
- ruby2_keywords (0.0.2)
68
+ ruby2_keywords (0.0.4)
69
+ sinatra (2.1.0)
70
+ mustermann (~> 1.0)
71
+ rack (~> 2.2)
72
+ rack-protection (= 2.1.0)
73
+ tilt (~> 2.0)
74
+ sinatra-contrib (2.1.0)
75
+ multi_json
76
+ mustermann (~> 1.0)
77
+ rack-protection (= 2.1.0)
78
+ sinatra (= 2.1.0)
79
+ tilt (~> 2.0)
61
80
  sqlite3 (1.4.2)
81
+ tilt (2.0.10)
62
82
  tty-color (0.6.0)
63
83
  tty-cursor (0.7.1)
64
84
  tty-prompt (0.23.0)
@@ -84,4 +104,4 @@ DEPENDENCIES
84
104
  rubocop-performance
85
105
 
86
106
  BUNDLED WITH
87
- 2.1.2
107
+ 2.1.4
data/README.md CHANGED
@@ -65,6 +65,15 @@ Process your junk folder:
65
65
  osa scan-junk
66
66
  ```
67
67
 
68
+ OSA also provides you a nice administration dashboard you. You can access the dashboard by running
69
+
70
+ ```sh
71
+ osa dashboard
72
+ ```
73
+
74
+ You are now able to access the dashboard on `http://localhost:8080`. You can also change the port of the server by
75
+ providing the `SERVER_PORT` environment variable.
76
+
68
77
  ## Contributing
69
78
 
70
79
  Bug reports and pull requests are welcome on GitHub at https://github.com/moray95/osa.
data/exe/osa CHANGED
@@ -12,7 +12,10 @@ when 'login'
12
12
  OSA::AuthService.login(OSA::Config.first || OSA::Config.new)
13
13
  when 'scan-junk'
14
14
  require 'osa/scripts/scan_junk_folder'
15
+ when 'dashboard'
16
+ require 'osa/scripts/dashboard_server'
17
+ DashboardServer.start!
15
18
  else
16
- $stderr.puts "Usage: #{File.basename($0)} [setup|login|scan-junk]"
19
+ $stderr.puts "Usage: #{File.basename($0)} [setup|login|scan-junk|dashboard]"
17
20
  exit 1
18
21
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'active_record/migration'
3
+
4
+ class CreateReports < ActiveRecord::Migration[5.0]
5
+ def change
6
+ create_table :reports do |t|
7
+ t.text :sender, null: false
8
+ t.text :sender_domain, null: false
9
+ t.text :subject, null: false
10
+ t.boolean :flagged, null: false
11
+ t.boolean :blacklisted, null: false
12
+ t.datetime :received_at, null: false
13
+ t.datetime :reported_at, null: false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require 'sinatra'
3
+ require 'sinatra/json'
4
+ require 'osa/util/db'
5
+
6
+ class DashboardServer < Sinatra::Base
7
+ set :views, File.absolute_path(File.dirname(__FILE__) + '/../views')
8
+ set :port, ENV['SERVER_PORT'] || 8080
9
+
10
+ get '/' do
11
+ erb :index
12
+ end
13
+
14
+ get '/api/stats/summary' do
15
+ blacklist_count = OSA::Blacklist.count
16
+ total_reported = OSA::Report.count
17
+ today = DateTime.now.to_date
18
+ today_reported = OSA::Report.where('reported_at > ?', today).count
19
+ week = DateTime.now - 1.week
20
+ week_reported = OSA::Report.where('reported_at > ?', week).count
21
+ month = DateTime.now - 30.days
22
+ month_reported = OSA::Report.where('reported_at > ?', month).count
23
+ mean_report_time = OSA::Report.select('avg(julianday(reported_at) - julianday(received_at)) * 86400.0 as avg').first['avg']
24
+
25
+ json blacklist_count: blacklist_count,
26
+ total_reported: total_reported,
27
+ today_reported: today_reported,
28
+ week_reported: week_reported,
29
+ month_reported: month_reported,
30
+ mean_report_time: mean_report_time
31
+
32
+ end
33
+
34
+ get '/api/stats/spammers' do
35
+ spammers = OSA::Report.select('sender_domain as domain', 'COUNT(*) as count').limit(50).order(count: :desc).group(:sender_domain)
36
+ json spammers
37
+ end
38
+
39
+ get '/api/stats/reports/historical' do
40
+ historical_data = OSA::Report.select("strftime('%Y-%m-%d', reported_at) as date", 'count(*) as count').group(:date)
41
+ json historical_data
42
+ end
43
+ end
@@ -21,7 +21,7 @@ while continue
21
21
 
22
22
  flagged = mail['flag']['flagStatus'] == 'flagged'
23
23
  blacklisted = nil
24
- blacklisted = OSA::Blacklist.where(value: email_address).or(OSA::Blacklist.where(value: domain)).exists? unless flagged
24
+ blacklisted = OSA::Blacklist.where(value: email_address).or(OSA::Blacklist.where(value: domain)).exists?
25
25
 
26
26
  if flagged
27
27
  puts "Email from #{email_address} is flagged, reporting and deleting"
@@ -39,9 +39,9 @@ while continue
39
39
  puts "deleting spam from #{email_address}"
40
40
  context.graph_client.delete_mail(mail['id'])
41
41
 
42
- if flagged
43
- domain = PublicSuffix.domain(email_address.split('@', 2)[1])
42
+ domain = PublicSuffix.domain(email_address.split('@', 2)[1])
44
43
 
44
+ if flagged
45
45
  is_free_provider = OSA::EmailProvider.where(value: domain).exists?
46
46
  if is_free_provider
47
47
  puts "#{email_address} is using a free provider, blacklisting full address"
@@ -51,6 +51,14 @@ while continue
51
51
  OSA::Blacklist.find_or_create_by(value: domain).save!
52
52
  end
53
53
  end
54
+
55
+ OSA::Report.create!(sender: email_address,
56
+ sender_domain: domain,
57
+ subject: mail['subject'],
58
+ flagged: flagged,
59
+ blacklisted: blacklisted,
60
+ received_at: Time.iso8601(mail['receivedDateTime']),
61
+ reported_at: Time.now)
54
62
  end
55
63
  mails = mails.next
56
64
  end
data/lib/osa/util/db.rb CHANGED
@@ -18,4 +18,7 @@ module OSA
18
18
 
19
19
  class EmailProvider < ActiveRecord::Base
20
20
  end
21
+
22
+ class Report < ActiveRecord::Base
23
+ end
21
24
  end
data/lib/osa/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module OSA
3
- VERSION = '0.1.3'
3
+ VERSION = '0.2.0'
4
4
  end
@@ -0,0 +1,296 @@
1
+ <!-- Content Wrapper. Contains page content -->
2
+ <div class="content-wrapper" style="min-height: 1233px;">
3
+ <!-- Content Header (Page header) -->
4
+ <div class="content-header">
5
+ <div class="container-fluid">
6
+ <div class="row mb-2">
7
+ <div class="col-sm-6">
8
+ <h1 class="m-0">Overview</h1>
9
+ </div><!-- /.col -->
10
+ </div><!-- /.row -->
11
+ </div><!-- /.container-fluid -->
12
+ </div>
13
+ <!-- /.content-header -->
14
+
15
+ <!-- Main content -->
16
+ <div class="content">
17
+ <div class="container-fluid">
18
+ <div class="row">
19
+ <div class="col-lg">
20
+ <!-- small card -->
21
+ <div class="small-box bg-info">
22
+ <div class="inner">
23
+ <h3 id="blacklist-count"></h3>
24
+ <p>entries in blacklist</p>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ <div class="col-lg">
29
+ <!-- small card -->
30
+ <div class="small-box bg-info">
31
+ <div class="inner">
32
+ <h3 id="reporting-time"></h3>
33
+ <p>average reporting time</p>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <!-- ./col -->
38
+ <div class="col-lg">
39
+ <!-- small card -->
40
+ <div class="small-box bg-success">
41
+ <div class="inner">
42
+ <h3 id="today-count"></h3>
43
+ <p>emails reported today</p>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <!-- ./col -->
48
+ <div class="col-lg">
49
+ <!-- small card -->
50
+ <div class="small-box bg-success">
51
+ <div class="inner">
52
+ <h3 id="week-count"></h3>
53
+ <p>emails reported in the last 7 days</p>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ <!-- ./col -->
58
+ <div class="col-lg">
59
+ <!-- small card -->
60
+ <div class="small-box bg-success">
61
+ <div class="inner">
62
+ <h3 id="month-count"></h3>
63
+ <p>emails reported in the last 30 days</p>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ <!-- ./col -->
68
+ <div class="col-lg">
69
+ <!-- small card -->
70
+ <div class="small-box bg-warning">
71
+ <div class="inner">
72
+ <h3 id="total-count"></h3>
73
+ <p>emails reported in total</p>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ <!-- ./col -->
78
+ </div>
79
+
80
+ <div class="row">
81
+ <div class="card">
82
+ <div class="card-header">
83
+ <h3 class="card-title">Spams reported</h3>
84
+ </div>
85
+ <!-- /.card-header -->
86
+ <div class="card-body">
87
+ <div class="chart">
88
+ <div class="chartjs-size-monitor">
89
+ <div class="chartjs-size-monitor-expand">
90
+ <div class=""></div>
91
+ </div>
92
+ <div class="chartjs-size-monitor-shrink">
93
+ <div class=""></div>
94
+ </div>
95
+ </div>
96
+ <canvas id="report-history-chart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%; display: block; width: 1732px;" width="1732" height="250" class="chartjs-render-monitor"></canvas>
97
+ </div>
98
+ </div>
99
+ <!-- /.card-body -->
100
+ </div>
101
+ </div>
102
+
103
+ <div class="row">
104
+ <div class="card">
105
+ <div class="card-header">
106
+ <h3 class="card-title">Top spammers</h3>
107
+ </div>
108
+ <!-- /.card-header -->
109
+ <div class="card-body p-0">
110
+ <table class="table table-striped">
111
+ <thead>
112
+ <tr>
113
+ <th style="width: 10px">#</th>
114
+ <th>Domain</th>
115
+ <th>Emails reported</th>
116
+ <th>Spam ratio</th>
117
+ <th style="width: 40px"></th>
118
+ </tr>
119
+ </thead>
120
+ <tbody id="top-spammers-table-body">
121
+ </tbody>
122
+ </table>
123
+ </div>
124
+ <!-- /.card-body -->
125
+ </div>
126
+ </div>
127
+
128
+
129
+ </div> <!-- /.container-fluid -->
130
+
131
+
132
+ </div>
133
+ <!-- /.content -->
134
+ </div>
135
+ <!-- /.content-wrapper -->
136
+
137
+ <script>
138
+ let totalReportCount = 0;
139
+
140
+ function sec2time(timeInSeconds) {
141
+ const pad = function (num, size) {
142
+ return ('000' + num).slice(size * -1);
143
+ }
144
+ const time = parseFloat(timeInSeconds).toFixed(3);
145
+ const hours = Math.floor(time / 60 / 60);
146
+ const minutes = Math.floor(time / 60) % 60;
147
+
148
+ const units = [{value: hours, unit: 'h'}, {value: minutes, unit: 'm'}].filter((value) => value.value > 0 );
149
+
150
+ return units.map((value) => `${pad(value.value, 2)}${value.unit}`).join(' ');
151
+ }
152
+
153
+ async function updateStats() {
154
+ const response = await fetch('/api/stats/summary');
155
+ if (response.ok) {
156
+ const body = await response.json();
157
+ document.getElementById('blacklist-count').innerText = body['blacklist_count'];
158
+ document.getElementById('today-count').innerText = body['today_reported'];
159
+ document.getElementById('week-count').innerText = body['week_reported'];
160
+ document.getElementById('month-count').innerText = body['month_reported'];
161
+ document.getElementById('total-count').innerText = body['total_reported'];
162
+ const meanReportTime = body['mean_report_time'];
163
+ if (meanReportTime !== null) {
164
+ document.getElementById('reporting-time').innerText = sec2time(meanReportTime);
165
+ } else {
166
+ document.getElementById('reporting-time').innerText = 'N/A';
167
+ }
168
+ totalReportCount = body['total_reported'];
169
+ }
170
+ }
171
+
172
+ function createProgressDiv(fillRatio) {
173
+ const progressDiv = document.createElement("div");
174
+ progressDiv.className = "progress progress-xs";
175
+
176
+ const progressBarDiv = document.createElement("div");
177
+ progressBarDiv.className = "progress-bar progress-bar-danger";
178
+ progressBarDiv.style.width = `${fillRatio}%`;
179
+
180
+ progressDiv.appendChild(progressBarDiv);
181
+ return progressDiv;
182
+ }
183
+
184
+ async function updateSpammers() {
185
+ const response = await fetch('/api/stats/spammers');
186
+ if (response.ok) {
187
+ const body = await response.json();
188
+
189
+ const table = body.map((spammer, index) => {
190
+ const tr = document.createElement("tr");
191
+
192
+ const indexTd = document.createElement("td");
193
+ indexTd.innerText = index + 1;
194
+ tr.appendChild(indexTd);
195
+
196
+ const domainTd = document.createElement("td");
197
+ domainTd.innerText = spammer['domain'];
198
+ tr.appendChild(domainTd);
199
+
200
+ const reportCountTd = document.createElement("td");
201
+ reportCountTd.innerText = spammer['count'];
202
+ tr.appendChild(reportCountTd);
203
+
204
+ const spamRatio = spammer['count'] / totalReportCount * 100;
205
+
206
+ const spamRatioProgressTd = document.createElement("td");
207
+ spamRatioProgressTd.append(createProgressDiv(spamRatio));
208
+ tr.appendChild(spamRatioProgressTd);
209
+
210
+ const spamRatioTd = document.createElement("td");
211
+ spamRatioTd.innerText = `${spamRatio}%`;
212
+ tr.appendChild(spamRatioTd);
213
+
214
+ return tr;
215
+ });
216
+ const tableBody = document.getElementById("top-spammers-table-body")
217
+ tableBody.innerHTML = "";
218
+ table.forEach((row) => {
219
+ tableBody.appendChild(row);
220
+ });
221
+
222
+ }
223
+ }
224
+
225
+ function createHistoricalChart() {
226
+ const options = {
227
+ maintainAspectRatio: false,
228
+ responsive: true,
229
+ legend: {
230
+ display: false
231
+ },
232
+ scales: {
233
+ xAxes: [{
234
+ gridLines: {
235
+ display: false,
236
+ }
237
+ }],
238
+ yAxes: [{
239
+ gridLines: {
240
+ display: false,
241
+ }
242
+ }]
243
+ }
244
+ }
245
+
246
+ const chartData = {
247
+ labels: [],
248
+ datasets: [
249
+ {
250
+ label: 'Spams reported',
251
+ backgroundColor: 'rgba(60,141,188,0.9)',
252
+ borderColor: 'rgba(60,141,188,0.8)',
253
+ pointColor: '#3b8bba',
254
+ pointStrokeColor: 'rgba(60,141,188,1)',
255
+ pointHighlightFill: '#fff',
256
+ pointHighlightStroke: 'rgba(60,141,188,1)',
257
+ data: []
258
+ }
259
+ ]
260
+ }
261
+ const ctx = document.getElementById('report-history-chart').getContext('2d');
262
+
263
+ return new Chart(ctx, {
264
+ type: 'line',
265
+ options: options,
266
+ data: chartData
267
+ });
268
+ }
269
+
270
+ const historicalChart = createHistoricalChart();
271
+
272
+ async function updateHistoricalChart() {
273
+ const response = await fetch("/api/stats/reports/historical");
274
+ if (response.ok) {
275
+ const body = await response.json();
276
+
277
+ const labels = body.map((data) => data['date']);
278
+ const values = body.map((data) => data['count']);
279
+
280
+ historicalChart.data.labels = labels
281
+ historicalChart.data.datasets[0].data = values
282
+ historicalChart.update();
283
+ }
284
+ }
285
+
286
+ function update() {
287
+ updateStats()
288
+ .then(updateSpammers)
289
+ .then(updateHistoricalChart)
290
+ .then(() => {
291
+ setTimeout(update, 10 * 60 * 1000);
292
+ })
293
+ }
294
+
295
+ update();
296
+ </script>
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Outlook Spam Automator Admin Panel</title>
6
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
7
+ <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js" integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/admin-lte@3.0/dist/js/adminlte.min.js" crossorigin="anonymous"></script>
10
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.0/dist/css/adminlte.min.css">
11
+ <body class="sidebar-mini" style="height: auto;">
12
+ <div class="wrapper">
13
+ <!-- Main Sidebar Container -->
14
+ <aside class="main-sidebar sidebar-dark-primary elevation-4">
15
+ <!-- Brand Logo -->
16
+ <a href="index.html" class="brand-link">
17
+ <span class="brand-text font-weight-light">Outlook Spam Automator</span>
18
+ </a>
19
+
20
+ <!-- Sidebar -->
21
+ <div class="sidebar">
22
+
23
+ <!-- Sidebar Menu -->
24
+ <nav class="mt-2">
25
+ <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
26
+ <li class="nav-item">
27
+ <a href="#" class="nav-link">
28
+ <i class="nav-icon fas fa-th"></i>
29
+ <p>Overview</p>
30
+ </a>
31
+ </li>
32
+ </ul>
33
+ </nav>
34
+ <!-- /.sidebar-menu -->
35
+ </div>
36
+ <!-- /.sidebar -->
37
+ </aside>
38
+
39
+ <%= yield %>
40
+
41
+ <!-- Main Footer -->
42
+ <footer class="main-footer">
43
+ <!-- To the right -->
44
+ <div class="float-right d-none d-sm-inline">
45
+ Anything you want
46
+ </div>
47
+ <!-- Default to the left -->
48
+ <strong>Copyright © 2014-2020 <a href="https://adminlte.io">AdminLTE.io</a>.</strong> All rights reserved.
49
+ </footer>
50
+ <div id="sidebar-overlay"></div></div>
51
+ <!-- ./wrapper -->
52
+
53
+ </body>
54
+
55
+ </html>
data/osa.gemspec CHANGED
@@ -26,4 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'public_suffix', '~> 4.0'
27
27
  spec.add_dependency 'sqlite3', '~> 1.4'
28
28
  spec.add_dependency 'tty-prompt', '~> 0.22'
29
+ spec.add_dependency 'sinatra', '~> 2.1.0'
30
+ spec.add_dependency 'sinatra-contrib', '~> 2.1.0'
29
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moray Baruh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-02 00:00:00.000000000 Z
11
+ date: 2021-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.22'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sinatra
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.1.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.1.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: sinatra-contrib
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 2.1.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.1.0
83
111
  description: Get rid of spam on your Outlook account
84
112
  email:
85
113
  - contact@moraybaruh.com
@@ -108,7 +136,9 @@ files:
108
136
  - lib/osa/migrations/00001_create_blacklists.rb
109
137
  - lib/osa/migrations/00002_create_email_providers.rb
110
138
  - lib/osa/migrations/00003_create_config.rb
139
+ - lib/osa/migrations/00004_create_reports.rb
111
140
  - lib/osa/migrations/free-email-providers.txt
141
+ - lib/osa/scripts/dashboard_server.rb
112
142
  - lib/osa/scripts/scan_junk_folder.rb
113
143
  - lib/osa/services/auth_service.rb
114
144
  - lib/osa/services/setup_service.rb
@@ -117,6 +147,8 @@ files:
117
147
  - lib/osa/util/db.rb
118
148
  - lib/osa/util/paginated.rb
119
149
  - lib/osa/version.rb
150
+ - lib/osa/views/index.erb
151
+ - lib/osa/views/layout.erb
120
152
  - osa.gemspec
121
153
  - release.sh
122
154
  - web/login.html
@@ -139,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
171
  - !ruby/object:Gem::Version
140
172
  version: '0'
141
173
  requirements: []
142
- rubygems_version: 3.1.2
174
+ rubygems_version: 3.2.3
143
175
  signing_key:
144
176
  specification_version: 4
145
177
  summary: Outlook Spam Automator