diff --git a/.example-env b/.example-env index c2c9a2e9..51d89c5d 100644 --- a/.example-env +++ b/.example-env @@ -14,6 +14,7 @@ export SECRET_KEY_BASE='267c8a84f63963282f45bc3010eaddf027abfab58fc759d6e239c800 # # you can safely leave these blank, unless you're deploying an instance, in # # which case you'll need to set them up # +# export S3_REGION # export S3_BUCKET_NAME # export AWS_ACCESS_KEY_ID # export AWS_SECRET_ACCESS_KEY diff --git a/Gemfile b/Gemfile index d5b42d83..7f34c12e 100644 --- a/Gemfile +++ b/Gemfile @@ -5,44 +5,34 @@ ruby '2.3.0' gem 'rails', '~> 5.0.0' gem 'active_model_serializers' -gem 'aws-sdk', '< 2.0' +gem 'aws-sdk' gem 'best_in_place' gem 'delayed_job' gem 'delayed_job_active_record' gem 'devise' -gem 'doorkeeper', '~> 4.0.0.rc4' +gem 'doorkeeper' gem 'dotenv-rails' gem 'exception_notification' -gem 'formtastic' -gem 'formula' gem 'httparty' gem 'json' gem 'kaminari' -gem 'paperclip', '~> 4.3.6' +gem 'paperclip' gem 'pg' gem 'pundit' gem 'pundit_extra' -gem 'rack-cors' gem 'rack-attack' +gem 'rack-cors' gem 'redis' gem 'slack-notifier' gem 'snorlax' gem 'uservoice-ruby' +# asset stuff +gem 'coffee-rails' gem 'jquery-rails' gem 'jquery-ui-rails' -gem 'jbuilder' -gem 'rails3-jquery-autocomplete' - -group :assets do - gem 'coffee-rails' - gem 'sass-rails' - gem 'uglifier' -end - -group :production do - gem 'rails_12factor' -end +gem 'sass-rails' +gem 'uglifier' group :test do gem 'factory_girl_rails' diff --git a/Gemfile.lock b/Gemfile.lock index 23d4c827..350585ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,11 +46,12 @@ GEM addressable (2.3.8) arel (7.1.2) ast (2.3.0) - aws-sdk (1.66.0) - aws-sdk-v1 (= 1.66.0) - aws-sdk-v1 (1.66.0) - json (~> 1.4) - nokogiri (>= 1.4.4) + aws-sdk (2.6.3) + aws-sdk-resources (= 2.6.3) + aws-sdk-core (2.6.3) + jmespath (~> 1.0) + aws-sdk-resources (2.6.3) + aws-sdk-core (= 2.6.3) bcrypt (3.1.11) best_in_place (3.1.0) actionpack (>= 3.2) @@ -91,7 +92,7 @@ GEM warden (~> 1.2.3) diff-lcs (1.2.5) docile (1.1.5) - doorkeeper (4.0.0) + doorkeeper (4.2.0) railties (>= 4.2) dotenv (2.1.1) dotenv-rails (2.1.1) @@ -108,18 +109,12 @@ GEM factory_girl_rails (4.7.0) factory_girl (~> 4.7.0) railties (>= 3.0.0) - formtastic (3.1.4) - actionpack (>= 3.2.13) - formula (1.1.1) - rails (> 3.0.0) globalid (0.3.7) activesupport (>= 4.1.0) httparty (0.14.0) multi_xml (>= 0.5.2) i18n (0.7.0) - jbuilder (2.6.0) - activesupport (>= 3.0.0, < 5.1) - multi_json (~> 1.2) + jmespath (1.3.1) jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -142,10 +137,9 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) - mimemagic (0.3.0) + mimemagic (0.3.2) mini_portile2 (2.1.0) - minitest (5.9.0) - multi_json (1.12.1) + minitest (5.9.1) multi_xml (0.5.5) nio4r (1.2.1) nokogiri (1.6.8) @@ -153,12 +147,12 @@ GEM pkg-config (~> 1.1.7) oauth (0.5.1) orm_adapter (0.5.0) - paperclip (4.3.7) - activemodel (>= 3.2.0) - activesupport (>= 3.2.0) + paperclip (5.1.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) cocaine (~> 0.5.5) mime-types - mimemagic (= 0.3.0) + mimemagic (~> 0.3.0) parser (2.3.1.4) ast (~> 2.2) pg (0.19.0) @@ -199,13 +193,6 @@ GEM nokogiri (~> 1.6.0) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - rails3-jquery-autocomplete (1.0.15) - rails (>= 3.2) - rails_12factor (0.0.3) - rails_serve_static_assets - rails_stdout_logging - rails_serve_static_assets (0.0.5) - rails_stdout_logging (0.0.5) railties (5.0.0.1) actionpack (= 5.0.0.1) activesupport (= 5.0.0.1) @@ -290,7 +277,7 @@ PLATFORMS DEPENDENCIES active_model_serializers - aws-sdk (< 2.0) + aws-sdk best_in_place better_errors binding_of_caller @@ -299,20 +286,17 @@ DEPENDENCIES delayed_job delayed_job_active_record devise - doorkeeper (~> 4.0.0.rc4) + doorkeeper dotenv-rails exception_notification factory_girl_rails - formtastic - formula httparty - jbuilder jquery-rails jquery-ui-rails json json-schema kaminari - paperclip (~> 4.3.6) + paperclip pg pry-byebug pry-rails @@ -321,8 +305,6 @@ DEPENDENCIES rack-attack rack-cors rails (~> 5.0.0) - rails3-jquery-autocomplete - rails_12factor redis rspec-rails rubocop @@ -339,4 +321,4 @@ RUBY VERSION ruby 2.3.0p0 BUNDLED WITH - 1.12.5 + 1.13.2 diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index 8d4c6e27..cdbbd900 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -1,13 +1,10 @@ # frozen_string_literal: true class MapsController < ApplicationController - before_action :require_user, only: [:create, :update, :destroy, :access, :events, - :screenshot] + before_action :require_user, only: [:create, :update, :destroy, :access, :events] before_action :set_map, only: [:show, :update, :destroy, :access, :contains, - :events, :export, :screenshot] + :events, :export] after_action :verify_authorized - autocomplete :map, :name, full: true, extra_data: [:user_id] - # GET maps/:id def show respond_to do |format| @@ -136,17 +133,6 @@ class MapsController < ApplicationController end end - # POST maps/:id/upload_screenshot - def screenshot - @map.base64_screenshot(params[:encoded_image]) - - if @map.save - render json: { message: 'Successfully uploaded the map screenshot.' } - else - render json: { message: 'Failed to upload image.' } - end - end - private def set_map @@ -159,7 +145,7 @@ class MapsController < ApplicationController end def update_map_params - params.require(:map).permit(:id, :name, :arranged, :desc, :permission) + params.require(:map).permit(:id, :name, :arranged, :desc, :permission, :screenshot) end def create_topics! diff --git a/app/models/map.rb b/app/models/map.rb index f9fe6312..609b1be4 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -95,21 +95,6 @@ class Map < ApplicationRecord json end - def decode_base64(imgBase64) - decoded_data = Base64.decode64(imgBase64) - - data = StringIO.new(decoded_data) - data.class_eval do - attr_accessor :content_type, :original_filename - end - - data.content_type = 'image/png' - data.original_filename = File.basename('map-' + id.to_s + '-screenshot.png') - - self.screenshot = data - save - end - # user param helps determine what records are visible def contains(user) { @@ -144,14 +129,4 @@ class Map < ApplicationRecord end removed.compact end - - def base64_screenshot(encoded_image) - png = Base64.decode64(encoded_image['data:image/png;base64,'.length..-1]) - StringIO.open(png) do |data| - data.class.class_eval { attr_accessor :original_filename, :content_type } - data.original_filename = 'map-' + @map.id.to_s + '-screenshot.png' - data.content_type = 'image/png' - @map.screenshot = data - end - end end diff --git a/app/policies/map_policy.rb b/app/policies/map_policy.rb index 84d24ca4..9999a055 100644 --- a/app/policies/map_policy.rb +++ b/app/policies/map_policy.rb @@ -60,8 +60,4 @@ class MapPolicy < ApplicationPolicy def unstar? user.present? end - - def screenshot? - update? - end end diff --git a/config/application.rb b/config/application.rb index 96505b32..9d8870a9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -43,5 +43,17 @@ module Metamaps # pundit errors return 403 FORBIDDEN config.action_dispatch.rescue_responses['Pundit::NotAuthorizedError'] = :forbidden + + # S3 file storage + config.paperclip_defaults = { + storage: :s3, + s3_protocol: 'https', + s3_region: ENV['S3_REGION'], + s3_credentials: { + bucket: ENV['S3_BUCKET_NAME'], + access_key_id: ENV['AWS_ACCESS_KEY_ID'], + secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] + } + } end end diff --git a/config/environments/development.rb b/config/environments/development.rb index dd6095b2..5449e5e8 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,17 +14,6 @@ Rails.application.configure do config.consider_all_requests_local = true config.action_controller.perform_caching = false - # S3 file storage - config.paperclip_defaults = { - storage: :s3, - s3_credentials: { - bucket: ENV['S3_BUCKET_NAME'], - access_key_id: ENV['AWS_ACCESS_KEY_ID'], - secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] - }, - s3_protocol: 'https' - } - config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: ENV['SMTP_SERVER'], diff --git a/config/environments/production.rb b/config/environments/production.rb index f9c94af6..d3f8794e 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -5,6 +5,11 @@ Rails.application.configure do config.log_level = :warn config.eager_load = true + # 12 factor: log to stdout + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + # Code is not reloaded between requests config.cache_classes = true @@ -13,24 +18,13 @@ Rails.application.configure do config.action_controller.perform_caching = true # Disable Rails's static asset server (Apache or nginx will already do this) - config.public_file_server.enabled = false + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = false config.assets.js_compressor = :uglifier - # S3 file storage - config.paperclip_defaults = { - storage: :s3, - s3_credentials: { - bucket: ENV['S3_BUCKET_NAME'], - access_key_id: ENV['AWS_ACCESS_KEY_ID'], - secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] - }, - s3_protocol: 'https' - } - config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: ENV['SMTP_SERVER'], diff --git a/config/routes.rb b/config/routes.rb index 13b2a5ba..41dd40c4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,7 +19,6 @@ Metamaps::Application.routes.draw do get :export post 'events/:event', action: :events get :contains - post :upload_screenshot, action: :screenshot post :access, default: { format: :json } post :star, to: 'stars#create', defaults: { format: :json } post :unstar, to: 'stars#destroy', defaults: { format: :json } diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 24ea08ea..387311c2 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -1,5 +1,7 @@ /* global Metamaps, $ */ +import outdent from 'outdent' + import Active from '../Active' import AutoLayout from '../AutoLayout' import Create from '../Create' @@ -323,9 +325,7 @@ const Map = { node.visited = !T }) - var imageData = { - encoded_image: canvas.canvas.toDataURL() - } + var imageData = canvas.canvas.toDataURL() var map = Active.Map @@ -341,24 +341,31 @@ const Map = { } today = mm + '/' + dd + '/' + yyyy - var mapName = map.get('name').split(' ').join([separator = '-']) - var downloadMessage = '' - downloadMessage += 'Captured map screenshot! ' - downloadMessage += "DOWNLOAD" + var mapName = map.get('name').split(' ').join(['-']) + const filename = `metamap-${map.id}-${mapName}-${today}.png` + + var downloadMessage = outdent` + Captured map screenshot! + DOWNLOAD` GlobalUI.notifyUser(downloadMessage) - $.ajax({ - type: 'POST', - dataType: 'json', - url: '/maps/' + Active.Map.id + '/upload_screenshot', - data: imageData, - success: function (data) { - console.log('successfully uploaded map screenshot') - }, - error: function () { - console.log('failed to save map screenshot') - } + canvas.canvas.toBlob(imageBlob => { + const formData = new window.FormData(); + formData.append('map[screenshot]', imageBlob, filename) + $.ajax({ + type: 'PATCH', + dataType: 'json', + url: `/maps/${map.id}`, + data: formData, + processData: false, + contentType: false, + success: function (data) { + console.log('successfully uploaded map screenshot') + }, + error: function () { + console.log('failed to save map screenshot') + } + }) }) } }