osa 0.1.3 → 0.2.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 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