From e2e7a8c292db54db20062ec43ab01c73bdc08b13 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 17:09:22 +0000 Subject: [PATCH] feat: add license key expiration warning on workers page (#7225) * feat: add license key expiration warning on workers page - Add license expiration check for superadmins in enterprise mode - Show error toast if license is expired - Show warning toast if license expires within 7 days - Follows existing license parsing pattern from InstanceSetting.svelte Fixes #7224 Co-authored-by: windmill-internal-app[bot] * nit * nit --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] Co-authored-by: HugoCasa --- .../(root)/(logged)/workers/+page.svelte | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/frontend/src/routes/(root)/(logged)/workers/+page.svelte b/frontend/src/routes/(root)/(logged)/workers/+page.svelte index 4afc391e88..e127ba9a36 100644 --- a/frontend/src/routes/(root)/(logged)/workers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workers/+page.svelte @@ -112,6 +112,70 @@ } } + function parseLicenseKey(key: string): { + valid: boolean + expiration?: Date + } { + let splitted = key.split('.') + if (splitted.length >= 3) { + try { + let i = parseInt(splitted[1]) + let date = new Date(i * 1000) + const stringDate = date.toLocaleDateString() + if (stringDate !== 'Invalid Date') { + return { + valid: date.getTime() > Date.now(), + expiration: date + } + } + } catch {} + } + return { + valid: false + } + } + + async function checkLicenseExpiration() { + if (!$superadmin || !$enterpriseLicense) { + return + } + + try { + const licenseKey = (await SettingService.getGlobal({ + key: 'license_key' + })) as string + + if (!licenseKey) { + return + } + + const { valid, expiration } = parseLicenseKey(licenseKey) + + if (!valid && expiration) { + // License is expired + sendUserToast( + `Enterprise license key expired on ${expiration.toLocaleDateString()}. Please renew your license key to continue using Windmill.`, + true + ) + } else if (expiration) { + // Check if expires within 7 days + const daysUntilExpiration = Math.floor( + (expiration.getTime() - Date.now()) / (1000 * 60 * 60 * 24) + ) + + if (daysUntilExpiration <= 7 && daysUntilExpiration >= 0) { + sendUserToast( + `Enterprise license key expires in ${daysUntilExpiration} day${daysUntilExpiration !== 1 ? 's' : ''} on ${expiration.toLocaleDateString()}. Please renew your license key to continue using Windmill.`, + true + ) + } + } + } catch (err) { + // Silently fail - don't show errors for license check + console.error('Failed to check license expiration:', err) + } + } + onMount(() => { intervalId = setInterval(() => { loadWorkers() @@ -126,6 +190,7 @@ loadWorkerGroups() loadCustomTags() $: ($superadmin || $devopsRole) && loadDefaultTagsPerWorkspace() + $: $superadmin && $enterpriseLicense && checkLicenseExpiration() onDestroy(() => { if (intervalId) {