|
100 | 100 |
|
101 | 101 | var app = builder.Build(); |
102 | 102 |
|
| 103 | +var nextServerBase = app.Environment.IsDevelopment() |
| 104 | + ? new Uri("http://localhost:3000") |
| 105 | + : new Uri("http://127.0.0.1:3000"); |
| 106 | + |
| 107 | +var allowInvalidCertsForNext = false; // No HTTPS when proxying to Next internally |
| 108 | + |
| 109 | +HttpMessageHandler nextHandler = allowInvalidCertsForNext |
| 110 | + ? new HttpClientHandler |
| 111 | + { |
| 112 | + ServerCertificateCustomValidationCallback = |
| 113 | + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator |
| 114 | + } |
| 115 | + : new HttpClientHandler(); |
| 116 | + |
| 117 | +var nextClient = new HttpClient(nextHandler) |
| 118 | +{ |
| 119 | + BaseAddress = nextServerBase |
| 120 | +}; |
| 121 | + |
103 | 122 | app.UseForwardedHeaders(); |
| 123 | +app.UseWebSockets(); |
104 | 124 |
|
105 | 125 | app.UseMigrationsEndPoint(); |
106 | 126 | app.UseSwagger(); |
|
113 | 133 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. |
114 | 134 | app.UseHsts(); |
115 | 135 | app.UseHttpsRedirection(); |
116 | | - |
117 | | - // Redirect root requests to the Client App in development |
118 | | - app.MapGet("/", async (HttpContext ctx) => |
119 | | - ctx.Response.Redirect("https://localhost:3000")); |
120 | 136 | } |
| 137 | +else |
| 138 | +{ |
| 139 | + // Production-specific middleware (if needed) can go here |
| 140 | +} |
| 141 | + |
| 142 | +// After all .NET middleware has run, let Next.js handle 404s |
| 143 | +Proxy.MapNotFoundToNode(app, nextClient, ignorePaths:[ |
| 144 | + "/api", |
| 145 | + "/auth", |
| 146 | + "/Identity", |
| 147 | + "/swagger", |
| 148 | +]); |
121 | 149 |
|
122 | 150 | app.UseStaticFiles(); |
123 | 151 | app.UseCookiePolicy(); |
124 | 152 | app.UseCors(); |
125 | 153 |
|
126 | | -// Fallback for dynamic post routes without a static exported page. |
127 | | -// If a specific post HTML exists, serve it; otherwise serve the generic |
128 | | -// placeholder page that loads the post client-side from the API. |
129 | | -var fallbackRoutes = new Dictionary<string, string> |
130 | | -{ |
131 | | - ["/posts/{id:long}/{slug}"] = "posts/0/_placeholder.html", |
132 | | - ["/tech/{slug}"] = "tech/_placeholder.html", |
133 | | - ["/stacks/{slug}"] = "stacks/_placeholder.html", |
134 | | -}; |
135 | | -foreach (var route in fallbackRoutes) |
136 | | -{ |
137 | | - app.MapGet(route.Key, (HttpContext ctx, IWebHostEnvironment env) => |
138 | | - { |
139 | | - var placeholderPath = Path.Combine(env.WebRootPath, route.Value); |
140 | | - if (File.Exists(placeholderPath)) |
141 | | - return Results.File(placeholderPath, "text/html; charset=utf-8"); |
142 | | - |
143 | | - return Results.NotFound(); |
144 | | - }); |
145 | | -} |
146 | | - |
147 | 154 | // GitHub OAuth endpoint |
148 | 155 | app.MapGet("/auth/github", ( |
149 | 156 | HttpContext context, |
|
166 | 173 |
|
167 | 174 | app.MapRazorPages(); |
168 | 175 | app.MapAdditionalIdentityEndpoints(); |
169 | | -app.MapFallbackToFile("index.html"); |
| 176 | + |
| 177 | +// Proxy development HMR WebSocket and fallback routes to the Next server |
| 178 | +if (app.Environment.IsDevelopment()) |
| 179 | +{ |
| 180 | + app.Map("/_next/webpack-hmr", async context => |
| 181 | + { |
| 182 | + if (context.WebSockets.IsWebSocketRequest) |
| 183 | + { |
| 184 | + await Proxy.WebSocketToNode(context, nextServerBase, allowInvalidCertsForNext); |
| 185 | + } |
| 186 | + else |
| 187 | + { |
| 188 | + await Proxy.HttpToNode(context, nextClient); |
| 189 | + } |
| 190 | + }); |
| 191 | + |
| 192 | + // Start the Next.js dev server if the Next.js lockfile does not exist '../TechStacks.Client/dist/lock' |
| 193 | + var nextLockFile = "../TechStacks.Client/dist/lock"; |
| 194 | + if (!File.Exists(nextLockFile)) |
| 195 | + { |
| 196 | + Console.WriteLine("Starting Next.js dev server..."); |
| 197 | + if (!Proxy.TryStartNode("../TechStacks.Client", out var process)) |
| 198 | + { |
| 199 | + Console.WriteLine($"Failed to start Next.js dev server: {process.ExitCode}"); |
| 200 | + return; |
| 201 | + } |
| 202 | + |
| 203 | + process.Exited += (s, e) => { |
| 204 | + Console.WriteLine("[node] Exited: " + process.ExitCode); |
| 205 | + File.Delete(nextLockFile); |
| 206 | + }; |
| 207 | + |
| 208 | + app.Lifetime.ApplicationStopping.Register(() => { |
| 209 | + if (!process.HasExited) |
| 210 | + { |
| 211 | + process.Kill(entireProcessTree: true); |
| 212 | + } |
| 213 | + }); |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +// Fallback: any unmatched route goes to Next.js |
| 218 | +app.MapFallback(context => Proxy.HttpToNode(context, nextClient)); |
170 | 219 |
|
171 | 220 | app.Run(); |
0 commit comments