diff --git a/app.rb b/app.rb index 3fc9bb64..ce118fbf 100644 --- a/app.rb +++ b/app.rb @@ -74,6 +74,75 @@ get '/?' do erb :index, layout: false end +get '/plan/?' do + erb :'plan/index' +end + +post '/plan/create' do + require_login + + DB.transaction do + customer = Stripe::Customer.create( + card: params[:stripe_token], + description: current_site.username, + email: current_site.email, + plan: params[:selected_plan] + ) + current_site.stripe_customer_id = customer.id + current_site.plan_ended = false + current_site.save + end + + redirect '/plan' +end + +def get_plan_name(customer_id) + subscriptions = Stripe::Customer.retrieve(current_site.stripe_customer_id).subscriptions.all + @plan_name = subscriptions.first.plan.name +end + +get '/plan/manage' do + require_login + redirect '/plan' unless current_site.supporter? && !current_site.plan_ended + @title = 'Manage Plan' + @plan_name = get_plan_name current_site.stripe_customer_id + erb :'plan/manage' +end + +get '/plan/end' do + require_login + redirect '/plan' unless current_site.supporter? && !current_site.plan_ended + @title = 'End Plan' + @plan_name = get_plan_name current_site.stripe_customer_id + erb :'plan/end' +end + +post '/plan/end' do + require_login + redirect '/plan' unless current_site.supporter? && !current_site.plan_ended + + recaptcha_is_valid = ENV['RACK_ENV'] == 'test' || recaptcha_valid? + + if !recaptcha_is_valid + @error = 'Recaptcha was filled out incorrectly, please try re-entering.' + @plan_name = get_plan_name current_site.stripe_customer_id + halt erb :'plan/end' + end + + customer = Stripe::Customer.retrieve current_site.stripe_customer_id + subscriptions = customer.subscriptions.all + + DB.transaction do + subscriptions.each do |subscription| + customer.subscriptions.retrieve(subscription.id).delete + end + current_site.plan_ended = true + current_site.save + end + + redirect '/plan' +end + get '/site/:username/tip' do |username| @site = Site[username: username] @title = "Tip #{@site.title}" @@ -330,7 +399,7 @@ post '/site_files/upload' do halt http_error_code, 'Did not receive file upload.' end - if params[:newfile][:tempfile].size > Site::MAX_SPACE || (params[:newfile][:tempfile].size + current_site.total_space) > Site::MAX_SPACE + if current_site.file_size_too_large? params[:newfile][:tempfile].size @errors << 'File size must be smaller than available space.' halt http_error_code, 'File size must be smaller than available space.' end @@ -390,15 +459,15 @@ post '/site_files/save/:filename' do |filename| tempfile = Tempfile.new 'neocities_saving_file' - if (tempfile.size + current_site.total_space) > Site::MAX_SPACE - halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.' - end - input = request.body.read tempfile.set_encoding input.encoding tempfile.write input tempfile.close + if current_site.file_site_too_large? tempfile.size + halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.' + end + sanitized_filename = filename.gsub(/[^a-zA-Z0-9_\-.]/, '') current_site.store_file sanitized_filename, tempfile @@ -622,7 +691,7 @@ post '/api/upload' do uploaded_size = files.collect {|f| f[:tempfile].size}.inject{|sum,x| sum + x } - if (uploaded_size + current_site.total_space) > Site::MAX_SPACE + if current_site.file_size_too_large? uploaded_size api_error 400, 'too_large', 'files are too large to fit in your space, try uploading smaller (or less) files' end diff --git a/migrations/020_add_plan_ended.rb b/migrations/020_add_plan_ended.rb new file mode 100644 index 00000000..538071b2 --- /dev/null +++ b/migrations/020_add_plan_ended.rb @@ -0,0 +1,9 @@ +Sequel.migration do + up { + DB.add_column :sites, :plan_ended, :boolean, default: false + } + + down { + DB.drop_column :sites, :plan_ended + } +end \ No newline at end of file diff --git a/models/site.rb b/models/site.rb index 0937079f..cba4a26f 100644 --- a/models/site.rb +++ b/models/site.rb @@ -28,7 +28,13 @@ class Site < Sequel::Model html htm txt text css js jpg jpeg png gif svg md markdown eot ttf woff json geojson csv tsv mf ico pdf asc key pgp xml mid midi } - MAX_SPACE = (5242880*2) # 10MB + + ONE_MEGABYTE_IN_BYTES = 1048576 + FREE_MAXIMUM_IN_MEGABYTES = 10 + SUPPORTER_MAXIMUM_IN_MEGABYTES = 200 + FREE_MAXIMUM_IN_BYTES = FREE_MAXIMUM_IN_MEGABYTES * ONE_MEGABYTE_IN_BYTES + SUPPORTER_MAXIMUM_IN_BYTES = SUPPORTER_MAXIMUM_IN_MEGABYTES * ONE_MEGABYTE_IN_BYTES + MINIMUM_PASSWORD_LENGTH = 5 BAD_USERNAME_REGEX = /[^\w-]/i VALID_HOSTNAME = /^[a-z0-9][a-z0-9-]+?[a-z0-9]$/i # http://tools.ietf.org/html/rfc1123 @@ -294,22 +300,54 @@ class Site < Sequel::Model Dir.glob(File.join(files_path, '*')).collect {|p| File.basename(p)}.sort.collect {|sitename| SiteFile.new sitename} end - def total_space + def file_size_too_large?(size_in_bytes) + return true if size_in_bytes + used_space_in_bytes > maximum_space_in_bytes + false + end + + def used_space_in_bytes space = Dir.glob(File.join(files_path, '*')).collect {|p| File.size(p)}.inject {|sum,x| sum += x} space.nil? ? 0 : space end - def total_space_in_megabytes - (total_space.to_f / 2**20).round(2) + def used_space_in_megabytes + (used_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2) end - def available_space - remaining = MAX_SPACE - total_space + def available_space_in_bytes + remaining = maximum_space_in_bytes - used_space_in_bytes remaining < 0 ? 0 : remaining end def available_space_in_megabytes - (available_space.to_f / 2**20).round(2) + (available_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2) + end + + def maximum_space_in_bytes + supporter? ? self.class::SUPPORTER_MAXIMUM_IN_BYTES : self.class::FREE_MAXIMUM_IN_BYTES + end + + def maximum_space_in_megabytes + supporter? ? self.class::SUPPORTER_MAXIMUM_IN_MEGABYTES : self.class::FREE_MAXIMUM_IN_MEGABYTES + end + + def space_percentage_used + ((used_space_in_bytes.to_f / maximum_space_in_bytes) * 100).round(1) + end + + # This returns true even if they end their support plan. + def supporter? + !values[:stripe_customer_id].nil? + end + + # This will return false if they have ended their support plan. + def ended_supporter? + values[:ended_plan] + end + + def plan_name + return 'Free Plan' if !supporter? || (supporter? && ended_supporter?) + 'Supporter Plan' end def title diff --git a/views/_header.erb b/views/_header.erb index 7ab50b73..68487016 100644 --- a/views/_header.erb +++ b/views/_header.erb @@ -22,6 +22,9 @@
http://<%= current_site.username %>.neocities.org
Last thing! Enter these two words correctly (with spaces) so we know you're not a robot (don't worry robots, we still love you).
+ You currently have the <%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB) - <%= @plan_name %>. +
++ If you need to end the plan, you can do that here. We'll be sorry to see you go. If there's a reason you're ending that we can help with, please contact us and we'll see if we can help you with your issue. Regardless, we'll let you keep your site and the extra space. We hope you'll decide to become a supporter again in the future! +
+ + <% if @error %> +<%= @error %>
++ You currently have the <%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB). +
++ Your support means a lot to us. On behalf of Penelope the cat and everyone at Neocities, thank you. If there's anything we can do to make your experience even better, please don't hesitate to contact us. +
+ + Manage plan + <% else %> + <% if current_site %> +You currently have the Free Plan (<%= current_site.maximum_space_in_megabytes %>MB). You should upgrade to the Neocities Supporter Plan!
+ +The Neocities Supporter Plan is a way to help sustain the site. When you join the Supporter Plan, you are directly helping our quest to bring back the creative, independent web, and to continue to improve Neocities for everyone. +
+If you would like to become a Neocities Supporter, Sign In and click the Settings link. If you haven't created a site yet, create your web site now! We would love if you became a Supporter, but base sites on Neocities will always be free.
+ <% else %> ++ Prefer to not use a credit card? Neocities has many other ways to become a supporter! Click here to learn about other ways to contribute. We accept PayPal, Bitcoin and Gittip, and will be happy to upgrade your account for you if you contribute and contact us with your site username. +
++ You currently have the <%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB) - <%= @plan_name %>. +
+ + End plan +