From d2b8d2e31a95bfd2e2713d6cdaca11b98479a7e9 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 18 Oct 2024 12:20:15 -0700 Subject: [PATCH 001/120] [#20241018] Fix SimpleModal icon --- src/components/SimpleModal.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/SimpleModal.vue b/src/components/SimpleModal.vue index 739f35f13..b8c7a12e7 100644 --- a/src/components/SimpleModal.vue +++ b/src/components/SimpleModal.vue @@ -11,7 +11,7 @@
-
Deactivate account @@ -35,7 +35,9 @@ From af460a9f36088b36b6eb06b31561f9b9019ad803 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 18 Oct 2024 12:44:13 -0700 Subject: [PATCH 002/120] [#20241018] Separate domain logic into separate files - Splits domain-related classes into individual files for better modularity - Improves maintainability and clarity of the domain logic --- lib/onetime/logic/domains.rb | 234 +-------------------- lib/onetime/logic/domains/add_domain.rb | 79 +++++++ lib/onetime/logic/domains/get_domain.rb | 76 +++++++ lib/onetime/logic/domains/list_domains.rb | 32 +++ lib/onetime/logic/domains/remove_domain.rb | 62 ++++++ 5 files changed, 255 insertions(+), 228 deletions(-) create mode 100644 lib/onetime/logic/domains/add_domain.rb create mode 100644 lib/onetime/logic/domains/get_domain.rb create mode 100644 lib/onetime/logic/domains/list_domains.rb create mode 100644 lib/onetime/logic/domains/remove_domain.rb diff --git a/lib/onetime/logic/domains.rb b/lib/onetime/logic/domains.rb index d7c14507a..8e1826ecc 100644 --- a/lib/onetime/logic/domains.rb +++ b/lib/onetime/logic/domains.rb @@ -1,237 +1,15 @@ - require 'public_suffix' require_relative 'base' require_relative '../cluster' +require_relative 'domains/add_domain' +require_relative 'domains/remove_domain' +require_relative 'domains/list_domains' +require_relative 'domains/get_domain' + module Onetime::Logic module Domains - - class AddDomain < OT::Logic::Base - attr_reader :greenlighted, :custom_domain - - def process_params - OT.ld "[AddDomain] Parsing #{params[:domain]}" - # PublicSuffix does its own normalizing so we don't need to do any here - @domain_input = params[:domain].to_s - end - - def raise_concerns - - OT.ld "[AddDomain] Raising any concerns about #{@domain_input}" - # TODO: Consider returning all applicable errors (plural) at once - raise_form_error "Please enter a domain" if @domain_input.empty? - raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) - - limit_action :add_domain - - # Only store a valid, parsed input value to @domain - @parsed_domain = OT::CustomDomain.parse(@domain_input, @cust) # raises OT::Problem - @display_domain = @parsed_domain.display_domain - - # Don't need to do a bunch of validation checks here. If the input value - # passes as valid, it's valid. If another account has verified the same - # domain, that's fine. Both accounts can generate secret links for that - # domain, and the links will be valid for both accounts. - # - # e.g. `OT::CustomDomain.exists?(@domain)` - - end - - def process - @greenlighted = true - OT.ld "[AddDomain] Processing #{@display_domain}" - @custom_domain = OT::CustomDomain.create(@display_domain, @cust.custid) - - # Create the approximated vhost for this domain. Approximated provides a - # custom domain as a service API. If no API key is set, then this will - # simply log a message and return. - create_vhost - end - - def create_vhost - api_key = OT::Cluster::Features.api_key - vhost_target = OT::Cluster::Features.vhost_target - - if api_key.to_s.empty? - return OT.info "[AddDomain.create_vhost] Approximated API key not set" - end - - res = OT::Cluster::Approximated.create_vhost(api_key, @display_domain, vhost_target, '443') - payload = res.parsed_response - - OT.info "[AddDomain.create_vhost] %s" % payload - custom_domain.vhost = payload['data'].to_json - custom_domain.updated = OT.now.to_i - custom_domain.save - - rescue HTTParty::ResponseError => e - OT.le "[AddDomain.create_vhost error] %s %s %s" % [@cust.custid, @display_domain, e] - end - - def success_data - { - custid: @cust.custid, - record: @custom_domain.safe_dump, - details: { - cluster: OT::Cluster::Features.cluster_safe_dump - } - } - end - end - - class RemoveDomain < OT::Logic::Base - attr_reader :greenlighted, :domain_input, :display_domain - def process_params - @domain_input = params[:domain].to_s.strip - end - - def raise_concerns - raise_form_error "Please enter a domain" if @domain_input.empty? - raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) - - limit_action :remove_domain - - @custom_domain = OT::CustomDomain.load(@domain_input, @cust.custid) - raise_form_error "Domain not found" unless @custom_domain - end - - def process - OT.ld "[RemoveDomain] Processing #{domain_input} for #{@custom_domain.identifier}" - @greenlighted = true - @display_domain = @custom_domain.display_domain - - # TODO: @custom_domain.redis.multi - - @custom_domain.destroy!(@cust) - - # NOTE: Disable deleting the domain from the cluster vhost to - # avoid issue with two customers adding the same domain and then - # one removing it. This would cause the domain to be removed for - # both customers, which would be surprising. Instead, we can - # just disable the domain for this customer and let them add it - # again if they want to use it in the future. - # - # delete_vhost - end - - def delete_vhost - api_key = OT::Cluster::Features.api_key - if api_key.to_s.empty? - return OT.info "[RemoveDomain.delete_vhost] Approximated API key not set" - end - res = OT::Cluster::Approximated.delete_vhost(api_key, @display_domain) - payload = res.parsed_response - OT.info "[RemoveDomain.delete_vhost] %s" % payload - rescue HTTParty::ResponseError => e - OT.le "[RemoveDomain.delete_vhost error] %s %s %s" % [@cust.custid, @display_domain, e] - end - - def success_data - { - custid: @cust.custid, - record: {}, - message: "Removed #{display_domain}" - } - end - end - - class ListDomains < OT::Logic::Base - attr_reader :custom_domains - - def raise_concerns - limit_action :list_domains - end - - def process - OT.ld "[ListDomains] Processing #{@cust.custom_domains.length}" - OT.info "[ListDomains] Processing #{@cust.custom_domains.rediskey}" - - @custom_domains = @cust.custom_domains_list.map { |cd| cd.safe_dump } - end - - def success_data - { - custid: @cust.custid, - records: @custom_domains, - count: @custom_domains.length, - details: { - cluster: OT::Cluster::Features.cluster_safe_dump - } - } - end - end - - class GetDomain < OT::Logic::Base - - attr_reader :greenlighted, :display_domain, :custom_domain - - def process_params - @domain_input = params[:domain].to_s.strip - end - - def raise_concerns - raise_form_error "Please enter a domain" if @domain_input.empty? - raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) - - limit_action :get_domain - - # Getting the domain record based on `req.params[:domain]` (which is - # the display_domain). That way we need to combine with the custid - # in order to find it. It's a way of proving ownership. Vs passing the - # domainid in the URL path which gives up the goods. - @custom_domain = OT::CustomDomain.load(@domain_input, @cust.custid) - - raise_form_error "Domain not found" unless @custom_domain - end - - def process - OT.ld "[GetDomain] Processing #{@custom_domain.display_domain}" - @greenlighted = true - @display_domain = @custom_domain.display_domain - end - - def success_data - { - custid: @cust.custid, - record: custom_domain.safe_dump, - details: { - cluster: OT::Cluster::Features.cluster_safe_dump - } - } - end - end - - class VerifyDomain < GetDomain - - def raise_concerns - # Run this limiter before calling super which in turn runs - # the get_domain limiter since verify is a more restrictive. No - # sense running the get logic more than we need to. - limit_action :verify_domain - - super - end - - def process - super - - refresh_vhost - end - - def refresh_vhost - api_key = OT::Cluster::Features.api_key - if api_key.to_s.empty? - return OT.info "[VerifyDomain.refresh_vhost] Approximated API key not set" - end - res = OT::Cluster::Approximated.get_vhost_by_incoming_address(api_key, display_domain) - payload = res.parsed_response - OT.info "[VerifyDomain.refresh_vhost] %s" % payload - OT.ld "" - custom_domain.vhost = payload['data'].to_json - custom_domain.updated = OT.now.to_i - custom_domain.save - end - end + # This file now serves as a namespace and requires all the individual domain-related files end end diff --git a/lib/onetime/logic/domains/add_domain.rb b/lib/onetime/logic/domains/add_domain.rb new file mode 100644 index 000000000..2176831f3 --- /dev/null +++ b/lib/onetime/logic/domains/add_domain.rb @@ -0,0 +1,79 @@ +require 'public_suffix' + +require_relative '../base' +require_relative '../../cluster' + +module Onetime::Logic + module Domains + class AddDomain < OT::Logic::Base + attr_reader :greenlighted, :custom_domain + + def process_params + OT.ld "[AddDomain] Parsing #{params[:domain]}" + # PublicSuffix does its own normalizing so we don't need to do any here + @domain_input = params[:domain].to_s + end + + def raise_concerns + OT.ld "[AddDomain] Raising any concerns about #{@domain_input}" + # TODO: Consider returning all applicable errors (plural) at once + raise_form_error "Please enter a domain" if @domain_input.empty? + raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) + + limit_action :add_domain + + # Only store a valid, parsed input value to @domain + @parsed_domain = OT::CustomDomain.parse(@domain_input, @cust) # raises OT::Problem + @display_domain = @parsed_domain.display_domain + + # Don't need to do a bunch of validation checks here. If the input value + # passes as valid, it's valid. If another account has verified the same + # domain, that's fine. Both accounts can generate secret links for that + # domain, and the links will be valid for both accounts. + # + # e.g. `OT::CustomDomain.exists?(@domain)` + end + + def process + @greenlighted = true + OT.ld "[AddDomain] Processing #{@display_domain}" + @custom_domain = OT::CustomDomain.create(@display_domain, @cust.custid) + + # Create the approximated vhost for this domain. Approximated provides a + # custom domain as a service API. If no API key is set, then this will + # simply log a message and return. + create_vhost + end + + def create_vhost + api_key = OT::Cluster::Features.api_key + vhost_target = OT::Cluster::Features.vhost_target + + if api_key.to_s.empty? + return OT.info "[AddDomain.create_vhost] Approximated API key not set" + end + + res = OT::Cluster::Approximated.create_vhost(api_key, @display_domain, vhost_target, '443') + payload = res.parsed_response + + OT.info "[AddDomain.create_vhost] %s" % payload + custom_domain.vhost = payload['data'].to_json + custom_domain.updated = OT.now.to_i + custom_domain.save + + rescue HTTParty::ResponseError => e + OT.le "[AddDomain.create_vhost error] %s %s %s" % [@cust.custid, @display_domain, e] + end + + def success_data + { + custid: @cust.custid, + record: @custom_domain.safe_dump, + details: { + cluster: OT::Cluster::Features.cluster_safe_dump + } + } + end + end + end +end diff --git a/lib/onetime/logic/domains/get_domain.rb b/lib/onetime/logic/domains/get_domain.rb new file mode 100644 index 000000000..2e93e3c1d --- /dev/null +++ b/lib/onetime/logic/domains/get_domain.rb @@ -0,0 +1,76 @@ +require_relative '../base' +require_relative '../../cluster' + +module Onetime::Logic + module Domains + class GetDomain < OT::Logic::Base + attr_reader :greenlighted, :display_domain, :custom_domain + + def process_params + @domain_input = params[:domain].to_s.strip + end + + def raise_concerns + raise_form_error "Please enter a domain" if @domain_input.empty? + raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) + + limit_action :get_domain + + # Getting the domain record based on `req.params[:domain]` (which is + # the display_domain). That way we need to combine with the custid + # in order to find it. It's a way of proving ownership. Vs passing the + # domainid in the URL path which gives up the goods. + @custom_domain = OT::CustomDomain.load(@domain_input, @cust.custid) + + raise_form_error "Domain not found" unless @custom_domain + end + + def process + OT.ld "[GetDomain] Processing #{@custom_domain.display_domain}" + @greenlighted = true + @display_domain = @custom_domain.display_domain + end + + def success_data + { + custid: @cust.custid, + record: custom_domain.safe_dump, + details: { + cluster: OT::Cluster::Features.cluster_safe_dump + } + } + end + end + + class VerifyDomain < GetDomain + def raise_concerns + # Run this limiter before calling super which in turn runs + # the get_domain limiter since verify is a more restrictive. No + # sense running the get logic more than we need to. + limit_action :verify_domain + + super + end + + def process + super + + refresh_vhost + end + + def refresh_vhost + api_key = OT::Cluster::Features.api_key + if api_key.to_s.empty? + return OT.info "[VerifyDomain.refresh_vhost] Approximated API key not set" + end + res = OT::Cluster::Approximated.get_vhost_by_incoming_address(api_key, display_domain) + payload = res.parsed_response + OT.info "[VerifyDomain.refresh_vhost] %s" % payload + OT.ld "" + custom_domain.vhost = payload['data'].to_json + custom_domain.updated = OT.now.to_i + custom_domain.save + end + end + end +end diff --git a/lib/onetime/logic/domains/list_domains.rb b/lib/onetime/logic/domains/list_domains.rb new file mode 100644 index 000000000..428408a3c --- /dev/null +++ b/lib/onetime/logic/domains/list_domains.rb @@ -0,0 +1,32 @@ +require_relative '../base' +require_relative '../../cluster' + +module Onetime::Logic + module Domains + class ListDomains < OT::Logic::Base + attr_reader :custom_domains + + def raise_concerns + limit_action :list_domains + end + + def process + OT.ld "[ListDomains] Processing #{@cust.custom_domains.length}" + OT.info "[ListDomains] Processing #{@cust.custom_domains.rediskey}" + + @custom_domains = @cust.custom_domains_list.map { |cd| cd.safe_dump } + end + + def success_data + { + custid: @cust.custid, + records: @custom_domains, + count: @custom_domains.length, + details: { + cluster: OT::Cluster::Features.cluster_safe_dump + } + } + end + end + end +end diff --git a/lib/onetime/logic/domains/remove_domain.rb b/lib/onetime/logic/domains/remove_domain.rb new file mode 100644 index 000000000..a0ca2684d --- /dev/null +++ b/lib/onetime/logic/domains/remove_domain.rb @@ -0,0 +1,62 @@ +require_relative '../base' +require_relative '../../cluster' + +module Onetime::Logic + module Domains + class RemoveDomain < OT::Logic::Base + attr_reader :greenlighted, :domain_input, :display_domain + def process_params + @domain_input = params[:domain].to_s.strip + end + + def raise_concerns + raise_form_error "Please enter a domain" if @domain_input.empty? + raise_form_error "Not a valid public domain" unless OT::CustomDomain.valid?(@domain_input) + + limit_action :remove_domain + + @custom_domain = OT::CustomDomain.load(@domain_input, @cust.custid) + raise_form_error "Domain not found" unless @custom_domain + end + + def process + OT.ld "[RemoveDomain] Processing #{domain_input} for #{@custom_domain.identifier}" + @greenlighted = true + @display_domain = @custom_domain.display_domain + + # TODO: @custom_domain.redis.multi + + @custom_domain.destroy!(@cust) + + # NOTE: Disable deleting the domain from the cluster vhost to + # avoid issue with two customers adding the same domain and then + # one removing it. This would cause the domain to be removed for + # both customers, which would be surprising. Instead, we can + # just disable the domain for this customer and let them add it + # again if they want to use it in the future. + # + # delete_vhost + end + + def delete_vhost + api_key = OT::Cluster::Features.api_key + if api_key.to_s.empty? + return OT.info "[RemoveDomain.delete_vhost] Approximated API key not set" + end + res = OT::Cluster::Approximated.delete_vhost(api_key, @display_domain) + payload = res.parsed_response + OT.info "[RemoveDomain.delete_vhost] %s" % payload + rescue HTTParty::ResponseError => e + OT.le "[RemoveDomain.delete_vhost error] %s %s %s" % [@cust.custid, @display_domain, e] + end + + def success_data + { + custid: @cust.custid, + record: {}, + message: "Removed #{display_domain}" + } + end + end + end +end From a6fc9ad7eec1d379208f1c43a2eb20262fa7d2b5 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 18 Oct 2024 12:47:07 -0700 Subject: [PATCH 003/120] [#20241018] Add domain brand settings view * Introduces API endpoints for getting and saving branding settings. * Adds new route and view for managing domain branding settings. * Enables users to configure logo, colors, description, font, and button style. --- lib/onetime/app/api/v2/domains.rb | 14 ++- src/components/DomainsTable.vue | 13 +-- src/router/account.ts | 13 ++- src/views/account/DomainBrandSettings.vue | 121 ++++++++++++++++++++++ 4 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 src/views/account/DomainBrandSettings.vue diff --git a/lib/onetime/app/api/v2/domains.rb b/lib/onetime/app/api/v2/domains.rb index 3e38faf39..f3bfe5395 100644 --- a/lib/onetime/app/api/v2/domains.rb +++ b/lib/onetime/app/api/v2/domains.rb @@ -1,5 +1,3 @@ - - require_relative 'base' require_relative '../../app_settings' require_relative '../../../logic/domains' @@ -49,5 +47,17 @@ def list_domains retrieve_records(OT::Logic::Domains::ListDomains) end + def get_branding_settings + retrieve_records(OT::Logic::Domains::GetBrandingSettings) + end + + def save_branding_settings + process_action( + OT::Logic::Domains::SaveBrandingSettings, + "Branding settings saved successfully.", + "Branding settings could not be saved." + ) + end + end end diff --git a/src/components/DomainsTable.vue b/src/components/DomainsTable.vue index 66aab0084..b719273ac 100644 --- a/src/components/DomainsTable.vue +++ b/src/components/DomainsTable.vue @@ -6,9 +6,6 @@

These are your verified custom domains.

- - - Add Domain @@ -49,9 +46,6 @@