diff --git a/Gemfile b/Gemfile index a39a03f..eef60c6 100644 --- a/Gemfile +++ b/Gemfile @@ -50,6 +50,10 @@ gem 'kamal', '~> 1.3' gem 'litestack', '~> 0.4.2' +gem 'active_storage_validations', '~> 1.1' +gem 'aws-sdk-s3', '~> 1.141' +gem 'inline_svg', '~> 1.9' + # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" @@ -60,6 +64,8 @@ end group :development do # Use console on exceptions pages [https://github.com/rails/web-console] + gem 'annotate', '~> 3.2' + gem 'erb-formatter', '~> 0.6.0' gem 'rubocop', '~> 1.59' gem 'web-console' @@ -69,11 +75,3 @@ group :development do # Speed up commands on slow machines / big apps [https://github.com/rails/spring] # gem "spring" end - -gem 'inline_svg', '~> 1.9' - -gem 'annotate', '~> 3.2' - -gem 'erb-formatter', '~> 0.6.0' - -gem "active_storage_validations", "~> 1.1" diff --git a/Gemfile.lock b/Gemfile.lock index 1a69ddc..1a82eca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -84,6 +84,22 @@ GEM activerecord (>= 3.2, < 8.0) rake (>= 10.4, < 14.0) ast (2.4.2) + aws-eventstream (1.3.0) + aws-partitions (1.868.0) + aws-sdk-core (3.190.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.8) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.75.0) + aws-sdk-core (~> 3, >= 3.188.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.141.0) + aws-sdk-core (~> 3, >= 3.189.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) + aws-eventstream (~> 1, >= 1.0.2) base64 (0.2.0) bcrypt_pbkdf (1.1.0) bigdecimal (3.1.4) @@ -130,6 +146,7 @@ GEM jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) + jmespath (1.6.2) json (2.7.1) kamal (1.3.0) activesupport (>= 7.0) @@ -302,6 +319,7 @@ PLATFORMS DEPENDENCIES active_storage_validations (~> 1.1) annotate (~> 3.2) + aws-sdk-s3 (~> 1.141) bootsnap debug erb-formatter (~> 0.6.0) diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index 479fe79..c000e9c 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -1,9 +1,9 @@ class ErrorsController < ApplicationController def not_found - render status: 404 + render status: :not_found end def internal_server_error - render status: 500 + render status: :internal_server_error end end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 5a476b3..90a555b 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -2,7 +2,8 @@ class UploadsController < ApplicationController include ActiveStorage::SetCurrent - include ActionController::Live + + skip_before_action :verify_authenticity_token, only: %i[upload preview] def new @upload = Upload.new(expiry: 10.minutes.from_now, remaining_uses: 1) @@ -26,28 +27,28 @@ def upload def preview @upload = upload_scope.where(previewed: false).find_by!(key: params[:id]) @upload.update!(previewed: true) - respond_to do |format| - format.html { render :preview, status: :not_found } - end + render :preview end def download @upload = upload_scope.find_by!(key: params[:id]) @upload.decrement!(:remaining_uses) - redirect_to rails_blob_path(@upload.data, disposition: 'attachment') - # send_data @upload.data.download, filename: @upload.data.filename.to_s, content_type: @upload.data.content_type + redirect_to(@upload.data.url(disposition: 'attachment', filename: @upload.data.filename.to_s), + allow_other_host: true) end private def upload_scope - Upload.where('expiry > ?', DateTime.now).where('remaining_uses > ?', 0) + Upload + # Upload.where('expiry > ?', DateTime.now).where('remaining_uses > ?', 0) end # Only allow a list of trusted parameters through. def upload_params params .require(:upload).permit(:data, :expiry, :remaining_uses) + .with_defaults(expiry: 10.minutes.from_now, remaining_uses: 1) end end diff --git a/app/models/upload.rb b/app/models/upload.rb index ed1d953..074d38b 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -21,6 +21,8 @@ class Upload < ApplicationRecord validates :data, attached: true, size: { less_than: 512.kilobytes, message: 'is must be smaller than 512 kb' } + def url; end + def to_param key end diff --git a/app/views/application/home.html.erb b/app/views/application/home.html.erb index 375fd26..a41f9f1 100644 --- a/app/views/application/home.html.erb +++ b/app/views/application/home.html.erb @@ -71,7 +71,8 @@ - curl -X POST -F "data=@yourfile.txt" https://datadeaddrop.com/drop + curl -X POST -F "upload[data]=@your-file.txt" + https://datadeaddrop.com/u.json @@ -117,7 +118,8 @@ - curl -O https://datadeaddrop.com/grab/your-easy-to-remember-url + curl -L http://datadeaddrop.com/d/your-secret-url.json > + your-file.txt diff --git a/app/views/errors/internal_server_error.json.jbuilder b/app/views/errors/internal_server_error.json.jbuilder new file mode 100644 index 0000000..1cca0c7 --- /dev/null +++ b/app/views/errors/internal_server_error.json.jbuilder @@ -0,0 +1,2 @@ + +json.error "Not found" diff --git a/app/views/errors/not_found.json.jbuilder b/app/views/errors/not_found.json.jbuilder new file mode 100644 index 0000000..1cca0c7 --- /dev/null +++ b/app/views/errors/not_found.json.jbuilder @@ -0,0 +1,2 @@ + +json.error "Not found" diff --git a/app/views/uploads/_upload.json.jbuilder b/app/views/uploads/_upload.json.jbuilder index 259d6b0..fce89e7 100644 --- a/app/views/uploads/_upload.json.jbuilder +++ b/app/views/uploads/_upload.json.jbuilder @@ -1,5 +1,5 @@ # frozen_string_literal: true -json.extract! upload, :id, :path, :data, :expiry, :remaining_uses, :previewed, :created_at, :updated_at -json.url upload_url(upload, format: :json) -json.data url_for(upload.data) +json.extract! upload, :key, :expiry, :remaining_uses, :created_at +json.url Rails.application.routes.url_helpers.download_url(upload) +json.name upload.data.filename.to_s diff --git a/config/application.rb b/config/application.rb index 798c662..6025d29 100644 --- a/config/application.rb +++ b/config/application.rb @@ -43,5 +43,7 @@ class Application < Rails::Application # Use routes to render error pages config.exceptions_app = routes + + config.active_storage.draw_routes = false end end diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc index 984674a..62a1156 100644 --- a/config/credentials/development.yml.enc +++ b/config/credentials/development.yml.enc @@ -1 +1 @@ -LiprxKVi8XEhjTywr2c4tXgDb8853hoHtCu7MBHqCV3eS/1H26OvLy+MWGF3bPGBqB+u0woHD5tpJ5d5nXc+jydyx2u9XxN6ozM8Ttg3rfiTW8fv5Ld2aMp5f6s8RgWUxt9wiT1PZmmVn5Bty8iOqVmjFZPIRh+mzRED7y9ZDENn9bq3r0s28EmH883FoqAzpVfbSFUr+cHhW3Ak7lyPMp27ZBXx0EmR7n+kVNz6n7qZtN/yhc0PR8X9wYlau7nAtwM2lAZYfOUVRPH9pVYwNwxX46D+tAqFyhXN94E1wMaQ2Jbtuot53RfChWcDFvcLFeDJh0bNOIe7j368vJ8qeEqNPZ//sneeTA8kA9MzkSf5PO1sGoIm/SJ7XeRtDNw2m9gzeNILjRpecIWYbSK6hoPJMIym--zZUM4heW6d0PimS4--uAWAPX3UFL+FJGL22zo9Kw== \ No newline at end of file +4sS2+n0Ick8STxrOAIIOaq5+xD/lPThoQJWY7MUF3v5G4nN1Uc0rpgRVoDazsEEJ0S2uK9rLjGpxX9LoPRZCvGSXMp4MUCUKe5kbR5TLcJvT/wNPM21yM6VTHkcJGBKtrqwx837Ds/IFSoFMEoZ2ny9RV1Zj3rWym73ePkJsRLKeLJ/5Vlw6uGR09xxjkR5BtDMBjGISl2xPjd1Wv5f9DjAa2b6NCDxbAYmlP/bjyWlxhIQp6hihzWuqK2C/7Osc+kgop0LtKdJTSo/4Cz65g2clR34kn+QwpV4VjK1QlUZsL8quG8osR63v2NNOgwTQe1uw4lWYAeWyB8OFwCW4hPePlxlrfAspP7m2GAAldfu5VfdxDjd7buYwpAxSV5hhGMc+xAASlkmjMAbudGgUODR/p77z--XBTbR6JVHGtqn2nr--q+7ZZYzW7tc976r8+RfASQ== \ No newline at end of file diff --git a/config/credentials/production.yml.enc b/config/credentials/production.yml.enc index c80e09e..51a889b 100644 --- a/config/credentials/production.yml.enc +++ b/config/credentials/production.yml.enc @@ -1 +1 @@ -ONYihcJE6z85Sr3c+PSS16urjpkmxk8OajAE4WuusfIo8s5YnSrtyn7qkq8XZ3Qn73F1o+zatPGsQb2rfV3TmR9g5BfSxcJcwWTdoSRjS7QppMR7EiB5PP7O3wd5N3bG39pZ7qTHrzVg08AGrEL6yvs+RXTgUR2QFHsDoIJj3YlkwIBer8byCr9nC9nt3xSpqhzbxmdhMmd+KTsCFL/OlXdcOJvOwxHzhPUj0rImHk+yD5evHVac0gSule7ucbbfAVvOxZOm15J66k3qR+eRv+f1BKG4WjlwBdPXqmBA5TFOfU3WiQ/stfHJww+AuTH53b3cGKxoEHgycW7WEDzolxwHkur7NEC737vsR/0dW4X1eyYUsNVviOFeRGIvxPr85vC92Mf3yY5VmVZNBvn3WCD7kHYd--UjeTagvsRgXeDVxp--x0gK9KAI8+md42EmZZdsdQ== \ No newline at end of file +Xfq7VdD6WwiG6rq1lNFt0FoUu3e15SGkQ7QQJkHfzq/E+BoXVUwHHEZVElwemmWVumiHkL5MyX5qr0/pbu2BtpDt8HmFtyOWrLvql1vIGmPQkTxHpFjq6fh5XZ6lrzoTa2CAUeeWXuzVtab1N5itDtf4IppWPHJ7+cXf895cRrVbIA0jt906O8tjCjnvgeoA6YQgGebdp2PW93WfAzdnhgM37w8yeU+fcxxKRi095FQ9VZ7s25/f8yS5VAM0GBV+aS1j7b+y0qycToOc3huF9YE1aRdxNmrGk21bXdJ4ylcpryPNoFrXJGTpo1QZ9Nx8d21sr3cX3V5AanTGIXquqn9Gz+6+w8RVAldmx8dcmJuvW7XvFZuvz8McNWsJrlUZkQavSMMUEV/7t7datMim0T4wW1aQ+4BfsbvDEm2Tn0HmTja00mbncBSaChJwAiEnYTsdbp6evU3PmIp9qUGAr5A5eYrr--lOdUxq5SatVrCS+d--CF7k2/VNtrXfUl9I9lJWFg== \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 80e0587..f5176c2 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,7 +14,7 @@ config.eager_load = false # Show full error reports. - config.consider_all_requests_local = true + config.consider_all_requests_local = false # Enable server timing config.server_timing = true diff --git a/config/environments/production.rb b/config/environments/production.rb index 8ae41a2..7805839 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -39,7 +39,7 @@ # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX # Store uploaded files on the local file system (see config/storage.yml for options). - config.active_storage.service = :local + config.active_storage.service = :amazon # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb new file mode 100644 index 0000000..296bfc7 --- /dev/null +++ b/config/initializers/default_url_options.rb @@ -0,0 +1,6 @@ +hosts = { + development: 'localhost:3000', + production: 'datadeaddrop.com' +}.freeze + +Rails.application.routes.default_url_options[:host] = hosts[Rails.env.to_sym] diff --git a/config/routes.rb b/config/routes.rb index 4e255c5..98c913c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,10 +5,10 @@ get 'errors/internal_server_error' root 'application#home' - get '/new', to: 'uploads#new', as: :new - post '/upload', to: 'uploads#upload', as: :upload - get '/download/:id/preview', to: 'uploads#preview', as: :preview - get '/download/:id', to: 'uploads#download', as: :download + get '/n', to: 'uploads#new', as: :new + post '/u', to: 'uploads#upload', as: :upload + get '/p/:id', to: 'uploads#preview', as: :preview + get '/d/:id', to: 'uploads#download', as: :download get 'up' => 'rails/health#show', as: :rails_health_check # Preview error pages @@ -17,4 +17,11 @@ match '/404', to: 'errors#not_found', via: :all match '/500', to: 'errors#internal_server_error', via: :all + + if Rails.application.config.active_storage.service == :local + scope ActiveStorage.routes_prefix do + get '/disk/:encoded_key/*filename' => 'active_storage/disk#show', as: :rails_disk_service + put '/disk/:encoded_token' => 'active_storage/disk#update', as: :update_rails_disk_service + end + end end diff --git a/config/storage.yml b/config/storage.yml index 4942ab6..7537251 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -6,29 +6,9 @@ local: service: Disk root: <%= Rails.root.join("storage") %> -# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) -# amazon: -# service: S3 -# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> -# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> -# region: us-east-1 -# bucket: your_own_bucket-<%= Rails.env %> - -# Remember not to checkin your GCS keyfile to a repository -# google: -# service: GCS -# project: your_project -# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> -# bucket: your_own_bucket-<%= Rails.env %> - -# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) -# microsoft: -# service: AzureStorage -# storage_account_name: your_account_name -# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> -# container: your_container_name-<%= Rails.env %> - -# mirror: -# service: Mirror -# primary: local -# mirrors: [ amazon, google, microsoft ] +amazon: + service: S3 + access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> + secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> + region: eu-central-1 + bucket: data-dead-drop