Feature Policies API + Invitations Policies (#375)

- Added Feature Policy API: a declarative system to enable/disable/modify default behavior in the SaaS kit
- Team invitation policies with pre-checks using the Feature Policy API: Invite Members dialog now shows loading, errors, and clear reasons when invitations are blocked
- Version bump to 2.16.0 and widespread dependency updates (Supabase, React types, react-i18next, etc.).
- Added comprehensive docs for the new policy system and orchestrators.
- Subscription cancellations now trigger immediate invoicing explicitly
This commit is contained in:
Giancarlo Buomprisco
2025-09-30 12:36:19 +08:00
committed by GitHub
parent 3c13b5ec1e
commit 1dd6fdad22
53 changed files with 3908 additions and 1128 deletions

View File

@@ -0,0 +1,69 @@
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { enhanceRouteHandler } from '@kit/next/routes';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import {
createInvitationContextBuilder,
createInvitationsPolicyEvaluator,
} from '@kit/team-accounts/policies';
export const GET = enhanceRouteHandler(
async function ({ params, user }) {
const client = getSupabaseServerClient();
const { account } = z.object({ account: z.string() }).parse(params);
try {
// Evaluate with standard evaluator
const evaluator = createInvitationsPolicyEvaluator();
const hasPolicies = await evaluator.hasPoliciesForStage('preliminary');
if (!hasPolicies) {
return NextResponse.json({
allowed: true,
reasons: [],
metadata: {
policiesEvaluated: 0,
timestamp: new Date().toISOString(),
noPoliciesConfigured: true,
},
});
}
// Build context for policy evaluation (empty invitations for testing)
const contextBuilder = createInvitationContextBuilder(client);
const context = await contextBuilder.buildContext(
{
invitations: [],
accountSlug: account,
},
user,
);
// validate against policies
const result = await evaluator.canInvite(context, 'preliminary');
return NextResponse.json(result);
} catch (error) {
return NextResponse.json(
{
allowed: false,
reasons: [
error instanceof Error ? error.message : 'Unknown error occurred',
],
metadata: {
error: true,
originalError:
error instanceof Error ? error.message : String(error),
},
},
{ status: 500 },
);
}
},
{
auth: true,
},
);