feat: Implement OAuth license activation flow
- Add LicenseConnect.tsx focused OAuth confirmation page in customer SPA - Add /licenses/oauth/validate and /licenses/oauth/confirm API endpoints - Update App.tsx to render license-connect outside BaseLayout (no header/footer) - Add license_activation_method field to product settings in Admin SPA - Create LICENSING_MODULE.md with comprehensive OAuth flow documentation - Update API_ROUTES.md with license module endpoints
This commit is contained in:
@@ -60,6 +60,7 @@ export interface PageItem {
|
||||
slug?: string;
|
||||
title: string;
|
||||
url?: string;
|
||||
isFrontPage?: boolean;
|
||||
isSpaLanding?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ export type ProductFormData = {
|
||||
licensing_enabled?: boolean;
|
||||
license_activation_limit?: string;
|
||||
license_duration_days?: string;
|
||||
license_activation_method?: '' | 'api' | 'oauth';
|
||||
// Subscription
|
||||
subscription_enabled?: boolean;
|
||||
subscription_period?: 'day' | 'week' | 'month' | 'year';
|
||||
@@ -95,6 +96,7 @@ export function ProductFormTabbed({
|
||||
const [licensingEnabled, setLicensingEnabled] = useState(initial?.licensing_enabled || false);
|
||||
const [licenseActivationLimit, setLicenseActivationLimit] = useState(initial?.license_activation_limit || '');
|
||||
const [licenseDurationDays, setLicenseDurationDays] = useState(initial?.license_duration_days || '');
|
||||
const [licenseActivationMethod, setLicenseActivationMethod] = useState<'' | 'api' | 'oauth'>(initial?.license_activation_method || '');
|
||||
// Subscription state
|
||||
const [subscriptionEnabled, setSubscriptionEnabled] = useState(initial?.subscription_enabled || false);
|
||||
const [subscriptionPeriod, setSubscriptionPeriod] = useState<'day' | 'week' | 'month' | 'year'>(initial?.subscription_period || 'month');
|
||||
@@ -131,6 +133,7 @@ export function ProductFormTabbed({
|
||||
setLicensingEnabled(initial.licensing_enabled || false);
|
||||
setLicenseActivationLimit(initial.license_activation_limit || '');
|
||||
setLicenseDurationDays(initial.license_duration_days || '');
|
||||
setLicenseActivationMethod(initial.license_activation_method || '');
|
||||
// Subscription
|
||||
setSubscriptionEnabled(initial.subscription_enabled || false);
|
||||
setSubscriptionPeriod(initial.subscription_period || 'month');
|
||||
@@ -199,6 +202,7 @@ export function ProductFormTabbed({
|
||||
licensing_enabled: licensingEnabled,
|
||||
license_activation_limit: licensingEnabled ? licenseActivationLimit : undefined,
|
||||
license_duration_days: licensingEnabled ? licenseDurationDays : undefined,
|
||||
license_activation_method: licensingEnabled ? licenseActivationMethod : undefined,
|
||||
// Subscription
|
||||
subscription_enabled: subscriptionEnabled,
|
||||
subscription_period: subscriptionEnabled ? subscriptionPeriod : undefined,
|
||||
@@ -261,6 +265,8 @@ export function ProductFormTabbed({
|
||||
setLicenseActivationLimit={setLicenseActivationLimit}
|
||||
licenseDurationDays={licenseDurationDays}
|
||||
setLicenseDurationDays={setLicenseDurationDays}
|
||||
licenseActivationMethod={licenseActivationMethod}
|
||||
setLicenseActivationMethod={setLicenseActivationMethod}
|
||||
subscriptionEnabled={subscriptionEnabled}
|
||||
setSubscriptionEnabled={setSubscriptionEnabled}
|
||||
subscriptionPeriod={subscriptionPeriod}
|
||||
|
||||
@@ -50,6 +50,8 @@ type GeneralTabProps = {
|
||||
setLicenseActivationLimit?: (value: string) => void;
|
||||
licenseDurationDays?: string;
|
||||
setLicenseDurationDays?: (value: string) => void;
|
||||
licenseActivationMethod?: '' | 'api' | 'oauth';
|
||||
setLicenseActivationMethod?: (value: '' | 'api' | 'oauth') => void;
|
||||
// Subscription
|
||||
subscriptionEnabled?: boolean;
|
||||
setSubscriptionEnabled?: (value: boolean) => void;
|
||||
@@ -95,6 +97,8 @@ export function GeneralTab({
|
||||
setLicenseActivationLimit,
|
||||
licenseDurationDays,
|
||||
setLicenseDurationDays,
|
||||
licenseActivationMethod,
|
||||
setLicenseActivationMethod,
|
||||
subscriptionEnabled,
|
||||
setSubscriptionEnabled,
|
||||
subscriptionPeriod,
|
||||
@@ -498,6 +502,27 @@ export function GeneralTab({
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{setLicenseActivationMethod && (
|
||||
<div>
|
||||
<Label className="text-xs">{__('Activation Method')}</Label>
|
||||
<Select
|
||||
value={licenseActivationMethod || 'default'}
|
||||
onValueChange={(v) => setLicenseActivationMethod(v === 'default' ? '' : v as '' | 'api' | 'oauth')}
|
||||
>
|
||||
<SelectTrigger className="mt-1">
|
||||
<SelectValue placeholder={__('Use Site Default')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="default">{__('Use Site Default')}</SelectItem>
|
||||
<SelectItem value="api">{__('Simple API (license key only)')}</SelectItem>
|
||||
<SelectItem value="oauth">{__('Secure OAuth (requires login)')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{__('Override site-level activation method for this product')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user