-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
145 lines (119 loc) · 7.04 KB
/
Copy pathProgram.cs
File metadata and controls
145 lines (119 loc) · 7.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using CloudZen;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
// Feature service registrations
using CloudZen.Features.Booking;
using CloudZen.Features.Booking.Services;
using CloudZen.Features.Contact;
using CloudZen.Features.Contact.Services;
using CloudZen.Features.Chat;
using CloudZen.Features.Chat.Services;
using CloudZen.Features.Landing.Services;
using CloudZen.Features.Profile.Services;
using CloudZen.Features.Projects.Services;
using CloudZen.Features.Tickets.Services;
using CloudZen.Common.Options;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
// =============================================================================
// LOCAL DEVELOPMENT: API URL OVERRIDE
// =============================================================================
// In production (Azure Static Web Apps), "/api" routes are proxied to the linked
// Azure Functions app automatically. In local development, the Blazor app and the
// Functions API run on separate ports, so API calls must use the absolute URL.
//
// NOTE: appsettings.Development.json can silently fail to load in Blazor WASM
// (the client fetches it via HTTP and skips it on any error). This programmatic
// override is more reliable for local development.
// =============================================================================
if (builder.HostEnvironment.IsDevelopment())
{
const string functionsLocalUrl = "http://localhost:7257/api"; // update with your local Functions URL and port
builder.Configuration["ChatbotService:ApiBaseUrl"] = functionsLocalUrl;
builder.Configuration["EmailService:ApiBaseUrl"] = functionsLocalUrl;
builder.Configuration["BookingService:ApiBaseUrl"] = functionsLocalUrl;
}
// =============================================================================
// CONFIGURATION WITH IOPTIONS PATTERN
// =============================================================================
// The IOptions pattern provides:
// - Strongly-typed access to configuration settings
// - Compile-time checking of configuration access
// - Easy unit testing through mocking
// - Support for configuration validation
//
// Note: Blazor WebAssembly uses BindConfiguration() extension method
// =============================================================================
// Configure Email Service options from appsettings.json
// Section: "EmailService"
builder.Services.AddOptions<EmailServiceOptions>()
.BindConfiguration(EmailServiceOptions.SectionName);
// Configure Blob Storage options from appsettings.json
// Section: "BlobStorage"
builder.Services.AddOptions<BlobStorageOptions>()
.BindConfiguration(BlobStorageOptions.SectionName);
// Configure Chatbot Service options from appsettings.json
// Section: "ChatbotService"
builder.Services.AddOptions<ChatbotOptions>()
.BindConfiguration(ChatbotOptions.SectionName);
// Configure Booking Service options from appsettings.json
// Section: "BookingService"
builder.Services.AddOptions<BookingServiceOptions>()
.BindConfiguration(BookingServiceOptions.SectionName);
// =============================================================================
// HTTP CLIENT REGISTRATION
// =============================================================================
// NOTE: Each service below gets its OWN HttpClient via AddHttpClient<T> (IHttpClientFactory).
// Do NOT register a single shared HttpClient instance here - ApiEmailService, ChatbotService,
// and AppointmentService each set httpClient.Timeout in their constructor, and HttpClient throws
// InvalidOperationException ("net_http_operation_started") if Timeout is set after any request
// has been sent on that instance. A shared instance crashes as soon as a second service is
// constructed after the first has made a request.
// =============================================================================
// SECURITY NOTES FOR BLAZOR WEBASSEMBLY
// =============================================================================
// - Blazor WebAssembly cannot use DefaultAzureCredential() - requires secure server environment
// - Cannot access secrets.json directly - use Azure Key Vault via backend API
// - All configuration in wwwroot/appsettings.json is publicly accessible
// - NEVER store API keys, connection strings, or secrets in client-side config
// - Use Azure Functions backend for sensitive operations (email, storage writes)
// =============================================================================
// =============================================================================
// SERVICE REGISTRATIONS
// =============================================================================
// Register GoogleCalendarUrlService
builder.Services.AddScoped<IGoogleCalendarUrlService, GoogleCalendarUrlService>();
// Register BookingService for calendar logic, date availability, and formatting
builder.Services.AddScoped<IBookingService, BookingService>();
// Register AppointmentService for n8n webhook appointment booking
builder.Services.AddHttpClient<IAppointmentService, AppointmentService>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Register TicketService as the implementation for ITicketService
builder.Services.AddScoped<ITicketService, TicketService>();
// Register ResumeService (uses IOptions<BlobStorageOptions> for configuration)
builder.Services.AddHttpClient<ResumeService>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Register ApiEmailService as the implementation for IEmailService
// Uses IOptions<EmailServiceOptions> for configuration
// This sends emails through the Azure Functions API backend (secure for WebAssembly)
builder.Services.AddHttpClient<IEmailService, ApiEmailService>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Register ChatbotService as the implementation for IChatbotService
// Uses IOptions<ChatbotOptions> for configuration
// This sends chat messages through the Azure Functions API backend (API key stays server-side)
builder.Services.AddHttpClient<IChatbotService, ChatbotService>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Register ProjectService for managing portfolio projects
builder.Services.AddScoped<IProjectService, ProjectService>();
// Register ServiceOfferingsService for managing professional service offerings
builder.Services.AddScoped<IServiceOfferingsService, ServiceOfferingsService>();
// Register ToolService for the Tools Overview section
builder.Services.AddScoped<IToolService, ToolService>();
// Register FeatureHighlightService for the Features Showcase section
builder.Services.AddScoped<IFeatureHighlightService, FeatureHighlightService>();
// Register MissionService for the About Us / Mission / Standards section
builder.Services.AddScoped<IMissionService, MissionService>();
// Register CaseStudyService for case study text transformations
builder.Services.AddScoped<ICaseStudyService, CaseStudyService>();
await builder.Build().RunAsync();