You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Migrate backend to Azure Functions (Flex Consumption)
Ports the ASP.NET Core Web API to a .NET 9 isolated-worker Function App.
The Web API project stays in the tree and keeps building; it serves
production until cutover.
Structure:
- Flowboard.Core: shared library (services, models, DTOs, JWT params).
No hosting dependencies, referenced by both hosts.
- Flowboard.Functions: 52 endpoints ported from 8 controllers with exact
route parity. Responses verified byte-identical to the old backend
across 7 endpoints, including camelCase dictionary keys.
Functions runs none of the ASP.NET pipeline, so auth and rate limiting
are IFunctionsWorkerMiddleware. Auth is default-deny: every function is
protected unless it carries [AllowAnonymous] (5 endpoints do), so a
forgotten attribute over-protects instead of exposing.
The 5 policies in use were all RequireAuthenticatedUser() under different
names, and ClientReadOnly was never applied to any endpoint, so
default-deny reproduces the original authorization exactly.
Four bugs found by verifying against the deployed app:
- Declaring "options" per-function created 10 (route, OPTIONS) collisions.
Colliding functions fail to register, producing a clean deploy with
green CI and 404s at runtime. CORS is now configured at the platform
level; adding an origin is an Azure config change, not a code change.
- The host answers OPTIONS before the worker runs, so worker-level CORS
never executed. Confirmed via App Insights: a catch-all preflight
function logged zero invocations while every GET and 401 appeared.
- /api/subtasks/me matched {id} and returned 400 where the old backend
returned 200. Fixed with an ObjectId-length constraint on {id}.
- Rate limiting keyed on X-Forwarded-For, which Azure writes as ip:port.
The port changes per connection, so every request created a new counter
and the limit never tripped: 110 requests against a 100/60s limit gave
zero 429s. Now 99x 200 then 429 with Retry-After.
Also fixes AllowedHosts, which was set to a URL rather than a hostname
and to the frontend's host rather than the backend's. Development mode
masked it; it 400s every request under Production.
Deletes TasksController and DetailedTasksController (comment-only files).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123HJQhm6bmiyiprJUhRUmD
echo "::error::Deploy succeeded but zero functions are registered on ${{ env.AZURE_FUNCTIONAPP_NAME }} after retrying for ~2.5 minutes. This usually means .funcignore stripped required files, or the publish output was empty/malformed. Failing the build."
0 commit comments