This commit is contained in:
2025-10-24 15:05:27 +08:00
parent 767af75aad
commit a4ae06ea52
6 changed files with 25 additions and 25 deletions

View File

@@ -9,7 +9,11 @@
<template v-if="!licenseDisplayInfo.isPaid"> <template v-if="!licenseDisplayInfo.isPaid">
<button class="action-btn" @click="refreshLicenseStatus" :disabled="checking" ref="checkButton">{{ $t('license.button.checkLicense.label') }}</button> <button class="action-btn" @click="refreshLicenseStatus" :disabled="checking" ref="checkButton">{{ $t('license.button.checkLicense.label') }}</button>
<kuc-tooltip :title="$t('license.button.checkLicense.desc')" :container="checkButton"></kuc-tooltip> <kuc-tooltip :title="$t('license.button.checkLicense.desc')" :container="checkButton"></kuc-tooltip>
<button class="action-btn main" @click="purchaseLicense" ref="buyButton">{{ $t('license.button.purchase.label') }}</button> <button class="action-btn main" @click="purchaseLicense" ref="buyButton">{{ $t('license.button.purchase.label') }}
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="open-icon">
<path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<kuc-tooltip :title="$t('license.button.purchase.desc')" :container="buyButton"></kuc-tooltip> <kuc-tooltip :title="$t('license.button.purchase.desc')" :container="buyButton"></kuc-tooltip>
</template> </template>
<button v-else class="action-btn" @click="hidePaidMsg">{{ $t('license.button.hide') }}</button> <button v-else class="action-btn" @click="hidePaidMsg">{{ $t('license.button.hide') }}</button>
@@ -134,9 +138,10 @@ async function refreshLicenseStatus() {
} }
function purchaseLicense() { function purchaseLicense() {
alert( const { name, email } = kintone.getLoginUser();
`请联系管理员或访问官方网站购买许可证。\nDomain: ${licenseInfo.value?.domain}\nPlugin: ${licenseInfo.value?.pluginId}`, const domain = licenseInfo.value?.domain;
); const pluginId = licenseInfo.value?.pluginId;
window.open(`https://alisurvey.alicorns.co.jp/s/Iuc5RxZv?input1761283180784=${domain}&input1761283200616=${pluginId}&input1761283314263=${name}&input1761283275767=${email}`);
} }
function hidePaidMsg() { function hidePaidMsg() {

View File

@@ -70,14 +70,19 @@
border-color: #33691E; border-color: #33691E;
} }
.license-status-info .action-btn .open-icon {
margin-left: 2px;
vertical-align: middle;
}
.notification-link { .notification-link {
color: #fff; color: #fff;
opacity: 0.8;
text-decoration: underline; text-decoration: underline;
} }
.notification-link:hover { .notification-link:hover {
opacity: 1; color: #fff;
opacity: 0.8;
} }
.text-red { .text-red {

View File

@@ -54,7 +54,8 @@ export default {
}, },
error: { error: {
fetchFailed: 'Remote license check failed: {e}', fetchFailed: 'Remote license check failed: {e}',
checkFailed: 'License check failed, plugin disabled: {e}' checkFailed: 'License check failed, plugin disabled: {e}',
jwtFailed: 'JWT verification or parsing failed: {e}',
} }
}, },
}; };

View File

@@ -42,7 +42,7 @@ export default {
}, },
notification: { notification: {
days: '今日 | 明日 | {count}日後', days: '今日 | 明日 | {count}日後',
gotoLink: 'ご利用には<a class="notification-link" href="{link}">プラグインの設定</a>をご確認ください。', gotoLink: 'ご利用には<a class="notification-link" href="{link}">プラグインの設定</a>をご確認ください。',
expired: 'プラグイン「{plugin}」の有効期限が切れました。', expired: 'プラグイン「{plugin}」の有効期限が切れました。',
warning: 'プラグイン「{plugin}」の試用期間が{days}で終了します。' warning: 'プラグイン「{plugin}」の試用期間が{days}で終了します。'
}, },
@@ -57,7 +57,8 @@ export default {
}, },
error: { error: {
fetchFailed: 'リモートライセンスチェックに失敗しました: {e}', fetchFailed: 'リモートライセンスチェックに失敗しました: {e}',
checkFailed: 'ライセンスチェックに失敗しました。プラグイン機能が無効になりました: {e}' checkFailed: 'ライセンスチェックに失敗しました。プラグイン機能が無効になりました: {e}',
jwtFailed: 'JWTの検証または解析に失敗しました: {e}',
} }
}, },
}; };

View File

@@ -51,11 +51,11 @@ export class LicenseService {
// 验证完整的JWT // 验证完整的JWT
const result = KJUR.jws.JWS.verifyJWT(savedLicense.jwt, rsaPublicKey.trim(), {alg: ['RS256']}); const result = KJUR.jws.JWS.verifyJWT(savedLicense.jwt, rsaPublicKey.trim(), {alg: ['RS256']});
if (!result) { if (!result) {
console.warn('JWT signature verification failed'); console.warn($t('license.error.jwtFailed', { e : '' }));
return false; return false;
} }
} catch (error) { } catch (error) {
console.error('JWT verification or parsing failed:', error); console.warn($t('license.error.jwtFailed', { e : error }));
return false; return false;
} }

View File

@@ -1,24 +1,12 @@
// 本地存储加密工具类 // 本地存储加密工具类
import { LicenseService } from '@/services/license-service'; import { LicenseService } from '@/services/license-service';
import type { LicenseInfo, LicenseJWSResult, LicenseSetting, SavedLicense } from '@/types/license'; import type { LicenseJWSResult, LicenseSetting, SavedLicense } from '@/types/license';
import { KJUR } from 'jsrsasign'; import { KJUR } from 'jsrsasign';
export class LicenseStorage { export class LicenseStorage {
private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_'; private static readonly STORAGE_KEY_PREFIX = 'alicorns_plugin_';
private static readonly STORAGE_SETTING_KEY_PREFIX = this.STORAGE_KEY_PREFIX + 'setting_'; private static readonly STORAGE_SETTING_KEY_PREFIX = this.STORAGE_KEY_PREFIX + 'setting_';
/**
* 解码JWT不验证
*/
private static parseJWT(jwt: string) {
try {
return KJUR.jws.JWS.parse(jwt);
} catch (error) {
console.error('JWT parsing failed:', error);
return null;
}
}
/** /**
* 生成存储key * 生成存储key
*/ */
@@ -89,7 +77,7 @@ export class LicenseStorage {
// decode // decode
let decodedJWT: LicenseJWSResult; let decodedJWT: LicenseJWSResult;
try { try {
decodedJWT = this.parseJWT(storedJWT) as LicenseJWSResult; decodedJWT = KJUR.jws.JWS.parse(storedJWT) as LicenseJWSResult;
if (!decodedJWT) { if (!decodedJWT) {
return null; return null;
} }