♻️ Refactor licensing feature (#1831)

This commit is contained in:
Luke Vella 2025-07-16 14:58:08 +01:00 committed by GitHub
parent b4cbb629b7
commit c9b5527432
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 51 additions and 18 deletions

View file

@ -2,8 +2,8 @@ import type { Stripe } from "@rallly/billing";
import { stripe } from "@rallly/billing";
import { posthog } from "@rallly/posthog/server";
import { env } from "@/env";
import { licensingClient } from "@/features/licensing/client";
import { licenseCheckoutMetadataSchema } from "@/features/licensing/schema";
import { licenseManager } from "@/features/licensing/server";
import { subscriptionCheckoutMetadataSchema } from "@/features/subscription/schema";
import { getEmailClient } from "@/utils/emails";
@ -61,7 +61,7 @@ async function handleSelfHostedCheckoutSessionCompleted(
);
}
const license = await licensingClient.createLicense({
const license = await licenseManager.createLicense({
type: licenseType,
licenseeEmail: email,
licenseeName: customerDetails.name ?? undefined,

View file

@ -0,0 +1,24 @@
"use server";
import { prisma } from "@rallly/database";
import { adminActionClient } from "@/features/safe-action/server";
export const removeInstanceLicenseAction = adminActionClient
.metadata({
actionName: "remove_instance_license",
})
.action(async () => {
try {
await prisma.instanceLicense.deleteMany();
} catch (_error) {
return {
success: false,
message: "Failed to delete license",
};
}
return {
success: true,
message: "License deleted successfully",
};
});

View file

@ -2,7 +2,7 @@
import { prisma } from "@rallly/database";
import { rateLimit } from "@/features/rate-limit";
import { licensingClient } from "../client";
import { licenseManager } from "../server";
export async function validateLicenseKey(key: string) {
const { success } = await rateLimit("validate_license_key", 10, "1 m");
@ -14,7 +14,7 @@ export async function validateLicenseKey(key: string) {
};
}
const { data } = await licensingClient.validateLicenseKey({
const { data } = await licenseManager.validateLicenseKey({
key,
});

View file

@ -1,7 +0,0 @@
import { env } from "@/env";
import { LicensingClient } from "./lib/licensing-client";
export const licensingClient = new LicensingClient({
apiUrl: env.LICENSE_API_URL,
authToken: env.LICENSE_API_AUTH_TOKEN,
});

View file

@ -14,15 +14,15 @@ import {
} from "@rallly/ui/dialog";
import { Icon } from "@rallly/ui/icon";
import { XIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useTransition } from "react";
import { Trans } from "@/components/trans";
import { removeInstanceLicense } from "../mutations";
import { useSafeAction } from "@/features/safe-action/client";
import { removeInstanceLicenseAction } from "../actions";
export function RemoveLicenseButton() {
const [isPending, startTransition] = useTransition();
const router = useRouter();
const dialog = useDialog();
const removeInstanceLicense = useSafeAction(removeInstanceLicenseAction);
return (
<Dialog {...dialog.dialogProps}>
<DialogTrigger asChild>
@ -56,8 +56,7 @@ export function RemoveLicenseButton() {
variant="destructive"
onClick={() =>
startTransition(async () => {
await removeInstanceLicense();
router.refresh();
await removeInstanceLicense.executeAsync();
dialog.dismiss();
})
}

View file

@ -1 +0,0 @@
export { LicensingClient } from "./lib/licensing-client";

View file

@ -7,7 +7,7 @@ import {
validateLicenseKeyResponseSchema,
} from "../schema";
export class LicensingClient {
export class LicenseManager {
apiUrl: string;
authToken?: string;

View file

@ -0,0 +1,7 @@
import { env } from "@/env";
import { LicenseManager } from "./lib/licensing-manager";
export const licenseManager = new LicenseManager({
apiUrl: env.LICENSE_API_URL,
authToken: env.LICENSE_API_AUTH_TOKEN,
});

View file

@ -88,3 +88,14 @@ export const authActionClient = actionClient
});
})
.use(posthogMiddleware);
export const adminActionClient = authActionClient.use(async ({ ctx, next }) => {
if (ctx.user.role !== "admin") {
throw new ActionError({
code: "FORBIDDEN",
message: "You do not have permission to perform this action.",
});
}
return next();
});