Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions controllers/license/api/v1/license_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type LicenseStatus struct {
//+kubebuilder:validation:Enum=Pending;Failed;Active;Expired;Invalid;Mismatch
//+kubebuilder:default=Pending
Phase LicenseStatusPhase `json:"phase,omitempty"`
Code ValidationCode `json:"code,omitempty"`
Reason string `json:"reason,omitempty"`
ActivationTime metav1.Time `json:"activationTime,omitempty"`
ExpirationTime metav1.Time `json:"expirationTime,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ spec:
activationTime:
format: date-time
type: string
code:
type: integer
expirationTime:
format: date-time
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ spec:
activationTime:
format: date-time
type: string
code:
type: integer
expirationTime:
format: date-time
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ func (r *LicenseReconciler) reconcile(
var reason string

var validationErr licenseutil.ValidationError
validationCode := licensev1.ValidationError
var phase licensev1.LicenseStatusPhase
if errors.As(err, &validationErr) {
reason = validationErr.Message
validationCode = validationErr.Code
switch validationErr.Code {
case licensev1.ValidationExpired:
phase = licensev1.LicenseStatusPhaseExpired
Expand All @@ -159,6 +161,7 @@ func (r *LicenseReconciler) reconcile(
}
updateStatus := &license.Status
updateStatus.Phase = phase
updateStatus.Code = validationCode
updateStatus.Reason = reason
updateStatus.ActivationTime = license.Status.ActivationTime
updateStatus.ExpirationTime = license.Status.ExpirationTime
Expand All @@ -182,6 +185,7 @@ func (r *LicenseReconciler) reconcile(
if err := r.activator.Active(ctx, license); err != nil {
failStatus := &license.Status
failStatus.Phase = licensev1.LicenseStatusPhaseFailed
failStatus.Code = licensev1.ValidationError
failStatus.Reason = fmt.Sprintf("license activation failed: %v", err)
failStatus.ActivationTime = license.Status.ActivationTime
failStatus.ExpirationTime = license.Status.ExpirationTime
Expand Down
6 changes: 5 additions & 1 deletion frontend/providers/license/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@
"file.upload error description": "Failed to upload files",
"Cancel": "Cancel",
"file.Select a maximum of 10 files": "You can select no more than 10 files",
"Copy Failed": "Failed to copy"
"Copy Failed": "Failed to copy",
"LICENSE_VALIDATION_generic": "License validation failed. Check the license content and try again.",
"LICENSE_VALIDATION_expired": "The current license has expired. Renew it and try again.",
"LICENSE_VALIDATION_clusterIdMismatch": "This license does not match the current cluster. Verify the cluster ID and try again.",
"LICENSE_VALIDATION_clusterInfoMismatch": "The current cluster resources do not satisfy the license constraints. Check node, CPU, memory, and user limits."
}
6 changes: 5 additions & 1 deletion frontend/providers/license/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@
"file.upload error description": "文件上传失败",
"Cancel": "取消",
"file.Select a maximum of 10 files": "至多选择 10 个文件",
"Copy Failed": "复制失败"
"Copy Failed": "复制失败",
"LICENSE_VALIDATION_generic": "License 校验失败,请检查许可证内容后重试",
"LICENSE_VALIDATION_expired": "当前 License 已过期,请续期后重试",
"LICENSE_VALIDATION_clusterIdMismatch": "当前 License 与本集群不匹配,请确认集群 ID 后重试",
"LICENSE_VALIDATION_clusterInfoMismatch": "当前集群资源不满足 License 约束,请核对节点、CPU、内存和用户数限制"
}
3 changes: 3 additions & 0 deletions frontend/providers/license/src/pages/api/applyYamlList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { authSession } from '@/services/backend/auth';
import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import { getLicenseErrorCode } from '@/utils/licenseError';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
Expand Down Expand Up @@ -30,6 +31,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
console.log(err, '------');
jsonRes(res, {
code: 500,
errorCode:
getLicenseErrorCode(err?.body?.details?.code) || getLicenseErrorCode(err?.body?.code),
error: err
});
}
Expand Down
13 changes: 10 additions & 3 deletions frontend/providers/license/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { debounce } from 'lodash';
import yaml from 'js-yaml';
import { useTranslation } from 'next-i18next';
import { getLicenseErrorMessage } from '@/utils/licenseError';
import { useEffect, useMemo, useState } from 'react';

export default function LicenseApp() {
Expand Down Expand Up @@ -139,7 +140,10 @@ export default function LicenseApp() {
});
} else if (licenseResult.status.phase !== 'Active') {
toast({
title: licenseResult.status.reason,
title: getLicenseErrorMessage(t, {
validationCode: licenseResult.status.code,
fallback: licenseResult.status.reason
}),
status: 'error'
});
} else {
Expand All @@ -152,10 +156,13 @@ export default function LicenseApp() {
}
queryClient.invalidateQueries(['getLicenseActive']);
},
onError(error: { message?: string }) {
onError(error: { message?: string; errorCode?: string | number }) {
if (error?.message && typeof error?.message === 'string') {
toast({
title: error.message,
title: getLicenseErrorMessage(t, {
errorCode: error.errorCode,
fallback: error.message
}),
status: 'error'
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ export const jsonRes = <T = any>(
props?: {
code?: number;
message?: string;
errorCode?: string | number;
data?: T;
error?: any;
}
) => {
const { code = 200, message = '', data = null, error } = props || {};
const { code = 200, message = '', errorCode, data = null, error } = props || {};

// Specified error
if (typeof error === 'string' && ERROR_RESPONSE[error]) {
Expand All @@ -33,6 +34,7 @@ export const jsonRes = <T = any>(
res.json({
code,
statusText: '',
errorCode,
message: msg,
data: data || error || null
});
Expand Down
1 change: 1 addition & 0 deletions frontend/providers/license/src/services/kubernet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface ApiResp<T = any> {
code: number;
message: string;
errorCode?: string | number;
data?: T;
}

Expand Down
1 change: 1 addition & 0 deletions frontend/providers/license/src/types/license.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export type LicenseCR = {
type: string;
};
status: {
code?: number;
activationTime: string;
expirationTime: string;
phase: 'Active' | 'Failed';
Expand Down
38 changes: 38 additions & 0 deletions frontend/providers/license/src/utils/licenseError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const LICENSE_ERROR_CODE_PREFIX = 'LICENSE_VALIDATION_';

export const LICENSE_ERROR_KEYS: Record<number, string> = {
1: 'generic',
2: 'expired',
3: 'clusterIdMismatch',
4: 'clusterInfoMismatch'
};

export const getLicenseErrorCode = (code?: number) => {
if (typeof code !== 'number') return undefined;
const key = LICENSE_ERROR_KEYS[code];
if (!key) return undefined;
return `${LICENSE_ERROR_CODE_PREFIX}${key}`;
};

export const getLicenseErrorMessage = (
t: (key: string) => string,
options: {
errorCode?: string | number;
validationCode?: number;
fallback?: string;
}
) => {
const keyFromErrorCode =
typeof options.errorCode === 'string' && options.errorCode.startsWith(LICENSE_ERROR_CODE_PREFIX)
? options.errorCode
: undefined;
const keyFromValidationCode = getLicenseErrorCode(options.validationCode);
const translationKey = keyFromErrorCode || keyFromValidationCode;

if (translationKey) {
const translated = t(translationKey);
if (translated !== translationKey) return translated;
}

return options.fallback || t('LICENSE_VALIDATION_generic');
};
Loading