mirror of
https://github.com/decaf-dev/obsidian-vault-explorer.git
synced 2026-07-22 05:11:41 +00:00
feat: remove premium features (#380)
This commit is contained in:
parent
4cb716297c
commit
c79f52e900
20 changed files with 23 additions and 436 deletions
|
|
@ -21,7 +21,6 @@ Vault Explorer is a work in progress. Please check the GitHub repository and doc
|
|||
- [Screenshots](#screenshots)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Premium](#premium)
|
||||
- [Features](#features)
|
||||
- [Network use](#network-use)
|
||||
- [Contributing](#contributing)
|
||||
|
|
@ -79,12 +78,6 @@ Vault Explorer is a work in progress. Please check the GitHub repository and doc
|
|||
|
||||
Click the compass button on the left-hand sidebar to open the vault explorer view.
|
||||
|
||||
## Premium
|
||||
|
||||
Premium features are available to users who purchase a [Vault Explorer license](https://vaultexplorer.com/docs/premium/).
|
||||
|
||||
Please do not share your license key with anyone. Shared license keys will be deactivated.
|
||||
|
||||
## Features
|
||||
|
||||
| Name | Categories | Documented |
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ export enum PluginEvent {
|
|||
FEED_CONTENT_SETTING_CHANGE = "feed-content-setting-change",
|
||||
COVER_IMAGE_SOURCE_SETTING_CHANGE = "cover-image-source-setting-change",
|
||||
PROPERTY_SETTING_CHANGE = "property-setting-change",
|
||||
LICENSE_KEY_VALIDATION_CHANGE = "license-key-validation-change",
|
||||
CLOCK_UPDATES_SETTING_CHANGE = "clock-updates-setting-change",
|
||||
FILTER_TOGGLE_SETTING_CHANGE = "filter-toggle-setting-change",
|
||||
SCROLL_BUTTONS_SETTING_CHANGE = "scroll-buttons-setting-change",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import { formatMessageForLogger, stringToLogLevel } from "./logger";
|
|||
import { preformMigrations } from "./migrations";
|
||||
import "./styles.css";
|
||||
import { clearSMICache } from "./svelte/app/services/smi-cache";
|
||||
import License from "./svelte/shared/services/license";
|
||||
import store from "./svelte/shared/services/store";
|
||||
import type { VaultExplorerPluginSettings } from "./types";
|
||||
import { isVersionLessThan } from "./utils";
|
||||
|
|
@ -31,8 +30,6 @@ export default class VaultExplorerPlugin extends Plugin {
|
|||
|
||||
store.plugin.set(this);
|
||||
|
||||
await License.getInstance().loadStoredKey();
|
||||
|
||||
this.registerView(
|
||||
VAULT_EXPLORER_VIEW,
|
||||
(leaf) => new VaultExplorerView(leaf, this)
|
||||
|
|
@ -202,11 +199,6 @@ export default class VaultExplorerPlugin extends Plugin {
|
|||
//Clean up the old device id from the versioning system
|
||||
const LOCAL_STORAGE_ID = "vault-explorer-id";
|
||||
localStorage.removeItem(LOCAL_STORAGE_ID);
|
||||
|
||||
//Clean up the old device id from the versioning system
|
||||
const LOCAL_STORAGE_LICENSE_KEY =
|
||||
"vault-explorer-license-key";
|
||||
localStorage.removeItem(LOCAL_STORAGE_LICENSE_KEY);
|
||||
}
|
||||
if (isVersionLessThan(loadedVersion, "1.37.1")) {
|
||||
console.log("Clearing image cache");
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
.vault-explorer-premium-setting {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
|
@ -24,21 +24,17 @@ import EventManager from "src/event/event-manager";
|
|||
import { PluginEvent } from "src/event/types";
|
||||
import { TExplorerView } from "src/types";
|
||||
import ImageSourceApp from "../svelte/image-source-app/index.svelte";
|
||||
import LicenseKeyApp from "../svelte/license-key-app/index.svelte";
|
||||
|
||||
import { clearSMICache } from "src/svelte/app/services/smi-cache";
|
||||
import "./styles.css";
|
||||
|
||||
export default class VaultExplorerSettingsTab extends PluginSettingTab {
|
||||
plugin: VaultExplorerPlugin;
|
||||
|
||||
licenseKeyApp: LicenseKeyApp | null;
|
||||
imageSourceApp: ImageSourceApp | null;
|
||||
|
||||
constructor(app: App, plugin: VaultExplorerPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
this.licenseKeyApp = null;
|
||||
this.imageSourceApp = null;
|
||||
}
|
||||
|
||||
|
|
@ -629,12 +625,6 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName("Premium").setHeading();
|
||||
|
||||
this.licenseKeyApp = new LicenseKeyApp({
|
||||
target: containerEl,
|
||||
});
|
||||
|
||||
new Setting(containerEl).setName("Debugging").setHeading();
|
||||
new Setting(containerEl)
|
||||
.setName("Log level")
|
||||
|
|
@ -693,7 +683,6 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
onClose() {
|
||||
this.licenseKeyApp?.$destroy();
|
||||
this.imageSourceApp?.$destroy();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
export let path: string;
|
||||
export let createdMillis: number;
|
||||
export let content: string | null;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let ref: HTMLElement | null = null;
|
||||
let enableFileIcons = false;
|
||||
|
|
@ -147,14 +146,7 @@
|
|||
function handleCardContextMenu(e: Event) {
|
||||
const nativeEvent = e as MouseEvent;
|
||||
const { app, settings } = plugin;
|
||||
openContextMenu(
|
||||
nativeEvent,
|
||||
path,
|
||||
app,
|
||||
settings,
|
||||
enablePremiumFeatures,
|
||||
{},
|
||||
);
|
||||
openContextMenu(nativeEvent, path, app, settings, {});
|
||||
}
|
||||
|
||||
function handleCardMouseOver(e: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
export let data: FileRenderData[] = [];
|
||||
export let startIndex;
|
||||
export let pageLength;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let filteredItems: FileRenderData[] = [];
|
||||
|
||||
|
|
@ -24,7 +23,6 @@
|
|||
<div class="vault-explorer-feed-view">
|
||||
{#each filteredItems as fileRenderData (fileRenderData.id)}
|
||||
<FeedCard
|
||||
{enablePremiumFeatures}
|
||||
displayName={fileRenderData.displayName}
|
||||
extension={fileRenderData.extension}
|
||||
baseName={fileRenderData.baseName}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
export let custom2: string | null;
|
||||
export let custom3: string | null;
|
||||
export let coverImageFit: CoverImageFit;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let plugin: VaultExplorerPlugin;
|
||||
let enableFileIcons: boolean = false;
|
||||
|
|
@ -110,21 +109,12 @@
|
|||
|
||||
const showCoverImageOptions = path.endsWith(".md");
|
||||
const { app, settings } = plugin;
|
||||
openContextMenu(
|
||||
nativeEvent,
|
||||
path,
|
||||
app,
|
||||
settings,
|
||||
enablePremiumFeatures,
|
||||
{
|
||||
coverImageFit: showCoverImageOptions
|
||||
? coverImageFit
|
||||
: undefined,
|
||||
onCoverImageFitChange: showCoverImageOptions
|
||||
? handleCoverImageFitChange
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
openContextMenu(nativeEvent, path, app, settings, {
|
||||
coverImageFit: showCoverImageOptions ? coverImageFit : undefined,
|
||||
onCoverImageFitChange: showCoverImageOptions
|
||||
? handleCoverImageFitChange
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function handleCardMouseOver(e: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
export let data: FileRenderData[];
|
||||
export let startIndex: number;
|
||||
export let pageLength: number;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let filteredItems: FileRenderData[] = [];
|
||||
|
||||
|
|
@ -25,7 +24,6 @@
|
|||
<div class="vault-explorer-grid-view__container">
|
||||
{#each filteredItems as fileRenderData (fileRenderData.id)}
|
||||
<GridCard
|
||||
{enablePremiumFeatures}
|
||||
displayName={fileRenderData.displayName}
|
||||
path={fileRenderData.path}
|
||||
coverImageFit={fileRenderData.coverImageFit}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<script lang="ts">
|
||||
import VaultExplorerPlugin from "src/main";
|
||||
import store from "../../shared/services/store";
|
||||
import Tag from "src/svelte/shared/components/tag.svelte";
|
||||
import Wrap from "src/svelte/shared/components/wrap.svelte";
|
||||
import Icon from "src/svelte/shared/components/icon.svelte";
|
||||
import Stack from "src/svelte/shared/components/stack.svelte";
|
||||
import { getIconIdForFile } from "../services/file-icon";
|
||||
import { onMount } from "svelte";
|
||||
import { HOVER_LINK_SOURCE_ID } from "src/constants";
|
||||
import EventManager from "src/event/event-manager";
|
||||
import { PluginEvent } from "src/event/types";
|
||||
import VaultExplorerPlugin from "src/main";
|
||||
import Icon from "src/svelte/shared/components/icon.svelte";
|
||||
import Stack from "src/svelte/shared/components/stack.svelte";
|
||||
import Tag from "src/svelte/shared/components/tag.svelte";
|
||||
import Wrap from "src/svelte/shared/components/wrap.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import store from "../../shared/services/store";
|
||||
import { openContextMenu } from "../services/context-menu";
|
||||
import { getIconIdForFile } from "../services/file-icon";
|
||||
import { openInCurrentTab } from "../services/open-file";
|
||||
import { HOVER_LINK_SOURCE_ID } from "src/constants";
|
||||
|
||||
export let displayName: string;
|
||||
export let baseName: string;
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
let showTags: boolean;
|
||||
export let isSmallScreenSize: boolean;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let enableFileIcons: boolean = false;
|
||||
let ref: HTMLElement | null = null;
|
||||
|
|
@ -78,14 +77,7 @@
|
|||
function handleItemContextMenu(e: Event) {
|
||||
const nativeEvent = e as MouseEvent;
|
||||
const { app, settings } = plugin;
|
||||
openContextMenu(
|
||||
nativeEvent,
|
||||
path,
|
||||
app,
|
||||
settings,
|
||||
enablePremiumFeatures,
|
||||
{},
|
||||
);
|
||||
openContextMenu(nativeEvent, path, app, settings, {});
|
||||
}
|
||||
|
||||
function handleItemMouseOver(e: MouseEvent) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
export let startIndex: number;
|
||||
export let pageLength: number;
|
||||
export let isSmallScreenSize: boolean;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let filteredItems: FileRenderData[] = [];
|
||||
|
||||
|
|
@ -26,7 +25,6 @@
|
|||
<div class="vault-explorer-list-view">
|
||||
{#each filteredItems as fileRenderData (fileRenderData.id)}
|
||||
<ListItem
|
||||
{enablePremiumFeatures}
|
||||
displayName={fileRenderData.displayName}
|
||||
extension={fileRenderData.extension}
|
||||
baseName={fileRenderData.baseName}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
export let data: FileRenderData[];
|
||||
export let startIndex: number;
|
||||
export let pageLength: number;
|
||||
export let enablePremiumFeatures: boolean;
|
||||
|
||||
let filteredItems: FileRenderData[] = [];
|
||||
let plugin: VaultExplorerPlugin | null = null;
|
||||
|
|
@ -100,14 +99,7 @@
|
|||
|
||||
const nativeEvent = e as MouseEvent;
|
||||
const { app, settings } = plugin;
|
||||
openContextMenu(
|
||||
nativeEvent,
|
||||
path,
|
||||
app,
|
||||
settings,
|
||||
enablePremiumFeatures,
|
||||
{},
|
||||
);
|
||||
openContextMenu(nativeEvent, path, app, settings, {});
|
||||
}
|
||||
|
||||
function getValue(item: FileRenderData, column: TColumn): unknown {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
TTableView,
|
||||
} from "src/types";
|
||||
|
||||
import { TExplorerView, } from "src/types";
|
||||
import { TExplorerView } from "src/types";
|
||||
import { onMount } from "svelte";
|
||||
import Flex from "../shared/components/flex.svelte";
|
||||
import IconButton from "../shared/components/icon-button.svelte";
|
||||
|
|
@ -29,7 +29,6 @@
|
|||
import TabList from "../shared/components/tab-list.svelte";
|
||||
import Tab from "../shared/components/tab.svelte";
|
||||
import Wrap from "../shared/components/wrap.svelte";
|
||||
import License from "../shared/services/license";
|
||||
import store from "../shared/services/store";
|
||||
import {
|
||||
getStartOfLastWeekMillis,
|
||||
|
|
@ -109,7 +108,6 @@
|
|||
let randomSortCache: RandomFileSortCache = new Map();
|
||||
|
||||
let isSmallScreenSize: boolean = false;
|
||||
let enablePremiumFeatures: boolean = false;
|
||||
|
||||
//Views
|
||||
let gridView: TGridView = {
|
||||
|
|
@ -151,12 +149,6 @@
|
|||
initialRender = false;
|
||||
});
|
||||
|
||||
License.getInstance()
|
||||
.getHasValidKeyStore()
|
||||
.subscribe((hasValidKey) => {
|
||||
enablePremiumFeatures = hasValidKey;
|
||||
});
|
||||
|
||||
randomFileSortStore.subscribe((value) => {
|
||||
randomSortCache = value;
|
||||
});
|
||||
|
|
@ -1046,7 +1038,6 @@
|
|||
data={renderData}
|
||||
{startIndex}
|
||||
{pageLength}
|
||||
{enablePremiumFeatures}
|
||||
on:coverImageFitChange={handleCoverImageFitChange}
|
||||
/>
|
||||
{:else if currentView === "list"}
|
||||
|
|
@ -1055,22 +1046,11 @@
|
|||
{isSmallScreenSize}
|
||||
{startIndex}
|
||||
{pageLength}
|
||||
{enablePremiumFeatures}
|
||||
/>
|
||||
{:else if currentView === "table"}
|
||||
<TableView
|
||||
data={renderData}
|
||||
{startIndex}
|
||||
{pageLength}
|
||||
{enablePremiumFeatures}
|
||||
/>
|
||||
<TableView data={renderData} {startIndex} {pageLength} />
|
||||
{:else if currentView === "feed"}
|
||||
<FeedView
|
||||
data={renderData}
|
||||
{startIndex}
|
||||
{pageLength}
|
||||
{enablePremiumFeatures}
|
||||
/>
|
||||
<FeedView data={renderData} {startIndex} {pageLength} />
|
||||
{/if}
|
||||
<PaginationIndicator
|
||||
{startIndex}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Menu, Notice } from "obsidian";
|
||||
import { App, Menu } from "obsidian";
|
||||
import type { CoverImageFit, VaultExplorerPluginSettings } from "src/types";
|
||||
|
||||
export const openContextMenu = (
|
||||
|
|
@ -6,7 +6,6 @@ export const openContextMenu = (
|
|||
filePath: string,
|
||||
app: App,
|
||||
settings: VaultExplorerPluginSettings,
|
||||
enablePremiumFeatures: boolean,
|
||||
{
|
||||
coverImageFit,
|
||||
onCoverImageFitChange,
|
||||
|
|
@ -51,12 +50,6 @@ export const openContextMenu = (
|
|||
menu.addItem((item) => {
|
||||
item.setTitle("Delete");
|
||||
item.onClick(async () => {
|
||||
if (!enablePremiumFeatures) {
|
||||
new Notice(
|
||||
"This feature requires a premium Vault Explorer license."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (confirmBeforeDelete) {
|
||||
if (confirm("Are you sure you want to delete this file?")) {
|
||||
await deleteFile(app, filePath);
|
||||
|
|
|
|||
|
|
@ -15,20 +15,9 @@
|
|||
export let condition: FilterCondition;
|
||||
export let isEnabled: boolean;
|
||||
|
||||
import PremiumLink from "src/svelte/shared/components/premium-link.svelte";
|
||||
import PremiumMessage from "src/svelte/shared/components/premium-message.svelte";
|
||||
import License from "src/svelte/shared/services/license";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let hasValidLicenseKey = false;
|
||||
|
||||
License.getInstance()
|
||||
.getHasValidKeyStore()
|
||||
.subscribe((hasValidKey) => {
|
||||
hasValidLicenseKey = hasValidKey;
|
||||
});
|
||||
|
||||
function handleValueChange(e: Event) {
|
||||
const value = (e.target as HTMLInputElement).value;
|
||||
dispatch("ruleValueChange", { id, value });
|
||||
|
|
@ -54,21 +43,12 @@
|
|||
{#if condition !== ContentFilterCondition.IS_EMPTY && condition !== ContentFilterCondition.IS_NOT_EMPTY}
|
||||
<input
|
||||
type="text"
|
||||
disabled={!hasValidLicenseKey}
|
||||
placeholder="value"
|
||||
{value}
|
||||
on:input={handleValueChange}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="after-toggle">
|
||||
{#if type === FilterRuleType.CONTENT && !hasValidLicenseKey}
|
||||
<div>
|
||||
<PremiumMessage />
|
||||
<PremiumLink />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</FilterRule>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import Stack from "src/svelte/shared/components/stack.svelte";
|
||||
import Switch from "src/svelte/shared/components/switch.svelte";
|
||||
import Wrap from "src/svelte/shared/components/wrap.svelte";
|
||||
import License from "src/svelte/shared/services/license";
|
||||
import {
|
||||
CheckboxFilterCondition,
|
||||
ContentFilterCondition,
|
||||
|
|
@ -33,16 +32,8 @@
|
|||
export let condition: FilterCondition;
|
||||
export let isEnabled: boolean;
|
||||
|
||||
let hasValidLicenseKey = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
License.getInstance()
|
||||
.getHasValidKeyStore()
|
||||
.subscribe((hasValidKey) => {
|
||||
hasValidLicenseKey = hasValidKey;
|
||||
});
|
||||
|
||||
function handleActionsClick(e: CustomEvent) {
|
||||
const nativeEvent = e.detail.nativeEvent as MouseEvent;
|
||||
|
||||
|
|
@ -141,11 +132,7 @@
|
|||
{/each}
|
||||
</select>
|
||||
<slot name="before-condition"></slot>
|
||||
<select
|
||||
disabled={type === FilterRuleType.CONTENT && !hasValidLicenseKey}
|
||||
value={condition}
|
||||
on:change={handleConditionChange}
|
||||
>
|
||||
<select value={condition} on:change={handleConditionChange}>
|
||||
{#each filterConditions as condition}
|
||||
<option value={condition}>
|
||||
{getDisplayNameForFilterCondition(condition)}
|
||||
|
|
|
|||
|
|
@ -1,129 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import License from "../shared/services/license";
|
||||
import EventManager from "src/event/event-manager";
|
||||
import PremiumLink from "../shared/components/premium-link.svelte";
|
||||
import { PluginEvent } from "src/event/types";
|
||||
|
||||
interface Message {
|
||||
type: "success" | "failure" | "info";
|
||||
text: string;
|
||||
}
|
||||
|
||||
let hasValidLicenseKey = false;
|
||||
let message: Message | null = null;
|
||||
|
||||
const LICENSE_KEY_SIZE = 148;
|
||||
|
||||
onMount(() => {
|
||||
const hasValidKey = License.getInstance().getHasValidKey();
|
||||
if (hasValidKey) {
|
||||
message = {
|
||||
type: "success",
|
||||
text: "Premium features are enabled.",
|
||||
};
|
||||
}
|
||||
hasValidLicenseKey = hasValidKey;
|
||||
});
|
||||
|
||||
async function handleInputChange(e: Event) {
|
||||
const value = (e.target as HTMLInputElement).value;
|
||||
|
||||
if (value.length < LICENSE_KEY_SIZE) return;
|
||||
|
||||
message = {
|
||||
type: "info",
|
||||
text: "Validating key...",
|
||||
};
|
||||
|
||||
const result = await License.getInstance().addKey(value);
|
||||
|
||||
if (result) {
|
||||
hasValidLicenseKey = true;
|
||||
message = {
|
||||
type: "success",
|
||||
text: "Premium features are enabled.",
|
||||
};
|
||||
EventManager.getInstance().emit(
|
||||
PluginEvent.LICENSE_KEY_VALIDATION_CHANGE,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
hasValidLicenseKey = false;
|
||||
message = {
|
||||
type: "failure",
|
||||
text: "Invalid key.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function handleRemoveButtonClick() {
|
||||
License.getInstance().removeKey();
|
||||
hasValidLicenseKey = false;
|
||||
message = null;
|
||||
EventManager.getInstance().emit(
|
||||
PluginEvent.LICENSE_KEY_VALIDATION_CHANGE,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
function getMessageClassName(message: Message | null) {
|
||||
let className = "vault-explorer-setting-message";
|
||||
if (message !== null) {
|
||||
const { type } = message;
|
||||
if (type === "success") {
|
||||
className += " vault-explorer-setting-message--success";
|
||||
} else if (type === "failure") {
|
||||
className += " vault-explorer-setting-message--failure";
|
||||
}
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
$: messageClassName = getMessageClassName(message);
|
||||
</script>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-item-info">
|
||||
<div class="setting-item-name">License key</div>
|
||||
<div class="setting-item-description">
|
||||
<div>Enter your license key to enable premium features.</div>
|
||||
{#if message == null}
|
||||
<PremiumLink />
|
||||
{/if}
|
||||
{#if message != null}
|
||||
<div class={messageClassName}>
|
||||
{message?.text ?? ""}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item-control">
|
||||
{#if hasValidLicenseKey === false}
|
||||
<input
|
||||
type="text"
|
||||
maxlength={LICENSE_KEY_SIZE}
|
||||
on:input={handleInputChange}
|
||||
/>
|
||||
{/if}
|
||||
{#if hasValidLicenseKey === true}
|
||||
<button class="mod-destructive" on:click={handleRemoveButtonClick}
|
||||
>Remove key</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.vault-explorer-setting-message {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.vault-explorer-setting-message--success {
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
.vault-explorer-setting-message--failure {
|
||||
color: var(--color-red);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<a href="https://vaultexplorer.com/docs/premium/" target="_blank" rel="noopener"
|
||||
>Learn more</a
|
||||
>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<div class="vault-explorer-premium-message">
|
||||
This feature requires a premium license
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.vault-explorer-premium-message {
|
||||
font-size: var(--font-smallest);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
import Logger from "js-logger";
|
||||
import { writable } from "svelte/store";
|
||||
import crypto from "crypto";
|
||||
|
||||
const LOCAL_STORAGE_LICENSE_KEY = "vault-explorer-license";
|
||||
|
||||
const PUBLIC_KEY_PEM = `
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAO539qAsgBzbukUNDuOtPZKXNj8MSXvt3zS1ci4plDBA=
|
||||
-----END PUBLIC KEY-----
|
||||
`;
|
||||
|
||||
export default class License {
|
||||
private licenseKey: string;
|
||||
private hasValidKey: boolean;
|
||||
private hasValidKeyStore = writable<boolean>();
|
||||
|
||||
private static instance: License;
|
||||
|
||||
constructor() {
|
||||
this.licenseKey = "";
|
||||
this.hasValidKey = false;
|
||||
this.hasValidKeyStore.set(false);
|
||||
}
|
||||
|
||||
async loadStoredKey() {
|
||||
const storedKey = this.getStoredLicenseKey();
|
||||
if (storedKey) {
|
||||
const isValid = await this.validateKey(storedKey);
|
||||
|
||||
this.licenseKey = storedKey;
|
||||
this.hasValidKey = isValid;
|
||||
this.hasValidKeyStore.set(isValid);
|
||||
}
|
||||
}
|
||||
|
||||
async addKey(licenseKey: string) {
|
||||
Logger.trace({
|
||||
fileName: "license.ts",
|
||||
functionName: "addKey",
|
||||
message: "called",
|
||||
});
|
||||
|
||||
const result = await this.validateKey(licenseKey);
|
||||
if (result) {
|
||||
this.setStoredKey(licenseKey);
|
||||
this.hasValidKeyStore.set(true);
|
||||
this.hasValidKey = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the licenseKey using the public key.
|
||||
* @param signature- The licenseKey to verify. This is created by signing a file with the private key.
|
||||
*/
|
||||
async validateKey(licenseKey: string) {
|
||||
Logger.trace({
|
||||
fileName: "license.ts",
|
||||
functionName: "validateKey",
|
||||
message: "called",
|
||||
});
|
||||
|
||||
try {
|
||||
// Decode Base64 to buffer
|
||||
const decodedBuffer = Buffer.from(licenseKey, "base64");
|
||||
const decodedString = decodedBuffer.toString("utf-8");
|
||||
const split = decodedString.split("|");
|
||||
|
||||
const signatureBase64 = split[0];
|
||||
const data = split[1].trim();
|
||||
|
||||
const signatureBuffer = Buffer.from(signatureBase64, "base64");
|
||||
const dataBuffer = Buffer.from(data);
|
||||
|
||||
const verify = crypto.createVerify("SHA256");
|
||||
verify.update(data);
|
||||
verify.end();
|
||||
|
||||
return crypto.verify(
|
||||
null,
|
||||
dataBuffer,
|
||||
{
|
||||
key: PUBLIC_KEY_PEM,
|
||||
format: "pem",
|
||||
type: "spki",
|
||||
},
|
||||
signatureBuffer
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
removeKey() {
|
||||
Logger.trace({
|
||||
fileName: "license.ts",
|
||||
functionName: "removeKey",
|
||||
message: "called",
|
||||
});
|
||||
|
||||
this.setStoredKey(null);
|
||||
this.hasValidKeyStore.set(false);
|
||||
this.hasValidKey = false;
|
||||
}
|
||||
|
||||
private setStoredKey(value: string | null) {
|
||||
Logger.trace({
|
||||
fileName: "license.ts",
|
||||
functionName: "setStoredKey",
|
||||
message: "called",
|
||||
});
|
||||
if (value !== null) {
|
||||
localStorage.setItem(LOCAL_STORAGE_LICENSE_KEY, value);
|
||||
} else {
|
||||
localStorage.removeItem(LOCAL_STORAGE_LICENSE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private getStoredLicenseKey() {
|
||||
return localStorage.getItem(LOCAL_STORAGE_LICENSE_KEY);
|
||||
}
|
||||
|
||||
getHasValidKey() {
|
||||
return this.hasValidKey;
|
||||
}
|
||||
|
||||
getHasValidKeyStore() {
|
||||
return this.hasValidKeyStore;
|
||||
}
|
||||
|
||||
getLicenseKey() {
|
||||
return this.licenseKey;
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new License();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue