Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Bezalu.ProjectReporting.API/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,13 @@ Required App Settings:

## Security

- Function auth level currently `Function`; set keys or add front-end auth (e.g., Entra ID) before production.
- Do not send sensitive data inside report payload for PDF endpoint; only project analysis data.
- **Authentication**: Azure Static Web Apps (SWA) with Azure AD integration
- Function endpoints use `AuthorizationLevel.Anonymous` - authentication enforced at SWA layer
- SWA configuration requires `authenticated` role for all `/api/*` routes
- Unauthenticated requests blocked by SWA with 401 redirect to Azure AD login
- Azure Functions trust SWA authentication; no additional function keys required
- **Configuration**: Sensitive data (API keys, credentials) stored in Azure App Settings; use Azure Key Vault references in production
- **Data Handling**: Report payloads contain project analysis data only; no raw credentials transmitted

## Extensibility

Expand Down
51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ A comprehensive project reporting solution that integrates with ConnectWise Mana

## Overview

This solution consists of two main projects:
This solution consists of three main projects:

1. **Bezalu.ProjectReporting.API** - Azure Functions backend API that generates project completion reports
2. **Bezalu.ProjectReporting.Web** - Blazor WebAssembly frontend (not yet implemented)
2. **Bezalu.ProjectReporting.Web** - Blazor WebAssembly frontend for interactive report viewing and PDF export
3. **Bezalu.ProjectReporting.Shared** - Shared DTOs for consistent contracts between API and frontend

## Features

Expand Down Expand Up @@ -90,8 +91,11 @@ Generates a comprehensive project completion report for the specified project ID

### Frontend (Web)

- **Blazor WebAssembly** (to be implemented)
- Will consume the API to display and generate PDF reports
- **Blazor WebAssembly** with Fluent UI components
- Interactive report visualization with tabs for phases and tickets
- AI-generated summary rendered as Markdown using Markdig
- PDF export functionality (posts existing report to API to avoid regeneration)
- Integrated with Azure Static Web Apps for deployment and authentication

## Project Structure

