cover/Elixir.DaProductApp.Settings.html

1 defmodule DaProductApp.Settings do
2 @moduledoc """
3 Settings context for platform configuration management.
4
5 This context handles platform-wide configuration settings across
6 different categories including general, UPI, international payments,
7 security, notifications, integrations, and system settings.
8
9 Supports gradual migration from hardcoded to database-backed settings.
10 """
11
12 import Ecto.Query, warn: false
13 alias DaProductApp.Repo
14 alias DaProductApp.Settings.PlatformSetting
15 alias DaProductApp.FeatureFlags
16
17 @doc """
18 Gets all platform settings organized by category.
19 Uses feature flags to determine data source.
20 """
21 def get_all_settings do
22
:-(
if FeatureFlags.enabled?(:use_database_settings) do
23
:-(
get_database_settings()
24 else
25
:-(
get_hardcoded_settings()
26 end
27 end
28
29 @doc """
30 Gets settings for a specific category.
31 """
32 def get_settings_by_category(category) do
33
:-(
if FeatureFlags.enabled?(:use_database_settings) do
34
:-(
get_database_settings_by_category(category)
35 else
36
:-(
all_settings = get_hardcoded_settings()
37
:-(
Map.get(all_settings, category, %{})
38 end
39 end
40
41 @doc """
42 Updates settings for a specific category.
43 """
44 def update_settings(category, updates) when is_map(updates) do
45
:-(
if FeatureFlags.enabled?(:use_database_settings) do
46
:-(
update_database_settings(category, updates)
47 else
48 # In hardcoded mode, simulate success but don't persist
49 {:ok, updates}
50 end
51 end
52
53 @doc """
54 Updates a single setting.
55 """
56
:-(
def update_setting(category, key, value, opts \\ []) do
57
:-(
value_type = Keyword.get(opts, :type, "string")
58
:-(
description = Keyword.get(opts, :description)
59
60
:-(
if FeatureFlags.enabled?(:use_database_settings) do
61
:-(
update_database_setting(category, key, value, value_type, description)
62 else
63 # In hardcoded mode, simulate success
64 {:ok, %{category: category, key: key, value: value}}
65 end
66 end
67
68 @doc """
69 Gets a single setting value with optional default.
70 """
71
:-(
def get_setting(category, key, default \\ nil) do
72
:-(
if FeatureFlags.enabled?(:use_database_settings) do
73
:-(
get_database_setting(category, key, default)
74 else
75
:-(
settings = get_settings_by_category(category)
76
:-(
Map.get(settings, key, default)
77 end
78 end
79
80 @doc """
81 Validates a setting value.
82 """
83 def validate_setting(category, key, value) do
84
:-(
case {category, key} do
85
:-(
{"general", "max_transaction_amount"} when is_number(value) and value > 0 -> {:ok, value}
86
:-(
{"general", "min_transaction_amount"} when is_number(value) and value > 0 -> {:ok, value}
87
:-(
{"upi", "npci_timeout"} when is_integer(value) and value > 0 -> {:ok, value}
88
:-(
{"upi", "retry_attempts"} when is_integer(value) and value >= 0 -> {:ok, value}
89
:-(
{"security", "session_timeout"} when is_integer(value) and value > 0 -> {:ok, value}
90
:-(
{"security", "max_login_attempts"} when is_integer(value) and value > 0 -> {:ok, value}
91
:-(
_ -> {:ok, value} # Default acceptance for demo
92 end
93 end
94
95 # Private functions for different setting categories
96
97 defp get_general_settings do
98
:-(
%{
99 "platform_name" => "Mercury UPI PSP",
100 "platform_description" => "Advanced UPI Payment Service Provider",
101 "default_timezone" => "Asia/Kolkata",
102 "default_currency" => "INR",
103 "maintenance_mode" => false,
104 "debug_mode" => false,
105 "max_transaction_amount" => 200000.00,
106 "min_transaction_amount" => 1.00
107 }
108 end
109
110 defp get_upi_settings do
111
:-(
%{
112 "npci_endpoint" => "https://api.npci.org.in/v1",
113 "npci_timeout" => 30,
114 "retry_attempts" => 3,
115 "retry_delay" => 5,
116 "qr_expiry_minutes" => 15,
117 "max_qr_per_merchant" => 100,
118 "validate_vpa_real_time" => true,
119 "allow_zero_amount_qr" => true,
120 "mandate_beneficiary_validation" => true
121 }
122 end
123
124 defp get_international_settings do
125
:-(
%{
126 "enable_international_payments" => true,
127 "default_fx_provider" => "reuters",
128 "fx_rate_refresh_interval" => 300,
129 "fx_rate_tolerance" => 0.05,
130 "supported_corridors" => ["SGD-INR", "USD-INR", "AED-INR"],
131 "max_international_amount" => 500000.00,
132 "compliance_check_required" => true,
133 "auto_settlement_enabled" => false
134 }
135 end
136
137 defp get_security_settings do
138
:-(
%{
139 "encryption_enabled" => true,
140 "encryption_algorithm" => "AES-256-GCM",
141 "session_timeout" => 30,
142 "max_login_attempts" => 5,
143 "lockout_duration" => 15,
144 "require_2fa" => false,
145 "ip_whitelist_enabled" => false,
146 "audit_logging_enabled" => true,
147 "sensitive_data_masking" => true
148 }
149 end
150
151 defp get_notification_settings do
152
:-(
%{
153 "email_enabled" => true,
154 "sms_enabled" => true,
155 "webhook_enabled" => true,
156 "push_notifications_enabled" => false,
157 "transaction_alerts" => true,
158 "failure_alerts" => true,
159 "system_alerts" => true,
160 "daily_reports" => true,
161 "alert_threshold_amount" => 50000.00
162 }
163 end
164
165 defp get_integration_settings do
166
:-(
%{
167 "razorpay_enabled" => false,
168 "razorpay_api_key" => "",
169 "razorpay_webhook_secret" => "",
170 "paytm_enabled" => true,
171 "paytm_merchant_id" => "PAYTM_MERCHANT_001",
172 "paytm_api_key" => "",
173 "phonepe_enabled" => true,
174 "phonepe_merchant_id" => "PHONEPE_MERCHANT_001",
175 "phonepe_api_key" => ""
176 }
177 end
178
179 defp get_system_settings do
180
:-(
%{
181 "max_concurrent_transactions" => 1000,
182 "queue_size_limit" => 10000,
183 "worker_pool_size" => 50,
184 "database_pool_size" => 20,
185 "cache_ttl_seconds" => 3600,
186 "log_level" => "info",
187 "metrics_enabled" => true,
188 "health_check_interval" => 60,
189 "live_reload_enabled" => true,
190 "performance_monitoring" => false,
191 "error_tracking_enabled" => true,
192 "backup_enabled" => true,
193 "backup_frequency" => "daily",
194 "log_retention_days" => 90,
195 "metrics_retention_days" => 30
196 }
197 end
198
199 # ================================
200 # DATABASE-BACKED SETTINGS
201 # ================================
202
203 defp get_database_settings do
204
:-(
settings =
205 from(s in PlatformSetting, where: s.is_active == true)
206 |> Repo.all()
207
:-(
|> Enum.group_by(& &1.category)
208
:-(
|> Enum.into(%{}, fn {category, settings} ->
209
:-(
settings_map =
210 settings
211
:-(
|> Enum.into(%{}, fn setting ->
212
:-(
{setting.key, PlatformSetting.parse_value(setting)}
213 end)
214 {category, settings_map}
215 end)
216
217 # Merge with defaults for any missing categories
218
:-(
Map.merge(get_hardcoded_settings(), settings)
219 end
220
221 defp get_database_settings_by_category(category) do
222 from(s in PlatformSetting,
223 where: s.category == ^category and s.is_active == true)
224 |> Repo.all()
225
:-(
|> Enum.into(%{}, fn setting ->
226
:-(
{setting.key, PlatformSetting.parse_value(setting)}
227 end)
228 end
229
230 defp get_database_setting(category, key, default) do
231
:-(
case Repo.get_by(PlatformSetting, category: category, key: key, is_active: true) do
232
:-(
nil -> default
233
:-(
setting -> PlatformSetting.parse_value(setting)
234 end
235 end
236
237 defp update_database_settings(category, updates) do
238
:-(
results =
239
:-(
for {key, value} <- updates do
240 update_database_setting(category, key, value, "string", nil)
241 end
242
243
:-(
case Enum.find(results, &match?({:error, _}, &1)) do
244
:-(
nil -> {:ok, updates}
245
:-(
error -> error
246 end
247 end
248
249 defp update_database_setting(category, key, value, value_type, description) do
250
:-(
case Repo.get_by(PlatformSetting, category: category, key: key) do
251 nil ->
252 # Create new setting
253 %PlatformSetting{}
254 |> PlatformSetting.changeset(%{
255 category: category,
256 key: key,
257 value: PlatformSetting.encode_value(value, value_type),
258 value_type: value_type,
259 description: description,
260 is_active: true
261 })
262
:-(
|> Repo.insert()
263
264 existing ->
265 # Update existing setting
266 existing
267 |> PlatformSetting.changeset(%{
268 value: PlatformSetting.encode_value(value, value_type),
269 value_type: value_type,
270
:-(
description: description || existing.description,
271 is_active: true
272 })
273
:-(
|> Repo.update()
274 end
275 end
276
277 defp get_hardcoded_settings do
278
:-(
%{
279 "general" => get_general_settings(),
280 "upi" => get_upi_settings(),
281 "international" => get_international_settings(),
282 "security" => get_security_settings(),
283 "notifications" => get_notification_settings(),
284 "integrations" => get_integration_settings(),
285 "system" => get_system_settings()
286 }
287 end
288 end
Line Hits Source