update license validation & urls

Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
FTCHD
2025-08-14 22:46:14 +02:00
parent cc3b35a4ea
commit 8c9ebecd67
4 changed files with 102 additions and 81 deletions
+11 -9
View File
@@ -1,5 +1,6 @@
import { invoke } from '@tauri-apps/api/core' import { invoke } from '@tauri-apps/api/core'
import { fetch } from '@tauri-apps/plugin-http' import { fetch } from '@tauri-apps/plugin-http'
import { platform } from '@tauri-apps/plugin-os'
import { usePersistedStore } from './store' import { usePersistedStore } from './store'
export async function validateLicense(licenseKey: string) { export async function validateLicense(licenseKey: string) {
@@ -20,11 +21,12 @@ export async function validateLicense(licenseKey: string) {
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
const validationResponse = await fetch('https://rcloneui.com/api/v1/validate', { const validationResponse = await fetch('https://rcloneui.com/api/v2/validate', {
method: 'POST', method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
licenseKey, licenseKey,
id, id,
platform: platform(),
}), }),
}) })
.then((r) => r.json()) .then((r) => r.json())
@@ -49,21 +51,21 @@ export async function validateLicense(licenseKey: string) {
console.log('[validateLicense] license validated') console.log('[validateLicense] license validated')
} }
export async function revokeLicense(licenseKey: string) { export async function revokeMachineLicense(licenseKey: string) {
console.log('[revokeLicense]') console.log('[revokeMachineLicense]')
let id let id
try { try {
id = await invoke('get_uid') id = await invoke('get_uid')
} catch (e) { } catch (e) {
console.error('[revokeLicense] failed to build unique identifier') console.error('[revokeMachineLicense] failed to build unique identifier')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
if (!id) { if (!id) {
console.error('[revokeLicense] missing unique identifier') console.error('[revokeMachineLicense] missing unique identifier')
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
@@ -76,22 +78,22 @@ export async function revokeLicense(licenseKey: string) {
}) })
.then((r) => r.json()) .then((r) => r.json())
.catch((e) => { .catch((e) => {
console.error('[revokeLicense] failed to revoke license, fetch failed') console.error('[revokeMachineLicense] failed to revoke license, fetch failed')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to revoke license. Are you connected to the internet?') throw new Error('Failed to revoke license. Are you connected to the internet?')
}) })
if (revocationResponse.error) { if (revocationResponse.error) {
console.error('[revokeLicense] failed to revoke license, has error response') console.error('[revokeMachineLicense] failed to revoke license, has error response')
throw new Error(revocationResponse.error) throw new Error(revocationResponse.error)
} }
if (!revocationResponse.revoked) { if (!revocationResponse.revoked) {
console.error('[revokeLicense] failed to revoke license, missing revoked response') console.error('[revokeMachineLicense] failed to revoke license, missing revoked response')
throw new Error('Failed to revoke license. Please check your license key and try again.') throw new Error('Failed to revoke license. Please check your license key and try again.')
} }
usePersistedStore.setState({ licenseKey: undefined, licenseValid: false }) usePersistedStore.setState({ licenseKey: undefined, licenseValid: false })
console.log('[revokeLicense] license revoked') console.log('[revokeMachineLicense] license revoked')
} }
+7 -9
View File
@@ -73,24 +73,22 @@ async function validateInstance() {
try { try {
await validateLicense(licenseKey) await validateLicense(licenseKey)
} catch (e) { } catch (e) {
usePersistedStore.setState({ licenseValid: false })
if (e instanceof Error) { if (e instanceof Error) {
await ask(e.message, { await message(e.message, {
title: 'Error', title: 'Error Validating License',
kind: 'error', kind: 'error',
okLabel: 'Exit', okLabel: 'OK',
cancelLabel: '',
}) })
await exit(0)
return return
} }
await ask('An error occurred while validating your license. Please try again.', { await message('An error occurred while validating your license. Please try again.', {
title: 'Error', title: 'Error',
kind: 'error', kind: 'error',
okLabel: 'Exit', okLabel: 'OK',
cancelLabel: '',
}) })
await exit(0)
} }
} }
+24
View File
@@ -285,8 +285,32 @@
{ {
"url": "https://github.com/**" "url": "https://github.com/**"
}, },
{
"url": "https://raw.githubusercontent.com/**"
},
{ {
"url": "https://rcloneui.com/**" "url": "https://rcloneui.com/**"
},
{
"url": "https://stripe.com/**"
},
{
"url": "https://buy.stripe.com/**"
},
{
"url": "https://checkout.stripe.com/**"
},
{
"url": "https://js.stripe.com/**"
},
{
"url": "https://donate.stripe.com/**"
},
{
"url": "https://payments.stripe.com/**"
},
{
"url": "https://checkout.link.com/**"
} }
] ]
}, },
+60 -63
View File
@@ -13,6 +13,7 @@ import {
Tab, Tab,
Tabs, Tabs,
Textarea, Textarea,
cn,
} from '@heroui/react' } from '@heroui/react'
import { getTauriVersion, getVersion as getUiVersion } from '@tauri-apps/api/app' import { getTauriVersion, getVersion as getUiVersion } from '@tauri-apps/api/app'
import { import {
@@ -45,8 +46,14 @@ import {
ServerIcon, ServerIcon,
Trash2Icon, Trash2Icon,
} from 'lucide-react' } from 'lucide-react'
import { useCallback, useEffect, useState } from 'react' import {
import { revokeLicense, validateLicense } from '../../lib/license' type DetailedHTMLProps,
type HTMLAttributes,
useCallback,
useEffect,
useState,
} from 'react'
import { revokeMachineLicense, validateLicense } from '../../lib/license'
import { deleteRemote, getVersion as getCliVersion, getVersion } from '../../lib/rclone/api' import { deleteRemote, getVersion as getCliVersion, getVersion } from '../../lib/rclone/api'
import { getConfigPath, getDefaultPaths } from '../../lib/rclone/common' import { getConfigPath, getDefaultPaths } from '../../lib/rclone/common'
import { usePersistedStore, useStore } from '../../lib/store' import { usePersistedStore, useStore } from '../../lib/store'
@@ -58,6 +65,15 @@ import RemoteCreateDrawer from '../components/RemoteCreateDrawer'
import RemoteDefaultsDrawer from '../components/RemoteDefaultsDrawer' import RemoteDefaultsDrawer from '../components/RemoteDefaultsDrawer'
import RemoteEditDrawer from '../components/RemoteEditDrawer' import RemoteEditDrawer from '../components/RemoteEditDrawer'
declare global {
// biome-ignore lint/style/noNamespace: <explanation>
namespace JSX {
interface IntrinsicElements {
'stripe-pricing-table': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>
}
}
}
function Settings() { function Settings() {
const settingsPass = usePersistedStore((state) => state.settingsPass) const settingsPass = usePersistedStore((state) => state.settingsPass)
@@ -550,13 +566,30 @@ function LicenseSection() {
const [isActivating, setIsActivating] = useState(false) const [isActivating, setIsActivating] = useState(false)
const [licenseKeyInput, setLicenseKeyInput] = useState('') const [licenseKeyInput, setLicenseKeyInput] = useState('')
const [showPricingTable, setShowPricingTable] = useState(false)
useEffect(() => { useEffect(() => {
setLicenseKeyInput(licenseKey || '') setLicenseKeyInput(licenseKey || '')
setIsLicenseEditable(!licenseKey) setIsLicenseEditable(!licenseKey)
}, [licenseKey]) }, [licenseKey])
useEffect(() => {
const script = document.createElement('script')
script.src = 'https://js.stripe.com/v3/pricing-table.js'
script.async = true
document.body.appendChild(script)
const cancelTimeout = setTimeout(() => {
setShowPricingTable(true)
}, 1000)
return () => {
clearTimeout(cancelTimeout)
setShowPricingTable(false)
document.body.removeChild(script)
}
}, [])
return ( return (
<div className="flex flex-col gap-10"> <div className="flex flex-col gap-8">
<BaseHeader title="License" /> <BaseHeader title="License" />
<div className="flex flex-row justify-center w-full gap-8 px-8"> <div className="flex flex-row justify-center w-full gap-8 px-8">
@@ -654,7 +687,7 @@ function LicenseSection() {
setIsRevoking(true) setIsRevoking(true)
try { try {
await revokeLicense(licenseKeyInput) await revokeMachineLicense(licenseKeyInput)
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
await ask(e.message, { await ask(e.message, {
@@ -689,65 +722,17 @@ function LicenseSection() {
</div> </div>
</div> </div>
<div className="flex flex-row justify-center w-full gap-8 px-8"> <div
<div className="flex flex-col items-end w-2/6 gap-2 bg-transparent-500"> className={cn(
<h3 className="font-medium">Buy</h3> 'w-full overflow-hidden border-0 border-red-500 left-28 h-[470px] opacity-0 transition-opacity duration-300 ease-in-out',
showPricingTable && 'opacity-100'
<p className="text-xs text-neutral-500 text-end"> )}
Includes access to future features and updates. >
</p> <stripe-pricing-table
</div> pricing-table-id="prctbl_1RvnumE0hPdsH0naQ6l8Rd86"
publishable-key="pk_live_51QmUqyE0hPdsH0naBICHzb0j5O5eTKyYnY72nOaS6aT99y3EBeCOyeihI2xX05D6cczifqPsX6vHhor8ozSblXPl00LqNwMxBE"
<div className="flex flex-col w-4/6 gap-3 bg-transparent-500"> />
<Button
size="lg"
fullWidth={true}
color="primary"
variant="shadow"
onPress={async () => {
await openUrl('https://buy.stripe.com/3csbKIb1t433a8obII')
}}
>
Lifetime License $7
</Button>
</div>
</div> </div>
{/* <div className="flex flex-row justify-center w-full gap-8 px-8">
<div className="flex flex-col items-end w-2/6 gap-2 bg-transparent-500">
<h3 className="font-medium">Features</h3>
<p className="text-xs text-neutral-500 text-end">What you get with a license</p>
</div>
<div className="flex flex-col w-4/6 gap-2 bg-transparent-500">
<Card className="border-none bg-background/60 dark:bg-default-100/60">
<CardBody>
<ul className="flex flex-col gap-3">
<li className="flex items-center gap-2">
<CheckIcon className="w-5 h-5 text-green-500" />
<span>Work with more than 3 remotes</span>
</li>
<li className="flex items-center gap-2">
<CheckIcon className="w-5 h-5 text-green-500" />
<span>Runs on up to 5 devices</span>
</li>
<li className="flex items-center gap-2">
<CheckIcon className="w-5 h-5 text-green-500" />
<span>File Commander</span>
</li>
<li className="flex items-center gap-2">
<CheckIcon className="w-5 h-5 text-green-500" />
<span>Mobile Client</span>
</li>
<li className="flex items-center gap-2">
<CheckIcon className="w-5 h-5 text-green-500" />
<span>Supporting Open Source</span>
</li>
</ul>
</CardBody>
</Card>
</div>
</div> */}
</div> </div>
) )
} }
@@ -917,6 +902,8 @@ function RemotesSection() {
} }
function ConfigSection() { function ConfigSection() {
const licenseValid = usePersistedStore((state) => state.licenseValid)
const configFiles = usePersistedStore((state) => state.configFiles) const configFiles = usePersistedStore((state) => state.configFiles)
const activeConfigFile = usePersistedStore((state) => state.activeConfigFile) const activeConfigFile = usePersistedStore((state) => state.activeConfigFile)
@@ -938,10 +925,20 @@ function ConfigSection() {
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu <DropdownMenu
onAction={(key) => { onAction={(key) => {
setTimeout(() => { setTimeout(async () => {
if (key === 'import') { if (key === 'import') {
setIsCreateDrawerOpen(true) setIsCreateDrawerOpen(true)
} else { } else {
if (!licenseValid) {
await message(
'Community version does not support syncing configs.',
{
title: 'Missing license',
kind: 'error',
}
)
return
}
setIsSyncDrawerOpen(true) setIsSyncDrawerOpen(true)
} }
}, 100) }, 100)