Expand All @@ -106,16 +110,34 @@ CW-ProjectReporting/
│ ├── host.json # Azure Functions host configuration
│ ├── local.settings.json # Local development settings (not in git)
│ └── README.md # Detailed API documentation
└── Bezalu.ProjectReporting.Web/
└── (Blazor WebAssembly project - to be implemented)
├── Bezalu.ProjectReporting.Web/
│ ├── Pages/ # Blazor pages (Home.razor)
│ ├── Layout/ # Layout components
│ ├── wwwroot/ # Static assets and JS helpers
│ ├── staticwebapp.config.json # Azure Static Web Apps configuration
│ └── Program.cs # Blazor WebAssembly startup
└── Bezalu.ProjectReporting.Shared/
└── DTOs/ # Shared data transfer objects
```

## Security

- API keys and credentials stored in configuration (use Azure Key Vault in production)
- `local.settings.json` excluded from source control
- Function-level authorization required for API endpoints
- No vulnerabilities detected by CodeQL security scanning
- **Authentication**: Azure Static Web Apps with Azure AD (Entra ID) integration
- API endpoints use `AuthorizationLevel.Anonymous` at the Function level
- Authentication enforced by Static Web Apps configuration - requires `authenticated` role for `/api/*` routes
- Unauthenticated requests receive 401 and redirect to Azure AD login
- **Configuration**: API keys and credentials stored in Azure App Settings (use Azure Key Vault in production)
- **Source Control**: `local.settings.json` excluded from source control
- **Scanning**: No vulnerabilities detected by CodeQL security scanning

### Authentication Flow

1. User accesses the Blazor WebAssembly app hosted on Azure Static Web Apps
2. Static Web Apps enforces authentication via Azure AD (configured in `staticwebapp.config.json`)
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states "configured in staticwebapp.config.json" but doesn't specify where this file is located. Consider adding the file path (e.g., "configured in Bezalu.ProjectReporting.Web/staticwebapp.config.json") to help readers locate this configuration file.

Suggested change
2. Static Web Apps enforces authentication via Azure AD (configured in `staticwebapp.config.json`)
2. Static Web Apps enforces authentication via Azure AD (configured in `Bezalu.ProjectReporting.Web/staticwebapp.config.json`)

Copilot uses AI. Check for mistakes.
3. Authenticated users receive session cookies from SWA
4. API requests include SWA authentication cookies automatically
5. SWA validates authentication before forwarding requests to Azure Functions
6. Azure Functions trust the SWA authentication layer (no additional function-level auth required)

## Development Notes

Expand All @@ -126,12 +148,13 @@ CW-ProjectReporting/

## Future Enhancements

- Implement Blazor frontend for report visualization
- Add PDF generation capability
- Implement additional report types
- Add caching for improved performance
- Add visual charts for timeline and budget variance trends
- Implement additional report types (financial analysis, resource utilization)
- Add caching layer for ConnectWise API responses
- Add unit and integration tests
- Implement batch report generation
- Add Excel export capability
- Implement browser-side caching (localStorage) for recent reports

## Documentation

Expand Down
31 changes: 22 additions & 9 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ The solution consists of three projects:
- Bezalu.ProjectReporting.Shared (DTO contracts shared by both)

## Data Flow
1. User enters Project ID in Web UI.
2. Front end POSTs projectId to `/api/reports/project-completion`.
3. API fetches project, phases, tickets, notes from ConnectWise.
4. API builds `ProjectCompletionReportResponse` and invokes Azure OpenAI to produce `AiGeneratedSummary`.
5. JSON returned to client; client renders summary markdown using Markdig.
6. User initiates PDF download; front end POSTs the full report JSON to `/api/reports/project-completion/pdf`.
7. API composes PDF using QuestPDF with supplied data (skip re-fetch & AI).
8. Client receives PDF bytes and triggers browser download via JS interop.
1. User accesses Blazor WebAssembly app via Azure Static Web Apps.
2. Static Web Apps enforces authentication via Azure AD (Entra ID).
3. User enters Project ID in Web UI.
4. Front end POSTs projectId to `/api/reports/project-completion` (with SWA auth cookies).
5. SWA validates authentication and forwards request to Azure Functions.
6. API fetches project, phases, tickets, notes from ConnectWise.
7. API builds `ProjectCompletionReportResponse` and invokes Azure OpenAI to produce `AiGeneratedSummary`.
8. JSON returned to client; client renders summary markdown using Markdig.
9. User initiates PDF download; front end POSTs the full report JSON to `/api/reports/project-completion/pdf`.
10. API composes PDF using QuestPDF with supplied data (skip re-fetch & AI).
11. Client receives PDF bytes and triggers browser download via JS interop.

## Key Services
- `IConnectWiseApiClient`: wraps HTTP calls to ConnectWise endpoints.
Expand All @@ -26,8 +29,18 @@ The solution consists of three projects:
- POST for PDF avoids second expensive aggregation call.
- Markdown + Markdig chosen for flexibility in AI summary formatting.
- QuestPDF chosen for deterministic server-side PDF rendering.
- **Azure Static Web Apps** provides integrated hosting, authentication, and API routing.
- **Function AuthorizationLevel.Anonymous** used because authentication is enforced upstream by SWA.
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase "Function AuthorizationLevel.Anonymous" reads awkwardly. Consider rewording to "Azure Functions use AuthorizationLevel.Anonymous" for better clarity and readability.

Suggested change
- **Function AuthorizationLevel.Anonymous** used because authentication is enforced upstream by SWA.
- **Azure Functions use AuthorizationLevel.Anonymous** because authentication is enforced upstream by SWA.

Copilot uses AI. Check for mistakes.

## Authentication Architecture
- **Static Web Apps** handles authentication via Azure AD (Entra ID)
- `staticwebapp.config.json` enforces `authenticated` role for `/api/*` routes
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying the full path to the configuration file (e.g., "Bezalu.ProjectReporting.Web/staticwebapp.config.json") to help readers locate this file in the project structure.

Suggested change
- `staticwebapp.config.json` enforces `authenticated` role for `/api/*` routes
- `Bezalu.ProjectReporting.Web/staticwebapp.config.json` enforces `authenticated` role for `/api/*` routes

Copilot uses AI. Check for mistakes.
- Unauthenticated users redirected to `/.auth/login/aad`
- SWA session cookies automatically included in API requests
- Azure Functions trust SWA authentication layer - no function keys needed

## Future Enhancements
- Caching of raw ConnectWise responses.
- Additional export formats (Excel, HTML full report).
- Authentication (OIDC) integration for user-level access.
- User-level access control based on ConnectWise permissions.
- Visual charts for variance trends and project analytics.
25 changes: 21 additions & 4 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,38 @@

## Azure Static Web Apps + Azure Functions
- Front end (Blazor WASM) deployed to Static Web Apps.
- API (Azure Functions) either integrated (api folder) or separate Functions App.
- API (Azure Functions) integrated via SWA's managed Functions (api folder) or linked to separate Functions App.
- **Authentication** handled by Static Web Apps with Azure AD integration.

## Steps (Integrated Deployment)
1. Create Azure Static Web App resource in Azure Portal.
2. Configure Azure AD authentication provider in SWA settings.
3. Set `api_location` to point to Azure Functions project folder during SWA build.
4. Configure Application Settings for ConnectWise + Azure OpenAI in SWA or linked Functions App.
5. Deploy via GitHub Actions (auto-configured by SWA) or manual deployment:
- Front-end: `dotnet publish -c Release Bezalu.ProjectReporting.Web`
- API: Functions automatically deployed with SWA or via `func azure functionapp publish <name>`

## Steps (Separate Functions App)
1. Create Azure Functions App (Isolated .NET runtime) and configure Application Settings for ConnectWise + Azure OpenAI.
2. Deploy API project via `func azure functionapp publish <name>` or CI.
3. Create Static Web App and set `api_location` to Functions App if integrated, else configure front end to call external API base URL.
4. Upload front-end build (`dotnet publish -c Release Bezalu.ProjectReporting.Web`).
3. Create Static Web App and link to external Functions App or configure front end to call external API base URL.
4. Configure Azure AD authentication in Static Web Apps settings.
5. Upload front-end build output to SWA.

## Configuration Keys
- `ConnectWise:*`
- `AzureOpenAI:Endpoint`
- `AzureOpenAI:DeploymentName`

## Authentication
- Add Entra ID or other auth on Static Web Apps; issue front-end access token; secure Functions with Easy Auth or custom.
- **Azure Static Web Apps** integrated with **Azure AD (Entra ID)** for authentication
- Configure authentication provider in Azure Portal under SWA > Settings > Authentication
- `staticwebapp.config.json` enforces authentication:
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying the full path to the configuration file (e.g., "Bezalu.ProjectReporting.Web/staticwebapp.config.json") to help readers locate this file in the project structure.

Suggested change
- `staticwebapp.config.json` enforces authentication:
- `Bezalu.ProjectReporting.Web/staticwebapp.config.json` enforces authentication:

Copilot uses AI. Check for mistakes.
- `/api/*` routes require `authenticated` role
- Unauthenticated users redirected to `/.auth/login/aad`
- No function keys needed - Azure Functions use `AuthorizationLevel.Anonymous` and trust SWA authentication
- Session managed by SWA; cookies automatically included in API requests

## Environment Segregation
- Use separate resource groups for dev/stage/prod.
Expand Down
17 changes: 16 additions & 1 deletion docs/frontend.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
# Front-End (Blazor WebAssembly)

## Overview
The front-end is a Blazor WebAssembly application using Microsoft Fluent UI components, deployed on Azure Static Web Apps with integrated Azure AD authentication.

## Key Behaviors
- User must authenticate via Azure AD before accessing the application.
- User enters Project ID; triggers report fetch.
- Displays metrics in Fluent UI components (cards, tabs, data grids, accordion).
- AI summary rendered as markdown using Markdig.
- PDF download posts existing report JSON to API; no recomputation.

## Authentication
- **Azure Static Web Apps** handles authentication via Azure AD (Entra ID)
- Configured in `staticwebapp.config.json`:
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider specifying the full path to the configuration file (e.g., "Configured in Bezalu.ProjectReporting.Web/staticwebapp.config.json") to help readers locate this file in the project structure.

Suggested change
- Configured in `staticwebapp.config.json`:
- Configured in `Bezalu.ProjectReporting.Web/staticwebapp.config.json`:

Copilot uses AI. Check for mistakes.
- `/api/*` routes require `authenticated` role
- Unauthenticated users redirected to `/.auth/login/aad`
- SWA session cookies automatically included in API requests
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent terminology: This line refers to "SWA session cookies" while line 28 refers to "SWA authentication cookies". Consider using consistent terminology throughout the documentation to refer to the same concept.

Suggested change
- SWA session cookies automatically included in API requests
- SWA authentication cookies automatically included in API requests

Copilot uses AI. Check for mistakes.
- No additional token management needed in Blazor code

## State Management
- Local component state only (no global store yet).
- `IsLoading` for initial report, `IsPdfLoading` for PDF call.

## HTTP
- `HttpClient` base address from host environment; assumes reverse proxy or relative `/api` route.
- `HttpClient` base address from host environment.
- Assumes SWA reverse proxy routing for `/api` routes.
- SWA authentication cookies automatically included in requests.
- No manual authorization headers needed.

## File Download
- `saveFile` JS helper converts Base64 to Blob and triggers `<a download>`.
Expand Down
Loading