feat(phase1d): sase admin-sdk skeleton + endpoint inventory on /projects/sase

This commit is contained in:
Semih
2026-05-13 10:43:07 +00:00
parent e4e734fc66
commit 7b83ef8f11
2 changed files with 112 additions and 14 deletions

View File

@@ -1,6 +1,7 @@
import { saseDb } from "@/lib/db-sase";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { SASE_ADMIN_ENDPOINTS, saseAdminWired } from "@/lib/admin-sdk/sase";
async function getSaseStats() {
try {
@@ -37,21 +38,52 @@ export async function SaseHealth() {
);
}
const wired = saseAdminWired();
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
<Stat label="Connection" value="healthy" badge="ok" />
<Stat label="Total users" value={stats.users.toString()} hint="public.users" />
<Stat
label="New users (7d)"
value={stats.recentUsers.toString()}
hint="created_at last 7 days"
/>
<Stat
label="Active subscriptions"
value={`${stats.activeSubs} / ${stats.totalSubs}`}
hint="status='active'"
/>
</div>
<>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
<Stat label="Connection" value="healthy" badge="ok" />
<Stat label="Total users" value={stats.users.toString()} hint="public.users" />
<Stat
label="New users (7d)"
value={stats.recentUsers.toString()}
hint="created_at last 7 days"
/>
<Stat
label="Active subscriptions"
value={`${stats.activeSubs} / ${stats.totalSubs}`}
hint="status='active'"
/>
</div>
<Card>
<CardHeader>
<CardDescription>Admin SDK</CardDescription>
<CardTitle className="flex items-center gap-2 text-base">
{wired ? (
<Badge>{SASE_ADMIN_ENDPOINTS.length} endpoints wired</Badge>
) : (
<Badge variant="outline">not wired</Badge>
)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm">
{!wired && (
<p className="text-muted-foreground">
Set <code>SASE_ADMIN_API_BASE</code> and{" "}
<code>INTERNAL_API_TOKEN_SASE</code> in Coolify env when spoke ships
the <code>/internal/admin/*</code> endpoints.
</p>
)}
<ul className="space-y-1 font-mono text-xs text-muted-foreground">
{SASE_ADMIN_ENDPOINTS.map((e) => (
<li key={e}>{e}</li>
))}
</ul>
</CardContent>
</Card>
</>
);
}

View File

@@ -0,0 +1,66 @@
import { AdminClient } from "./base";
export type SaseUserRef = { id: string; email: string };
export type SaseSubscriptionStatus = "pending" | "active" | "cancelled" | "expired";
export type SaseAdmin = {
/**
* Update a user's subscription. Spoke endpoint: PATCH /internal/admin/users/:id/subscription
* Not yet implemented spoke-side.
*/
setSubscription(input: {
userId: string;
planId: string;
billingPeriod: "monthly" | "yearly";
}): Promise<{ id: string; status: SaseSubscriptionStatus }>;
/**
* Force-cancel a subscription. Spoke endpoint: DELETE /internal/admin/subscriptions/:id
*/
cancelSubscription(subscriptionId: string): Promise<{ ok: true }>;
/**
* Resend verification email. Spoke endpoint: POST /internal/admin/users/:id/resend-verification
*/
resendVerification(userId: string): Promise<{ ok: true }>;
};
export function createSaseAdmin(): SaseAdmin {
const base = process.env.SASE_ADMIN_API_BASE;
const token = process.env.INTERNAL_API_TOKEN_SASE;
if (!base || !token) {
return notWiredSdk("sase");
}
const client = new AdminClient({ projectKey: "sase", baseUrl: base, token });
return {
setSubscription: (input) =>
client.call("PATCH", `/internal/admin/users/${input.userId}/subscription`, input),
cancelSubscription: (id) =>
client.call("DELETE", `/internal/admin/subscriptions/${id}`),
resendVerification: (id) =>
client.call("POST", `/internal/admin/users/${id}/resend-verification`),
};
}
function notWiredSdk(projectKey: string): SaseAdmin {
const reject = () => {
throw new Error(
`admin-sdk[${projectKey}]: spoke not wired (SASE_ADMIN_API_BASE / INTERNAL_API_TOKEN_SASE missing)`,
);
};
return {
setSubscription: () => Promise.reject(reject()),
cancelSubscription: () => Promise.reject(reject()),
resendVerification: () => Promise.reject(reject()),
};
}
export function saseAdminWired(): boolean {
return Boolean(process.env.SASE_ADMIN_API_BASE && process.env.INTERNAL_API_TOKEN_SASE);
}
export const SASE_ADMIN_ENDPOINTS = [
"PATCH /internal/admin/users/:id/subscription",
"DELETE /internal/admin/subscriptions/:id",
"POST /internal/admin/users/:id/resend-verification",
];