-
Notifications
You must be signed in to change notification settings - Fork 457
Expand file tree
/
Copy pathpush.rb
More file actions
242 lines (193 loc) 路 6.45 KB
/
Copy pathpush.rb
File metadata and controls
242 lines (193 loc) 路 6.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# frozen_string_literal: true
require "addressable/uri"
class Push < ApplicationRecord
include Pwpush::NotifyEmailsTo
enum :kind, [:text, :file, :url, :qr], validate: true
validate :check_enabled_push_kinds, on: :create
validates :url_token, presence: true, uniqueness: true
validate :check_payload_for_text, if: :text?
validate :check_files_for_file, if: :file?
validate :check_payload_for_url, if: :url?
validate :check_payload_for_qr, if: :qr?
with_options on: :create do |create|
create.before_validation :set_expire_limits
create.before_validation :set_url_token
create.before_validation :set_default_attributes
end
belongs_to :user, optional: true
has_encrypted :payload, :note, :passphrase
has_many :audit_logs, -> { order(created_at: :asc) }, dependent: :destroy
has_many_attached :files, dependent: :destroy
def to_param
url_token.to_s
end
def days_old
(Time.zone.now.to_datetime - created_at.to_datetime).to_i
end
def days_remaining
[(expire_after_days - days_old), 0].max
end
def views_remaining
[(expire_after_views - view_count), 0].max
end
# Minutes until the day-based expiry (days_remaining 脳 24 脳 60). Used by format_time_remaining.
def minutes_remaining
days_remaining * 24 * 60
end
def view_count
audit_logs.where(kind: %i[view failed_view]).size
end
def successful_views
audit_logs.where(kind: :view).order(:created_at)
end
def failed_views
audit_logs.where(kind: :failed_view).order(:created_at)
end
# Expire this push, delete the content and save the record
def expire
# Delete content
self.payload = nil
self.passphrase = nil
files.purge
# Mark as expired
self.expired = true
self.expired_on = Time.current.utc
save
end
# Override to_json so that we can add in <days_remaining>, <views_remaining>
# and show the clear push
def to_json(*args)
# def to_json(owner: false, payload: false)
attr_hash = attributes
owner = false
payload = false
owner = args.first[:owner] if args.first.key?(:owner)
payload = args.first[:payload] if args.first.key?(:payload)
attr_hash["days_remaining"] = days_remaining
attr_hash["views_remaining"] = views_remaining
attr_hash["deleted"] = audit_logs.any?(&:expire?)
if file?
file_list = {}
files.each do |file|
# FIXME: default host?
file_list[file.filename] = Rails.application.routes.url_helpers.rails_blob_url(file, only_path: true)
end
attr_hash["files"] = file_list.to_json
end
# Remove unnecessary fields
attr_hash.delete("kind")
attr_hash.delete("payload_ciphertext")
attr_hash.delete("note_ciphertext")
attr_hash.delete("passphrase_ciphertext")
attr_hash.delete("user_id")
attr_hash.delete("id")
attr_hash.delete("passphrase")
attr_hash.delete("name") unless owner
attr_hash.delete("note") unless owner
attr_hash.delete("payload") unless payload
attr_hash.delete("deletable_by_viewer") if url?
Oj.dump attr_hash
end
def check_files_for_file
if files.attached? && files.count { |file| !(file.is_a?(String) && file.empty?) } > settings_for_kind.max_file_uploads
errors.add(:files, I18n._("You can only attach up to %{count} files per push.") % {count: settings_for_kind.max_file_uploads})
end
end
def check_payload_for_text
# Allow nil payload when expired
return if expired?
if payload.blank?
errors.add(:payload, I18n._("Payload is required."))
return
end
unless payload.is_a?(String) && payload.length.between?(1, 1.megabyte)
errors.add(:payload, I18n._("The payload is too large. You can only push up to %{count} bytes.") % {count: 1.megabyte})
end
end
def check_payload_for_url
# Allow nil payload when expired
return if expired?
if payload.present?
if !valid_url?(payload)
errors.add(:payload, I18n._("must be a valid HTTP or HTTPS URL."))
end
else
errors.add(:payload, I18n._("Payload is required."))
end
end
def check_payload_for_qr
# Allow nil payload when expired
return if expired?
if payload.present?
# If the push is a QR code, max payload length is 1024 characters
if payload.length > 1024
errors.add(:payload, I18n._("The QR code payload is too large. You can only push up to %{count} bytes.") % {count: 1024})
end
else
errors.add(:payload, I18n._("Payload is required."))
end
end
def set_expire_limits
self.expire_after_days ||= settings_for_kind.expire_after_days_default
self.expire_after_views ||= settings_for_kind.expire_after_views_default
# MIGRATE - ask
# Are these assignments needed?
unless self.expire_after_days.between?(settings_for_kind.expire_after_days_min, settings_for_kind.expire_after_days_max)
self.expire_after_days = settings_for_kind.expire_after_days_default
end
unless self.expire_after_views.between?(settings_for_kind.expire_after_views_min, settings_for_kind.expire_after_views_max)
self.expire_after_views = settings_for_kind.expire_after_views_default
end
end
def check_limits
expire if !expired? && (!days_remaining.positive? || !views_remaining.positive?)
end
def set_url_token
self.url_token = SecureRandom.urlsafe_base64(rand(8..14)).downcase
end
def expire!
# Delete content
self.payload = nil
self.passphrase = nil
files.purge
# Mark as expired
self.expired = true
self.expired_on = Time.current.utc
save!
end
def settings_for_kind
if text?
Settings.pw
elsif url?
Settings.url
elsif file?
Settings.files
elsif qr?
Settings.qr
end
end
def check_enabled_push_kinds
if kind == "file" && !(Settings.enable_logins && Settings.enable_file_pushes)
errors.add(:kind, I18n._("File pushes are disabled."))
end
if kind == "url" && !(Settings.enable_logins && Settings.enable_url_pushes)
errors.add(:kind, I18n._("URL pushes are disabled."))
end
if kind == "qr" && !(Settings.enable_logins && Settings.enable_qr_pushes)
errors.add(:kind, I18n._("QR code pushes are disabled."))
end
end
def set_default_attributes
self.note ||= ""
self.passphrase ||= ""
self.name ||= ""
end
def valid_url?(url)
!Addressable::URI.parse(url).scheme.nil?
rescue Addressable::URI::InvalidURIError
false
end
def deleted
audit_logs.where(kind: AuditLog.kinds[:expire]).exists?
end
end