From d199e034ad9b7b08edc8c98a6d3388b1a0beade6 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Mon, 16 Jun 2025 18:59:42 -0600 Subject: [PATCH 01/13] Fix production deploy issue --- .github/workflows/deploy-production.yml | 1 + Dockerfile | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 37615d3d..4711e760 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -39,6 +39,7 @@ jobs: --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$IMAGE_TAG \ --build-arg VERSION=$IMAGE_TAG \ + --build-arg DOMAIN=account.system76.com \ --tag $ECR_REPOSITORY:$IMAGE_TAG \ . diff --git a/Dockerfile b/Dockerfile index 076e663a..b0c969dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN set -xe; \ # ----------------------------------------------- # 2) Build assets (relies on Elixir dependencies) # ----------------------------------------------- -FROM node:14.18-alpine as build-node +FROM node:16.20-alpine as build-node COPY --from=build-elixir /usr/local/src/recognizer /usr/local/src/recognizer WORKDIR /usr/local/src/recognizer/assets @@ -47,6 +47,10 @@ FROM build-elixir as build-release ARG APP_NAME=recognizer ARG MIX_ENV=prod +ARG DOMAIN + +# Set DOMAIN environment variable for production build +ENV DOMAIN=${DOMAIN} COPY --from=build-node /usr/local/src/recognizer /usr/local/src/recognizer WORKDIR /usr/local/src/recognizer From e785c92a29dc62a1e7a10e21aa3f2e485bace53e Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Tue, 17 Jun 2025 13:50:05 -0600 Subject: [PATCH 02/13] Fix asset DOMAIN variables --- .github/workflows/deploy-staging.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 3c794400..9ead7cac 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -39,6 +39,7 @@ jobs: --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$IMAGE_TAG \ --build-arg VERSION=$IMAGE_TAG \ + --build-arg DOMAIN=account.genesis76.com \ --tag $ECR_REPOSITORY:$IMAGE_TAG \ . From 61ff963925388dba7858e8ed08fc5545c0b12c21 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Tue, 17 Jun 2025 16:02:58 -0600 Subject: [PATCH 03/13] Fix asset DOMAIN variables --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index befded90..46f8d855 100644 --- a/mix.exs +++ b/mix.exs @@ -46,7 +46,7 @@ defmodule Recognizer.MixProject do {:ex_aws_sqs, "~> 3.2"}, {:ex_aws, "~> 2.0"}, {:ex_oauth2_provider, "~> 0.5.6"}, - {:gettext, "~> 0.11"}, + {:gettext, "~> 0.18"}, {:guardian, "~> 2.0"}, {:guardian_db, "~> 2.1"}, {:hammer, "~> 6.0"}, From 740afc4f73bcb60b560f8d6916e4662f1d8e5909 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Tue, 17 Jun 2025 17:02:35 -0600 Subject: [PATCH 04/13] Fix deploy issue --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b0c969dc..c7eb2b52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,7 @@ RUN set -xe; \ FROM debian:11.6-slim as release ARG APP_NAME=recognizer +ARG DOMAIN # These are fed in from the build script ARG VCS_REF @@ -103,7 +104,8 @@ ENV \ MIX_APP="recognizer" \ MIX_ENV="prod" \ SHELL="/bin/bash" \ - LANG=C.UTF-8 + LANG=C.UTF-8 \ + DOMAIN="${DOMAIN}" # Drop down to our unprivileged `recognizer` user USER recognizer From 9b204f5b4ccefacd757bffb2c76e9824127e1b3c Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Tue, 17 Jun 2025 17:43:25 -0600 Subject: [PATCH 05/13] Fix deploy issue --- .github/workflows/deploy-staging.yml | 40 ++++++++++++++++++++++++++-- Dockerfile | 4 +-- config/releases.exs | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 9ead7cac..b58f3ead 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -39,7 +39,6 @@ jobs: --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$IMAGE_TAG \ --build-arg VERSION=$IMAGE_TAG \ - --build-arg DOMAIN=account.genesis76.com \ --tag $ECR_REPOSITORY:$IMAGE_TAG \ . @@ -66,8 +65,45 @@ jobs: - name: Deploy uses: aws-actions/amazon-ecs-deploy-task-definition@v2 + timeout-minutes: 15 with: task-definition: ${{ steps.template.outputs.task-definition }} service: staging-genesis76-recognizer cluster: genesis76-us-east-2 - wait-for-service-stability: true + wait-for-service-stability: false + + - name: Check ECS Service Status + run: | + echo "Checking ECS service status..." + aws ecs describe-services \ + --cluster genesis76-us-east-2 \ + --services staging-genesis76-recognizer \ + --query 'services[0].{Status:status,RunningCount:runningCount,PendingCount:pendingCount,DesiredCount:desiredCount}' + + echo "Getting recent ECS events..." + aws ecs describe-services \ + --cluster genesis76-us-east-2 \ + --services staging-genesis76-recognizer \ + --query 'services[0].events[:10]' + + echo "Getting task details..." + TASK_ARN=$(aws ecs list-tasks \ + --cluster genesis76-us-east-2 \ + --service-name staging-genesis76-recognizer \ + --query 'taskArns[0]' --output text) + + if [ "$TASK_ARN" != "None" ]; then + echo "Task ARN: $TASK_ARN" + aws ecs describe-tasks \ + --cluster genesis76-us-east-2 \ + --tasks $TASK_ARN \ + --query 'tasks[0].{LastStatus:lastStatus,HealthStatus:healthStatus,CreatedAt:createdAt,StoppedReason:stoppedReason}' + + echo "Getting container details..." + aws ecs describe-tasks \ + --cluster genesis76-us-east-2 \ + --tasks $TASK_ARN \ + --query 'tasks[0].containers[?name==`app`].{Name:name,LastStatus:lastStatus,ExitCode:exitCode,Reason:reason}' + else + echo "No tasks found" + fi diff --git a/Dockerfile b/Dockerfile index c7eb2b52..b0c969dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,7 +65,6 @@ RUN set -xe; \ FROM debian:11.6-slim as release ARG APP_NAME=recognizer -ARG DOMAIN # These are fed in from the build script ARG VCS_REF @@ -104,8 +103,7 @@ ENV \ MIX_APP="recognizer" \ MIX_ENV="prod" \ SHELL="/bin/bash" \ - LANG=C.UTF-8 \ - DOMAIN="${DOMAIN}" + LANG=C.UTF-8 # Drop down to our unprivileged `recognizer` user USER recognizer diff --git a/config/releases.exs b/config/releases.exs index f9743f90..6e2d6756 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -11,7 +11,7 @@ config :recognizer, hal_token: recognizer_config["HAL_TOKEN"] config :recognizer, RecognizerWeb.Endpoint, - url: [host: System.get_env("DOMAIN")], + url: [host: recognizer_config["DOMAIN"]], secret_key_base: recognizer_config["SECRET_KEY_BASE"] config :recognizer, Recognizer.Repo, From 79259b26a3efffc1402a33f3ece012fe3cc2a7c3 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Wed, 18 Jun 2025 11:17:03 -0600 Subject: [PATCH 06/13] Fix deploy-production.yml --- .github/workflows/deploy-production.yml | 45 +++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 4711e760..33895ba8 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -39,7 +39,6 @@ jobs: --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VCS_REF=$IMAGE_TAG \ --build-arg VERSION=$IMAGE_TAG \ - --build-arg DOMAIN=account.system76.com \ --tag $ECR_REPOSITORY:$IMAGE_TAG \ . @@ -66,8 +65,50 @@ jobs: - name: Deploy uses: aws-actions/amazon-ecs-deploy-task-definition@v2 + timeout-minutes: 15 with: task-definition: ${{ steps.template.outputs.task-definition }} service: production-system76-recognizer cluster: system76-us-east-2 - wait-for-service-stability: true + wait-for-service-stability: false + + - name: Check ECS Service Status + run: | + echo "Checking ECS service status..." + aws ecs describe-services \ + --cluster system76-us-east-2 \ + --services production-system76-recognizer \ + --query 'services[0].{Status:status,RunningCount:runningCount,PendingCount:pendingCount,DesiredCount:desiredCount}' + + echo "Getting recent ECS events..." + aws ecs describe-services \ + --cluster system76-us-east-2 \ + --services production-system76-recognizer \ + --query 'services[0].events[:10]' + + echo "Getting task details..." + if aws ecs list-tasks --cluster system76-us-east-2 --service-name production-system76-recognizer --query 'taskArns[0]' --output text 2>/dev/null; then + TASK_ARN=$(aws ecs list-tasks \ + --cluster system76-us-east-2 \ + --service-name production-system76-recognizer \ + --query 'taskArns[0]' --output text) + + if [ "$TASK_ARN" != "None" ] && [ "$TASK_ARN" != "" ]; then + echo "Task ARN: $TASK_ARN" + aws ecs describe-tasks \ + --cluster system76-us-east-2 \ + --tasks $TASK_ARN \ + --query 'tasks[0].{LastStatus:lastStatus,HealthStatus:healthStatus,CreatedAt:createdAt,StoppedReason:stoppedReason}' 2>/dev/null || echo "Could not get task details" + + echo "Getting container details..." + aws ecs describe-tasks \ + --cluster system76-us-east-2 \ + --tasks $TASK_ARN \ + --query 'tasks[0].containers[?name==`app`].{Name:name,LastStatus:lastStatus,ExitCode:exitCode,Reason:reason}' 2>/dev/null || echo "Could not get container details" + else + echo "No tasks found" + fi + else + echo "Permission denied for ListTasks - checking CloudWatch Logs instead" + echo "Check logs at: https://console.aws.amazon.com/cloudwatch/home?region=us-east-2#logsV2:log-groups/log-group/%2Fecs%2Fproduction-system76-recognizer" + fi From d9fc31ebea7429f5855320c84b1b62f57a53a077 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Wed, 18 Jun 2025 13:35:47 -0600 Subject: [PATCH 07/13] Fix deploy-staging.yml --- config/prod.exs | 1 - config/releases.exs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/config/prod.exs b/config/prod.exs index b5fec7a3..846b8f61 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,7 +1,6 @@ import Config config :recognizer, RecognizerWeb.Endpoint, - url: [scheme: "https", port: 443], http: [port: 8080], cache_static_manifest: "priv/static/cache_manifest.json", gzip: true, diff --git a/config/releases.exs b/config/releases.exs index 6e2d6756..ca60e2b8 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -11,7 +11,7 @@ config :recognizer, hal_token: recognizer_config["HAL_TOKEN"] config :recognizer, RecognizerWeb.Endpoint, - url: [host: recognizer_config["DOMAIN"]], + url: [scheme: "https", port: 443, host: recognizer_config["DOMAIN"]], secret_key_base: recognizer_config["SECRET_KEY_BASE"] config :recognizer, Recognizer.Repo, From 89fdb6f1783482839dcaea86cd5f08080c3296f4 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Wed, 18 Jun 2025 14:06:53 -0600 Subject: [PATCH 08/13] Fix deploy url mismatch --- mix.exs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 46f8d855..38efc90e 100644 --- a/mix.exs +++ b/mix.exs @@ -10,7 +10,8 @@ defmodule Recognizer.MixProject do compilers: Mix.compilers(), start_permanent: Mix.env() == :prod, aliases: aliases(), - deps: deps() + deps: deps(), + releases: releases() ] end @@ -95,4 +96,14 @@ defmodule Recognizer.MixProject do test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"] ] end + + defp releases do + [ + recognizer: [ + validate_compile_env: false, + include_executables_for: [:unix], + applications: [runtime_tools: :permanent] + ] + ] + end end From 8b432e263d950bf7f67c74c07b63c72558ce2037 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Wed, 18 Jun 2025 14:17:43 -0600 Subject: [PATCH 09/13] Fix deploy url mismatch --- config/config.exs | 3 +-- config/dev.exs | 1 + mix.exs | 12 +----------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/config/config.exs b/config/config.exs index f14b764e..28593c9e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -7,8 +7,7 @@ config :recognizer, redis_host: "localhost" config :recognizer, RecognizerWeb.Endpoint, - url: [host: "localhost"], - secret_key_base: "vbOPS+hzz+UAQRtWxIdqiKrcOuWpbLTfocvgvRVDR9P4JRfxtmWZa45H25iKKYoI", + url: [host: "localhost", port: 4000], render_errors: [ view: RecognizerWeb.ErrorView, accepts: ~w(html json), diff --git a/config/dev.exs b/config/dev.exs index 863484c6..fee154db 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -16,6 +16,7 @@ config :recognizer, Recognizer.Repo, config :recognizer, RecognizerWeb.Endpoint, http: [port: 4000], + secret_key_base: "vbOPS+hzz+UAQRtWxIdqiKrcOuWpbLTfocvgvRVDR9P4JRfxtmWZa45H25iKKYoI", debug_errors: true, code_reloader: true, check_origin: false, diff --git a/mix.exs b/mix.exs index 38efc90e..e985064a 100644 --- a/mix.exs +++ b/mix.exs @@ -10,8 +10,7 @@ defmodule Recognizer.MixProject do compilers: Mix.compilers(), start_permanent: Mix.env() == :prod, aliases: aliases(), - deps: deps(), - releases: releases() + deps: deps() ] end @@ -97,13 +96,4 @@ defmodule Recognizer.MixProject do ] end - defp releases do - [ - recognizer: [ - validate_compile_env: false, - include_executables_for: [:unix], - applications: [runtime_tools: :permanent] - ] - ] - end end From 7002d45ce8e9f8ee516ad66806af85997c4cbc1f Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Wed, 18 Jun 2025 14:45:58 -0600 Subject: [PATCH 10/13] Fix deploy url mismatch --- config/config.exs | 3 ++- config/dev.exs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index 28593c9e..f14b764e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -7,7 +7,8 @@ config :recognizer, redis_host: "localhost" config :recognizer, RecognizerWeb.Endpoint, - url: [host: "localhost", port: 4000], + url: [host: "localhost"], + secret_key_base: "vbOPS+hzz+UAQRtWxIdqiKrcOuWpbLTfocvgvRVDR9P4JRfxtmWZa45H25iKKYoI", render_errors: [ view: RecognizerWeb.ErrorView, accepts: ~w(html json), diff --git a/config/dev.exs b/config/dev.exs index fee154db..863484c6 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -16,7 +16,6 @@ config :recognizer, Recognizer.Repo, config :recognizer, RecognizerWeb.Endpoint, http: [port: 4000], - secret_key_base: "vbOPS+hzz+UAQRtWxIdqiKrcOuWpbLTfocvgvRVDR9P4JRfxtmWZa45H25iKKYoI", debug_errors: true, code_reloader: true, check_origin: false, From ebbb28cdf66a16900c16e6a01bf250a9dde66d76 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Mon, 13 Oct 2025 12:39:15 -0600 Subject: [PATCH 11/13] Update Oauth account can't recevice password changing email --- lib/recognizer/accounts.ex | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/recognizer/accounts.ex b/lib/recognizer/accounts.ex index 882410cc..8208bb2b 100644 --- a/lib/recognizer/accounts.ex +++ b/lib/recognizer/accounts.ex @@ -503,6 +503,9 @@ defmodule Recognizer.Accounts do @doc """ Delivers the reset password email to the given user. + For security reasons, if the user account is OAuth-only (no password set), + we silently skip sending the email to prevent account enumeration attacks. + ## Examples iex> deliver_user_reset_password_instructions(user, &Routes.user_reset_password_url(conn, :edit, &1)) @@ -511,12 +514,23 @@ defmodule Recognizer.Accounts do """ def deliver_user_reset_password_instructions(%User{} = user, reset_password_url_fun) when is_function(reset_password_url_fun, 1) do - {:ok, token, _claims} = Guardian.encode_and_sign(user, %{"typ" => "reset_password"}) + # Preload OAuth associations to check if this is an OAuth-only account + user_with_oauths = Repo.preload(user, :oauths) + + if Enum.any?(user_with_oauths.oauths) do + # OAuth account - silently skip sending email for security + # Return success to prevent account enumeration + Logger.info("Password reset requested for OAuth-only account #{user.id}, skipping email") + {:ok, :skipped} + else + # Regular password account - send reset email + {:ok, token, _claims} = Guardian.encode_and_sign(user, %{"typ" => "reset_password"}) - Notification.deliver_reset_password_instructions( - user, - reset_password_url_fun.(token) - ) + Notification.deliver_reset_password_instructions( + user, + reset_password_url_fun.(token) + ) + end end @doc """ From afa950ddea3f97b22fb6a9c61ef7044a4b3106d1 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Mon, 20 Oct 2025 17:08:42 -0600 Subject: [PATCH 12/13] Update Sync BC when user login --- lib/recognizer_web/authentication.ex | 57 ++++++++++++++++++- .../accounts/user_oauth_controller.ex | 3 +- .../accounts/user_session_controller.ex | 4 +- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/lib/recognizer_web/authentication.ex b/lib/recognizer_web/authentication.ex index 5ec98272..92cb0d40 100644 --- a/lib/recognizer_web/authentication.ex +++ b/lib/recognizer_web/authentication.ex @@ -31,7 +31,10 @@ defmodule RecognizerWeb.Authentication do |> put_session(:prompt_user_id, user.id) |> redirect(to: Routes.prompt_two_factor_path(conn, :new)) - {:ok, _user} -> + {:ok, user} -> + # Attempt to sync BigCommerce in background if not already synced + ensure_bigcommerce_user_async(user) + redirect_opts = login_redirect(conn, user) conn @@ -79,6 +82,58 @@ defmodule RecognizerWeb.Authentication do Guardian.Plug.current_resource(conn) end + @doc """ + Sets up two-factor authentication session for a user. + This is used across multiple controllers to maintain consistency. + """ + def put_two_factor_session(conn, user) do + conn + |> put_session(:two_factor_user_id, user.id) + |> put_session(:two_factor_sent, false) + |> put_session(:two_factor_issue_time, System.system_time(:second)) + end + + @doc """ + Ensures BigCommerce user synchronization in the background. + This does not block the login flow if sync fails. + """ + if Mix.env() == :test do + def ensure_bigcommerce_user_async(_user), do: :noop + else + def ensure_bigcommerce_user_async(user) do + unless should_sync_on_login?(), do: :noop + + user = Recognizer.Repo.preload(user, :bigcommerce_user) + + if is_nil(user.bigcommerce_user) do + Task.start(fn -> sync_bigcommerce_customer(user) end) + end + end + end + + defp should_sync_on_login? do + BigCommerce.enabled?() and auto_sync_enabled?() + end + + defp auto_sync_enabled? do + Application.get_env(:recognizer, Recognizer.BigCommerce) + |> Keyword.get(:auto_sync_on_login?, true) + end + + defp sync_bigcommerce_customer(user) do + require Logger + + Logger.info("Attempting BigCommerce sync for user #{user.id} (#{user.email}) during login") + + case BigCommerce.get_or_create_customer(user) do + {:ok, _user} -> + Logger.info("Successfully synced BigCommerce customer for user #{user.id} during login") + + {:error, reason} -> + Logger.warn("Failed to sync BigCommerce customer for user #{user.id} during login: #{inspect(reason)}") + end + end + @doc """ The URL to redirect the user to after authentication is done. """ diff --git a/lib/recognizer_web/controllers/accounts/user_oauth_controller.ex b/lib/recognizer_web/controllers/accounts/user_oauth_controller.ex index 407ab1f7..26461b4d 100644 --- a/lib/recognizer_web/controllers/accounts/user_oauth_controller.ex +++ b/lib/recognizer_web/controllers/accounts/user_oauth_controller.ex @@ -25,8 +25,7 @@ defmodule RecognizerWeb.Accounts.UserOAuthController do {:two_factor, user} -> conn - |> put_session(:two_factor_user_id, user.id) - |> put_session(:two_factor_sent, false) + |> Authentication.put_two_factor_session(user) |> redirect(to: Routes.user_two_factor_path(conn, :new)) {:error, %Ecto.Changeset{} = changeset} -> diff --git a/lib/recognizer_web/controllers/accounts/user_session_controller.ex b/lib/recognizer_web/controllers/accounts/user_session_controller.ex index 51d0777b..17bd3cf3 100644 --- a/lib/recognizer_web/controllers/accounts/user_session_controller.ex +++ b/lib/recognizer_web/controllers/accounts/user_session_controller.ex @@ -17,9 +17,7 @@ defmodule RecognizerWeb.Accounts.UserSessionController do {:two_factor, user} -> conn - |> put_session(:two_factor_user_id, user.id) - |> put_session(:two_factor_sent, false) - |> put_session(:two_factor_issue_time, System.system_time(:second)) + |> Authentication.put_two_factor_session(user) |> redirect(to: Routes.user_two_factor_path(conn, :new)) {:oauth, _user} -> From af5665a2cc650d5695562ac30a89f67e3d12f7d2 Mon Sep 17 00:00:00 2001 From: Yongjin Chong Date: Mon, 20 Oct 2025 17:09:07 -0600 Subject: [PATCH 13/13] Update Sync BC when user login --- lib/recognizer/accounts.ex | 7 ++- lib/recognizer/bigcommerce.ex | 66 +++++++++++++++++++++------- lib/recognizer_web/authentication.ex | 8 ++-- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/lib/recognizer/accounts.ex b/lib/recognizer/accounts.ex index 8208bb2b..4a42e1c8 100644 --- a/lib/recognizer/accounts.ex +++ b/lib/recognizer/accounts.ex @@ -226,7 +226,12 @@ defmodule Recognizer.Accounts do {:error, error} -> # Log the error but continue with account creation - Logger.error("BigCommerce customer creation failed but continuing account process: #{inspect(error)}") + # Auto-sync during login will retry the synchronization + Logger.error( + "[BigCommerce Sync] ✗ REGISTRATION SYNC FAILED for user #{user.id} (#{user.email}) - " <> + "Reason: #{inspect(error)} - Will retry on next login" + ) + {:ok, user} end else diff --git a/lib/recognizer/bigcommerce.ex b/lib/recognizer/bigcommerce.ex index ed085eb8..75a1f61f 100644 --- a/lib/recognizer/bigcommerce.ex +++ b/lib/recognizer/bigcommerce.ex @@ -28,7 +28,9 @@ defmodule Recognizer.BigCommerce do end defp handle_email_already_exists(user) do - Logger.info("BigCommerce customer email already exists for user #{user.id}, attempting to link existing customer") + Logger.warn( + "[BigCommerce Sync] Email already exists in BC for user #{user.id} (#{user.email}), attempting to link existing customer" + ) case Client.get_customers(emails: [user.email]) do {:ok, [customer_id | _]} -> @@ -45,79 +47,111 @@ defmodule Recognizer.BigCommerce do defp link_existing_customer(user, customer_id) do case Repo.insert(%Customer{user_id: user.id, bc_id: customer_id}) do {:ok, _} -> - Logger.info("Successfully linked existing BigCommerce customer #{customer_id} to user #{user.id}") + Logger.warn( + "[BigCommerce Sync] ✓ Successfully linked existing BC customer #{customer_id} to user #{user.id} (#{user.email})" + ) + {:ok, user} {:error, changeset} -> - Logger.error("Failed to link BigCommerce customer to user: #{inspect(changeset)}") + Logger.error( + "[BigCommerce Sync] ✗ FAILED to link BC customer #{customer_id} to user #{user.id} (#{user.email}) - " <> + "Changeset errors: #{inspect(changeset.errors)}" + ) + {:error, {:bigcommerce_link_failed, changeset}} end end defp handle_customer_not_found_error(user) do - Logger.error("BigCommerce reported email exists but customer not found via API for user #{user.id}") + Logger.error( + "[BigCommerce Sync] ✗ CRITICAL: BC reported email exists but customer NOT FOUND via API " <> + "for user #{user.id} (#{user.email}) - Possible BC API inconsistency" + ) + {:error, {:bigcommerce_customer_not_found, "Email exists but customer not found"}} end defp handle_get_customers_error(user, e) do - Logger.error("Failed to get existing BigCommerce customer for user #{user.id}: #{inspect(e)}") + Logger.error( + "[BigCommerce Sync] ✗ BC API ERROR while fetching customer for user #{user.id} (#{user.email}) - " <> + "Error: #{inspect(e)}" + ) + {:error, {:bigcommerce_api_error, e}} end defp handle_new_customer_created(user, bc_id) do case Repo.insert(%Customer{user_id: user.id, bc_id: bc_id}) do {:ok, _} -> + Logger.warn("[BigCommerce Sync] ✓ Created NEW BC customer #{bc_id} for user #{user.id} (#{user.email})") + {:ok, user} {:error, changeset} -> - Logger.error("Failed to save BigCommerce customer ID to database: #{inspect(changeset)}") + Logger.error( + "[BigCommerce Sync] ✗ CRITICAL: BC customer #{bc_id} created but DB LINK FAILED " <> + "for user #{user.id} (#{user.email}) - Changeset: #{inspect(changeset.errors)}" + ) + {:error, {:bigcommerce_link_failed, changeset}} end end defp handle_customer_creation_error(user, e) do - Logger.error("BigCommerce customer creation failed for user #{user.id}: #{inspect(e)}") + Logger.error( + "[BigCommerce Sync] ✗ BC customer creation FAILED for user #{user.id} (#{user.email}) - " <> + "Error: #{inspect(e)}" + ) + {:error, e} end def get_or_create_customer(%{email: email, id: id} = user) do - Logger.info("Starting BigCommerce get_or_create_customer for user #{id} with email #{email}") + Logger.info("[BigCommerce Sync] Starting sync for user #{id} (#{email})") case Client.get_customers(emails: [email]) do {:ok, []} -> - Logger.info("No existing BigCommerce customer found for email #{email}, creating new customer") + Logger.info("[BigCommerce Sync] No existing BC customer found for #{email}, creating new...") result = create_customer(user) - Logger.info("BigCommerce customer creation result: #{inspect(result)}") + Logger.info("[BigCommerce Sync] Creation result: #{inspect(result)}") result {:ok, [customer_id]} -> - Logger.info("Found existing BigCommerce customer #{customer_id} for email #{email}") + Logger.info("[BigCommerce Sync] Found existing BC customer #{customer_id} for #{email}") case Repo.insert(%Customer{user_id: id, bc_id: customer_id}) do {:ok, _customer_db_entry} -> - Logger.info("Successfully linked BigCommerce customer #{customer_id} to user #{id}") + Logger.warn("[BigCommerce Sync] ✓ Linked BC customer #{customer_id} to user #{id} (#{email})") + {:ok, user} {:error, changeset} -> - Logger.error("Error inserting BigCommerce customer into local DB: #{inspect(changeset)}") + Logger.error( + "[BigCommerce Sync] ✗ DB INSERT FAILED for user #{id} (#{email}) - " <> + "Changeset: #{inspect(changeset.errors)}" + ) + # Apply strict approach: fail account creation when DB linking fails {:error, {:bigcommerce_link_failed, changeset}} end {:error, e} -> - Logger.error("Error while getting BigCommerce customer: #{inspect(e)}") + Logger.error("[BigCommerce Sync] ✗ BC API ERROR for user #{id} (#{email}) - Error: #{inspect(e)}") + # Apply strict approach: fail account creation for BigCommerce API errors {:error, e} e -> - Logger.error("Unexpected error while getting or creating BigCommerce customer: #{inspect(e)}") + Logger.error("[BigCommerce Sync] ✗ UNEXPECTED ERROR for user #{id} (#{email}) - Error: #{inspect(e)}") + # Apply strict approach: fail account creation for unexpected errors {:error, "Unexpected BigCommerce error"} end end def get_or_create_customer(e) do - Logger.error("unexpected customer #{inspect(e)}") + Logger.error("[BigCommerce Sync] ✗ INVALID INPUT: #{inspect(e)}") {:error, "unexpected customer"} end diff --git a/lib/recognizer_web/authentication.ex b/lib/recognizer_web/authentication.ex index 92cb0d40..b4c16933 100644 --- a/lib/recognizer_web/authentication.ex +++ b/lib/recognizer_web/authentication.ex @@ -123,14 +123,16 @@ defmodule RecognizerWeb.Authentication do defp sync_bigcommerce_customer(user) do require Logger - Logger.info("Attempting BigCommerce sync for user #{user.id} (#{user.email}) during login") + Logger.warn("[BigCommerce Sync] Attempting auto-sync for user #{user.id} (#{user.email}) during login") case BigCommerce.get_or_create_customer(user) do {:ok, _user} -> - Logger.info("Successfully synced BigCommerce customer for user #{user.id} during login") + Logger.warn("[BigCommerce Sync] ✓ Successfully synced user #{user.id} (#{user.email}) - BC account linked") {:error, reason} -> - Logger.warn("Failed to sync BigCommerce customer for user #{user.id} during login: #{inspect(reason)}") + Logger.error( + "[BigCommerce Sync] ✗ FAILED to sync user #{user.id} (#{user.email}) - Reason: #{inspect(reason)} - USER MAY NOT BE ABLE TO PLACE ORDERS" + ) end end