feat(licenses): refactor license handling and introduce activation functionality

- Replaced validateLicense with saveLicense in the EnablePaidFeatures component for improved clarity.
- Updated user API to include saveLicense and activateLicense methods for better license management.
- Enhanced validateLicense function to fetch from a new endpoint and added activateLicense for server activation.
- Removed unnecessary console logging in license validation for cleaner code.
This commit is contained in:
Mauricio Siu
2025-03-23 19:04:24 -06:00
parent 5fd8fcfa1e
commit 4074942dbf
5 changed files with 36 additions and 8 deletions

View File

@@ -17,8 +17,7 @@ import { useState, useEffect } from "react";
export const EnablePaidFeatures = () => { export const EnablePaidFeatures = () => {
const { data, refetch } = api.user.get.useQuery(); const { data, refetch } = api.user.get.useQuery();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { mutateAsync: validateLicense } = const { mutateAsync: saveLicense } = api.user.saveLicense.useMutation();
api.user.validateLicense.useMutation();
const { mutateAsync: update } = api.user.update.useMutation(); const { mutateAsync: update } = api.user.update.useMutation();
const [licenseKey, setLicenseKey] = useState(""); const [licenseKey, setLicenseKey] = useState("");
@@ -34,14 +33,13 @@ export const EnablePaidFeatures = () => {
return; return;
} }
setIsLoading(true); setIsLoading(true);
await validateLicense({ await saveLicense({
licenseKey, licenseKey,
}) })
.then(() => { .then(() => {
toast.success("License validated successfully"); toast.success("License validated successfully");
}) })
.catch((e) => { .catch((e) => {
console.error(e);
toast.error("Error validating license", { toast.error("Error validating license", {
description: e.message, description: e.message,
}); });

View File

@@ -29,7 +29,10 @@ import {
protectedProcedure, protectedProcedure,
publicProcedure, publicProcedure,
} from "../trpc"; } from "../trpc";
import { validateLicense } from "@/server/utils/validate-license"; import {
validateLicense,
activateLicense,
} from "@/server/utils/validate-license";
const apiCreateApiKey = z.object({ const apiCreateApiKey = z.object({
name: z.string().min(1), name: z.string().min(1),
prefix: z.string().optional(), prefix: z.string().optional(),
@@ -140,7 +143,7 @@ export const userRouter = createTRPCRouter({
} }
return await updateUser(ctx.user.id, input); return await updateUser(ctx.user.id, input);
}), }),
validateLicense: adminProcedure saveLicense: adminProcedure
.input( .input(
z.object({ z.object({
licenseKey: z.string(), licenseKey: z.string(),
@@ -159,9 +162,12 @@ export const userRouter = createTRPCRouter({
}); });
} }
await activateLicense(input.licenseKey, owner?.serverIp || "");
await updateUser(ctx.user.id, { await updateUser(ctx.user.id, {
licenseKey: input.licenseKey, licenseKey: input.licenseKey,
}); });
return result; return result;
}), }),
getUserByToken: publicProcedure getUserByToken: publicProcedure

View File

@@ -1,7 +1,7 @@
const licensesUrl = process.env.LICENSES_URL || "http://localhost:4002"; const licensesUrl = process.env.LICENSES_URL || "http://localhost:4002";
export const validateLicense = async (licenseKey: string, serverIp: string) => { export const validateLicense = async (licenseKey: string, serverIp: string) => {
const response = await fetch(`${licensesUrl}/api/validate`, { const response = await fetch(`${licensesUrl}/api/license/validate`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -16,3 +16,16 @@ export const validateLicense = async (licenseKey: string, serverIp: string) => {
return data; return data;
}; };
export const activateLicense = async (licenseKey: string, serverIp: string) => {
const response = await fetch(`${licensesUrl}/api/license/activate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ licenseKey, serverIp }),
});
const data = await response.json();
return data;
};

View File

@@ -25,7 +25,6 @@ licenseRouter.post(
try { try {
const result = await validateLicense(licenseKey, serverIp); const result = await validateLicense(licenseKey, serverIp);
console.log("Result", result);
return c.json(result); return c.json(result);
} catch (error) { } catch (error) {
logger.error("Error validating license:", { error }); logger.error("Error validating license:", { error });

View File

@@ -75,6 +75,16 @@ export const validateLicense = async (licenseKey: string, serverIp: string) => {
const suscription = await stripe.subscriptions.retrieve( const suscription = await stripe.subscriptions.retrieve(
license.stripeSubscriptionId, license.stripeSubscriptionId,
); );
const currentServerQuantity = license.serverIps?.length || 0;
const serversQuantity = suscription.items.data[0].quantity || 0;
if (currentServerQuantity >= serversQuantity) {
return {
isValid: false,
error:
"You have reached the maximum number of servers, please upgrade your license to add more servers",
};
}
if (suscription.status !== "active") { if (suscription.status !== "active") {
return { return {
@@ -120,6 +130,8 @@ export const activateLicense = async (licenseKey: string, serverIp: string) => {
); );
} }
console.log("License", license.serverIps?.includes(serverIp));
if (license.serverIps && !license.serverIps.includes(serverIp)) { if (license.serverIps && !license.serverIps.includes(serverIp)) {
throw new Error("License is already activated on a different server"); throw new Error("License is already activated on a different server");
} }