* file attachments in db * rubocop * factor out a bunch of file types * thumb and medium image styles" * syntax error in concern * markdown is also plaintext * rubocop
This commit is contained in:
parent
b16617286f
commit
696ff396b0
9 changed files with 185 additions and 91 deletions
38
app/models/attachment.rb
Normal file
38
app/models/attachment.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
class Attachment < ApplicationRecord
|
||||
belongs_to :attachable, polymorphic: true
|
||||
|
||||
has_attached_file :file,
|
||||
styles: lambda { |a|
|
||||
if a.instance.image?
|
||||
{
|
||||
thumb: 'x128#',
|
||||
medium: 'x320>'
|
||||
}
|
||||
else
|
||||
{}
|
||||
end
|
||||
}
|
||||
|
||||
validates_attachment_content_type :file, content_type: Attachable.allowed_types
|
||||
|
||||
def image?
|
||||
Attachable.image_types.include?(file.instance.file_content_type)
|
||||
end
|
||||
|
||||
def audio?
|
||||
Attachable.audio_types.include?(file.instance.file_content_type)
|
||||
end
|
||||
|
||||
def text?
|
||||
Attachable.text_types.include?(file.instance.file_content_type)
|
||||
end
|
||||
|
||||
def pdf?
|
||||
Attachable.pdf_types.include?(file.instance.file_content_type)
|
||||
end
|
||||
|
||||
def document?
|
||||
text? || pdf?
|
||||
end
|
||||
end
|
50
app/models/concerns/attachable.rb
Normal file
50
app/models/concerns/attachable.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
# frozen_string_literal: true
|
||||
module Attachable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :attachments, as: :attachable, dependent: :destroy
|
||||
end
|
||||
|
||||
def images
|
||||
attachments.where(file_content_type: image_types)
|
||||
end
|
||||
|
||||
def audios
|
||||
attachments.where(file_content_type: audio_types)
|
||||
end
|
||||
|
||||
def texts
|
||||
attachments.where(file_content_type: text_types)
|
||||
end
|
||||
|
||||
def pdfs
|
||||
attachments.where(file_content_type: pdf_types)
|
||||
end
|
||||
|
||||
def documents
|
||||
attachments.where(file_content_type: text_types + pdf_types)
|
||||
end
|
||||
|
||||
class << self
|
||||
def image_types
|
||||
['image/png', 'image/gif', 'image/jpeg']
|
||||
end
|
||||
|
||||
def audio_types
|
||||
['audio/ogg', 'audio/mp3']
|
||||
end
|
||||
|
||||
def text_types
|
||||
['text/plain']
|
||||
end
|
||||
|
||||
def pdf_types
|
||||
['application/pdf']
|
||||
end
|
||||
|
||||
def allowed_types
|
||||
image_types + audio_types + text_types + pdf_types
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
class Topic < ApplicationRecord
|
||||
include TopicsHelper
|
||||
include Attachable
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
|
||||
|
@ -21,23 +22,6 @@ class Topic < ApplicationRecord
|
|||
validates :permission, presence: true
|
||||
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
|
||||
|
||||
# This method associates the attribute ":image" with a file attachment
|
||||
has_attached_file :image
|
||||
|
||||
# , styles: {
|
||||
# thumb: '100x100>',
|
||||
# square: '200x200#',
|
||||
# medium: '300x300>'
|
||||
# }
|
||||
|
||||
# Validate the attached image is image/jpg, image/png, etc
|
||||
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
|
||||
|
||||
# This method associates the attribute ":image" with a file attachment
|
||||
has_attached_file :audio
|
||||
# Validate the attached audio is audio/wav, audio/mp3, etc
|
||||
validates_attachment_content_type :audio, content_type: /\Aaudio\/.*\Z/
|
||||
|
||||
def synapses
|
||||
synapses1.or(synapses2)
|
||||
end
|
||||
|
|
|
@ -29,4 +29,7 @@ Rails.application.configure do
|
|||
# Expands the lines which load the assets
|
||||
config.assets.debug = false
|
||||
config.assets.quiet = true
|
||||
|
||||
# S3 file storage
|
||||
config.paperclip_defaults = {} # store on local machine for dev
|
||||
end
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
class Rack::Attack
|
||||
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
||||
|
||||
# Throttle all requests by IP (60rpm)
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
|
||||
# throttle('req/ip', :limit => 300, :period => 5.minutes) do |req|
|
||||
# req.ip # unless req.path.start_with?('/assets')
|
||||
# end
|
||||
|
||||
# Throttle POST requests to /login by IP address
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
|
||||
throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
|
||||
req.ip if req.path == '/login' && req.post?
|
||||
end
|
||||
|
||||
# Throttle POST requests to /login by email param
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}"
|
||||
#
|
||||
# Note: This creates a problem where a malicious user could intentionally
|
||||
# throttle logins for another user and force their login requests to be
|
||||
# denied, but that's not very common and shouldn't happen to you. (Knock
|
||||
# on wood!)
|
||||
throttle('logins/email', limit: 5, period: 20.seconds) do |req|
|
||||
if req.path == '/login' && req.post?
|
||||
# return the email if present, nil otherwise
|
||||
req.params['email'].presence
|
||||
end
|
||||
end
|
||||
|
||||
throttle('load_url_title/req/5mins/ip', limit: 300, period: 5.minutes) do |req|
|
||||
req.ip if req.path == 'hacks/load_url_title'
|
||||
end
|
||||
throttle('load_url_title/req/1s/ip', limit: 5, period: 1.second) do |req|
|
||||
# If the return value is truthy, the cache key for the return value
|
||||
# is incremented and compared with the limit. In this case:
|
||||
# "rack::attack:#{Time.now.to_i/1.second}:load_url_title/req/ip:#{req.ip}"
|
||||
#
|
||||
# If falsy, the cache key is neither incremented nor checked.
|
||||
|
||||
req.ip if req.path == 'hacks/load_url_title'
|
||||
end
|
||||
|
||||
self.throttled_response = lambda do |env|
|
||||
now = Time.now
|
||||
match_data = env['rack.attack.match_data']
|
||||
period = match_data[:period]
|
||||
limit = match_data[:limit]
|
||||
|
||||
headers = {
|
||||
'X-RateLimit-Limit' => limit.to_s,
|
||||
'X-RateLimit-Remaining' => '0',
|
||||
'X-RateLimit-Reset' => (now + (period - now.to_i % period)).to_s
|
||||
}
|
||||
|
||||
[429, headers, ['']]
|
||||
end
|
||||
end
|
63
config/initializers/rack_attack.rb
Normal file
63
config/initializers/rack_attack.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
module Rack
|
||||
class Attack
|
||||
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
||||
|
||||
# Throttle all requests by IP (60rpm)
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
|
||||
# throttle('req/ip', :limit => 300, :period => 5.minutes) do |req|
|
||||
# req.ip # unless req.path.start_with?('/assets')
|
||||
# end
|
||||
|
||||
# Throttle POST requests to /login by IP address
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
|
||||
throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
|
||||
req.ip if req.path == '/login' && req.post?
|
||||
end
|
||||
|
||||
# Throttle POST requests to /login by email param
|
||||
#
|
||||
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}"
|
||||
#
|
||||
# Note: This creates a problem where a malicious user could intentionally
|
||||
# throttle logins for another user and force their login requests to be
|
||||
# denied, but that's not very common and shouldn't happen to you. (Knock
|
||||
# on wood!)
|
||||
throttle('logins/email', limit: 5, period: 20.seconds) do |req|
|
||||
if req.path == '/login' && req.post?
|
||||
# return the email if present, nil otherwise
|
||||
req.params['email'].presence
|
||||
end
|
||||
end
|
||||
|
||||
throttle('load_url_title/req/5mins/ip', limit: 300, period: 5.minutes) do |req|
|
||||
req.ip if req.path == 'hacks/load_url_title'
|
||||
end
|
||||
throttle('load_url_title/req/1s/ip', limit: 5, period: 1.second) do |req|
|
||||
# If the return value is truthy, the cache key for the return value
|
||||
# is incremented and compared with the limit. In this case:
|
||||
# "rack::attack:#{Time.now.to_i/1.second}:load_url_title/req/ip:#{req.ip}"
|
||||
#
|
||||
# If falsy, the cache key is neither incremented nor checked.
|
||||
|
||||
req.ip if req.path == 'hacks/load_url_title'
|
||||
end
|
||||
|
||||
self.throttled_response = lambda do |env|
|
||||
now = Time.zone.now
|
||||
match_data = env['rack.attack.match_data']
|
||||
period = match_data[:period]
|
||||
limit = match_data[:limit]
|
||||
|
||||
headers = {
|
||||
'X-RateLimit-Limit' => limit.to_s,
|
||||
'X-RateLimit-Remaining' => '0',
|
||||
'X-RateLimit-Reset' => (now + (period - now.to_i % period)).to_s
|
||||
}
|
||||
|
||||
[429, headers, ['']]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +1,10 @@
|
|||
Warden::Manager.after_set_user do |user,auth,opts|
|
||||
# frozen_string_literal: true
|
||||
Warden::Manager.after_set_user do |user, auth, opts|
|
||||
scope = opts[:scope]
|
||||
auth.cookies.signed["#{scope}.id"] = user.id
|
||||
auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
|
||||
end
|
||||
Warden::Manager.before_logout do |user, auth, opts|
|
||||
Warden::Manager.before_logout do |_user, auth, opts|
|
||||
scope = opts[:scope]
|
||||
auth.cookies.signed["#{scope}.id"] = nil
|
||||
auth.cookies.signed["#{scope}.expires_at"] = nil
|
||||
|
|
12
db/migrate/20170122201451_create_attachments.rb
Normal file
12
db/migrate/20170122201451_create_attachments.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class CreateAttachments < ActiveRecord::Migration[5.0]
|
||||
def change
|
||||
create_table :attachments do |t|
|
||||
t.references :attachable, polymorphic: true
|
||||
t.attachment :file
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
remove_attachment :topics, :image
|
||||
remove_attachment :topics, :audio
|
||||
end
|
||||
end
|
26
db/schema.rb
26
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20161218183817) do
|
||||
ActiveRecord::Schema.define(version: 20170122201451) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -26,6 +26,18 @@ ActiveRecord::Schema.define(version: 20161218183817) do
|
|||
t.index ["user_id"], name: "index_access_requests_on_user_id", using: :btree
|
||||
end
|
||||
|
||||
create_table "attachments", force: :cascade do |t|
|
||||
t.string "attachable_type"
|
||||
t.integer "attachable_id"
|
||||
t.string "file_file_name"
|
||||
t.string "file_content_type"
|
||||
t.integer "file_file_size"
|
||||
t.datetime "file_updated_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["attachable_type", "attachable_id"], name: "index_attachments_on_attachable_type_and_attachable_id", using: :btree
|
||||
end
|
||||
|
||||
create_table "delayed_jobs", force: :cascade do |t|
|
||||
t.integer "priority", default: 0, null: false
|
||||
t.integer "attempts", default: 0, null: false
|
||||
|
@ -269,17 +281,9 @@ ActiveRecord::Schema.define(version: 20161218183817) do
|
|||
t.text "link"
|
||||
t.integer "user_id"
|
||||
t.integer "metacode_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.text "permission"
|
||||
t.string "image_file_name", limit: 255
|
||||
t.string "image_content_type", limit: 255
|
||||
t.integer "image_file_size"
|
||||
t.datetime "image_updated_at"
|
||||
t.string "audio_file_name", limit: 255
|
||||
t.string "audio_content_type", limit: 255
|
||||
t.integer "audio_file_size"
|
||||
t.datetime "audio_updated_at"
|
||||
t.integer "defer_to_map_id"
|
||||
t.index ["metacode_id"], name: "index_topics_on_metacode_id", using: :btree
|
||||
t.index ["user_id"], name: "index_topics_on_user_id", using: :btree
|
||||
|
|
Loading…
Reference in a new issue