1- // Add registrations (only the new lines shown; keep existing ones)
1+ using System . Text ;
2+ using System . Text . Json ;
3+ using System . Text . Json . Serialization ;
24using eAuthor . Services ;
5+ using eAuthor . Services . Expressions ;
6+ using Microsoft . AspNetCore . Authentication . JwtBearer ;
7+ using Microsoft . AspNetCore . Http . HttpResults ;
8+ using Microsoft . AspNetCore . Mvc ;
9+ using Microsoft . Extensions . Options ;
10+ using Microsoft . IdentityModel . Tokens ;
311
4- builder . Services . AddScoped < StyleRenderer > ( ) ;
5- builder . Services . AddScoped < DynamicDocxBuilderService > ( ) ;
6- builder . Services . AddScoped < HtmlToDynamicConverter > ( ) ;
12+ // using WordTemplating.Core.Repositories; // Uncomment when repository interfaces are present
13+ // using WordTemplating.Core.Background; // Uncomment if you have a batch worker hosted service
14+
15+ // NOTE: This Program.cs is a consolidated "final" version aligned with the documented architecture.
16+ // Adjust namespaces to match your actual project structure if they differ.
17+
18+ var builder = WebApplication . CreateBuilder ( args ) ;
19+
20+ // ------------------------------------------------------------
21+ // Configuration & Constants
22+ // ------------------------------------------------------------
23+ var jwtKey = builder . Configuration [ "Jwt:Key" ] ?? "DEV_INSECURE_KEY_CHANGE_ME" ;
24+ var allowAnyCors = builder . Configuration . GetValue ( "Cors:AllowAny" , true ) ;
25+
26+ // ------------------------------------------------------------
27+ // JSON Options
28+ // ------------------------------------------------------------
29+ builder . Services
30+ . AddControllers ( )
31+ . AddJsonOptions ( o =>
32+ {
33+ o . JsonSerializerOptions . PropertyNamingPolicy = JsonNamingPolicy . CamelCase ;
34+ o . JsonSerializerOptions . DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ;
35+ o . JsonSerializerOptions . Converters . Add ( new JsonStringEnumConverter ( ) ) ;
36+ } ) ;
37+
38+ // Return problem details for model validation errors
39+ builder . Services . Configure < ApiBehaviorOptions > ( opt =>
40+ {
41+ opt . InvalidModelStateResponseFactory = ctx =>
42+ {
43+ var problem = new ValidationProblemDetails ( ctx . ModelState )
44+ {
45+ Status = StatusCodes . Status400BadRequest ,
46+ Title = "Request validation failed" ,
47+ } ;
48+ return new BadRequestObjectResult ( problem ) ;
49+ } ;
50+ } ) ;
51+
52+ // ------------------------------------------------------------
53+ // CORS
54+ // ------------------------------------------------------------
55+ builder . Services . AddCors ( opt =>
56+ {
57+ if ( allowAnyCors )
58+ {
59+ opt . AddPolicy ( "DevOpen" ,
60+ p => p . AllowAnyOrigin ( )
61+ . AllowAnyHeader ( )
62+ . AllowAnyMethod ( ) ) ;
63+ }
64+ else
65+ {
66+ opt . AddPolicy ( "Restricted" , p =>
67+ {
68+ var origins = builder . Configuration . GetSection ( "Cors:Origins" ) . Get < string [ ] > ( ) ?? Array . Empty < string > ( ) ;
69+ p . WithOrigins ( origins )
70+ . AllowAnyHeader ( )
71+ . AllowAnyMethod ( ) ;
72+ } ) ;
73+ }
74+ } ) ;
75+
76+ // ------------------------------------------------------------
77+ // Authentication (JWT)
78+ // ------------------------------------------------------------
79+ var signingKey = new SymmetricSecurityKey ( Encoding . UTF8 . GetBytes ( jwtKey ) ) ;
80+ builder . Services
81+ . AddAuthentication ( JwtBearerDefaults . AuthenticationScheme )
82+ . AddJwtBearer ( opt =>
83+ {
84+ opt . RequireHttpsMetadata = false ;
85+ opt . TokenValidationParameters = new TokenValidationParameters
86+ {
87+ ValidateIssuer = false ,
88+ ValidateAudience = false ,
89+ ValidateIssuerSigningKey = true ,
90+ IssuerSigningKey = signingKey ,
91+ ValidateLifetime = true ,
92+ ClockSkew = TimeSpan . FromMinutes ( 2 )
93+ } ;
94+ } ) ;
95+
96+ // ------------------------------------------------------------
97+ // Authorization (basic – extend with policies/roles as needed)
98+ // ------------------------------------------------------------
99+ builder . Services . AddAuthorization ( ) ;
100+
101+ // ------------------------------------------------------------
102+ // Swagger / OpenAPI
103+ // ------------------------------------------------------------
104+ builder . Services . AddEndpointsApiExplorer ( ) ;
105+ builder . Services . AddSwaggerGen ( cfg =>
106+ {
107+ cfg . SwaggerDoc ( "v1" , new ( )
108+ {
109+ Title = "Word Templating API" ,
110+ Version = "v1" ,
111+ Description = "Dynamic DOCX generation & templating platform"
112+ } ) ;
113+
114+ // JWT header
115+ cfg . AddSecurityDefinition ( "Bearer" , new ( )
116+ {
117+ In = Microsoft . OpenApi . Models . ParameterLocation . Header ,
118+ Description = "JWT Authorization header using the Bearer scheme. Example: \" Bearer {token}\" " ,
119+ Name = "Authorization" ,
120+ Type = Microsoft . OpenApi . Models . SecuritySchemeType . ApiKey
121+ } ) ;
122+ cfg . AddSecurityRequirement ( new ( )
123+ {
124+ {
125+ new ( ) { Reference = new ( ) { Id = "Bearer" , Type = Microsoft . OpenApi . Models . ReferenceType . SecurityScheme } } ,
126+ Array . Empty < string > ( )
127+ }
128+ } ) ;
129+ } ) ;
130+
131+ // ------------------------------------------------------------
132+ // Core / Domain Services Registration
133+ // ------------------------------------------------------------
134+
135+ // Expression + Conditional system
136+ builder . Services . AddSingleton < IExpressionParser , ExpressionParser > ( ) ;
137+ builder . Services . AddSingleton < IExpressionEvaluator , ExpressionEvaluator > ( ) ;
138+ builder . Services . AddSingleton < IConditionalBlockProcessor , ConditionalBlockProcessor > ( ) ;
139+
140+ // Rendering / DOCX build
141+ builder . Services . AddSingleton < StyleRenderer > ( ) ;
142+ builder . Services . AddSingleton < DynamicDocxBuilderService > ( ) ;
143+
144+ // If you have repeater / conditional processors beyond the interface above, register them here.
145+ // builder.Services.AddSingleton<IRepeaterBlockProcessor, RepeaterBlockProcessor>();
146+
147+ // Repositories & Data Access: register your custom implementations (uncomment & adjust):
148+ // builder.Services.AddScoped<ITemplateRepository, TemplateRepository>();
149+ // builder.Services.AddScoped<IXsdRepository, XsdRepository>();
150+ // builder.Services.AddScoped<IDocumentJobRepository, DocumentJobRepository>();
151+ // builder.Services.AddScoped<IBaseDocxTemplateRepository, BaseDocxTemplateRepository>();
152+
153+ // Batch job queue / worker (uncomment when implemented):
154+ // builder.Services.AddSingleton<IDocumentJobQueue, InMemoryDocumentJobQueue>();
155+ // builder.Services.AddHostedService<BatchJobWorker>();
156+
157+ // ------------------------------------------------------------
158+ // Health & Monitoring
159+ // ------------------------------------------------------------
160+ builder . Services . AddHealthChecks ( ) ;
161+
162+ // ------------------------------------------------------------
163+ // Build App
164+ // ------------------------------------------------------------
165+ var app = builder . Build ( ) ;
166+
167+ // ------------------------------------------------------------
168+ // Middleware Pipeline
169+ // ------------------------------------------------------------
170+ if ( app . Environment . IsDevelopment ( ) )
171+ {
172+ app . UseSwagger ( ) ;
173+ app . UseSwaggerUI ( ) ;
174+ }
175+
176+ // Security headers (basic – extend as needed)
177+ app . Use ( async ( ctx , next ) =>
178+ {
179+ ctx . Response . Headers . TryAdd ( "X-Content-Type-Options" , "nosniff" ) ;
180+ ctx . Response . Headers . TryAdd ( "X-Frame-Options" , "DENY" ) ;
181+ ctx . Response . Headers . TryAdd ( "X-XSS-Protection" , "1; mode=block" ) ;
182+ await next ( ) ;
183+ } ) ;
184+
185+ // Global exception handling (lightweight)
186+ app . UseExceptionHandler ( errorApp =>
187+ {
188+ errorApp . Run ( async context =>
189+ {
190+ context . Response . StatusCode = StatusCodes . Status500InternalServerError ;
191+ context . Response . ContentType = "application/json" ;
192+ var problem = new
193+ {
194+ error = "Unhandled server error" ,
195+ traceId = context . TraceIdentifier
196+ } ;
197+ await context . Response . WriteAsJsonAsync ( problem ) ;
198+ } ) ;
199+ } ) ;
200+
201+ app . UseCors ( allowAnyCors ? "DevOpen" : "Restricted" ) ;
202+ app . UseAuthentication ( ) ;
203+ app . UseAuthorization ( ) ;
204+
205+ // Map controllers
206+ app . MapControllers ( ) ;
207+
208+ // Minimal health endpoint(s)
209+ app . MapHealthChecks ( "/health" ) ;
210+ app . MapGet ( "/ready" , ( ) => Results . Ok ( new { status = "ready" } ) ) ;
211+
212+ // Example minimal endpoint (can remove if all logic is via controllers)
213+ app . MapGet ( "/" , ( ) => Results . Ok ( new
214+ {
215+ name = "Word Templating & Dynamic DOCX Platform" ,
216+ version = typeof ( Program ) . Assembly . GetName ( ) . Version ? . ToString ( ) ?? "dev"
217+ } ) ) ;
218+
219+ // ------------------------------------------------------------
220+ // Start
221+ // ------------------------------------------------------------
222+ app . Run ( ) ;
223+
224+ /// <summary>
225+ /// Dummy Program class to satisfy certain hosting scenarios / tests.
226+ /// </summary>
227+ public partial class Program
228+ {
229+ // Intentionally left blank – used for WebApplicationFactory in integration tests.
230+ }
0 commit comments