file attachments in db (re: #124) (#1043)

* 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:
Devin Howard 2017-01-24 15:10:40 -05:00 committed by GitHub
parent b16617286f
commit 696ff396b0
9 changed files with 185 additions and 91 deletions

38
app/models/attachment.rb Normal file
View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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

View file

@ -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