mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 09:48:28 +02:00
Show error when enabling encryption fails
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::window_menu::app_menu;
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle, Emitter, LogicalSize, Manager, Runtime, WebviewUrl, WebviewWindow, WindowEvent,
|
AppHandle, Emitter, LogicalSize, Manager, PhysicalSize, Runtime, WebviewUrl, WebviewWindow, WindowEvent
|
||||||
};
|
};
|
||||||
use tauri_plugin_opener::OpenerExt;
|
use tauri_plugin_opener::OpenerExt;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
@@ -160,6 +160,11 @@ pub(crate) fn create_window<R: Runtime>(
|
|||||||
"dev.reset_size" => webview_window
|
"dev.reset_size" => webview_window
|
||||||
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
"dev.reset_size_record" => {
|
||||||
|
let width = webview_window.outer_size().unwrap().width;
|
||||||
|
let height = width * 9 / 16;
|
||||||
|
webview_window.set_size(PhysicalSize::new(width, height)).unwrap()
|
||||||
|
}
|
||||||
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
||||||
"dev.generate_theme_css" => {
|
"dev.generate_theme_css" => {
|
||||||
w.emit("generate_theme_css", true).unwrap();
|
w.emit("generate_theme_css", true).unwrap();
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ pub fn app_menu<R: Runtime>(app_handle: &AppHandle<R>) -> tauri::Result<Menu<R>>
|
|||||||
.build(app_handle)?,
|
.build(app_handle)?,
|
||||||
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
|
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
|
||||||
.build(app_handle)?,
|
.build(app_handle)?,
|
||||||
|
&MenuItemBuilder::with_id("dev.reset_size_record".to_string(), "Reset Size 16x9")
|
||||||
|
.build(app_handle)?,
|
||||||
&MenuItemBuilder::with_id(
|
&MenuItemBuilder::with_id(
|
||||||
"dev.generate_theme_css".to_string(),
|
"dev.generate_theme_css".to_string(),
|
||||||
"Generate Theme CSS",
|
"Generate Theme CSS",
|
||||||
|
|||||||
@@ -1,35 +1,49 @@
|
|||||||
import { enableEncryption, revealWorkspaceKey, setWorkspaceKey } from '@yaakapp-internal/crypto';
|
import {
|
||||||
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
enableEncryption,
|
||||||
import classNames from 'classnames';
|
revealWorkspaceKey,
|
||||||
import { useAtomValue } from 'jotai';
|
setWorkspaceKey,
|
||||||
import { useEffect, useState } from 'react';
|
} from "@yaakapp-internal/crypto";
|
||||||
import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../hooks/useActiveWorkspace';
|
import type { WorkspaceMeta } from "@yaakapp-internal/models";
|
||||||
import { createFastMutation } from '../hooks/useFastMutation';
|
import classNames from "classnames";
|
||||||
import { useStateWithDeps } from '../hooks/useStateWithDeps';
|
import { useAtomValue } from "jotai";
|
||||||
import { CopyIconButton } from './CopyIconButton';
|
import { useEffect, useState } from "react";
|
||||||
import { Banner } from './core/Banner';
|
import {
|
||||||
import type { ButtonProps } from './core/Button';
|
activeWorkspaceAtom,
|
||||||
import { Button } from './core/Button';
|
activeWorkspaceMetaAtom,
|
||||||
import { IconButton } from './core/IconButton';
|
} from "../hooks/useActiveWorkspace";
|
||||||
import { IconTooltip } from './core/IconTooltip';
|
import { createFastMutation } from "../hooks/useFastMutation";
|
||||||
import { Label } from './core/Label';
|
import { useStateWithDeps } from "../hooks/useStateWithDeps";
|
||||||
import { PlainInput } from './core/PlainInput';
|
import { CopyIconButton } from "./CopyIconButton";
|
||||||
import { HStack, VStack } from './core/Stacks';
|
import { Banner } from "./core/Banner";
|
||||||
import { EncryptionHelp } from './EncryptionHelp';
|
import type { ButtonProps } from "./core/Button";
|
||||||
|
import { Button } from "./core/Button";
|
||||||
|
import { IconButton } from "./core/IconButton";
|
||||||
|
import { IconTooltip } from "./core/IconTooltip";
|
||||||
|
import { Label } from "./core/Label";
|
||||||
|
import { PlainInput } from "./core/PlainInput";
|
||||||
|
import { HStack, VStack } from "./core/Stacks";
|
||||||
|
import { EncryptionHelp } from "./EncryptionHelp";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: ButtonProps['size'];
|
size?: ButtonProps["size"];
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
onDone?: () => void;
|
onDone?: () => void;
|
||||||
onEnabledEncryption?: () => void;
|
onEnabledEncryption?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEncryption }: Props) {
|
export function WorkspaceEncryptionSetting(
|
||||||
const [justEnabledEncryption, setJustEnabledEncryption] = useState<boolean>(false);
|
{ size, expanded, onDone, onEnabledEncryption }: Props,
|
||||||
|
) {
|
||||||
|
const [justEnabledEncryption, setJustEnabledEncryption] = useState<boolean>(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const workspace = useAtomValue(activeWorkspaceAtom);
|
const workspace = useAtomValue(activeWorkspaceAtom);
|
||||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||||
const [key, setKey] = useState<{ key: string | null; error: string | null } | null>(null);
|
const [key, setKey] = useState<
|
||||||
|
{ key: string | null; error: string | null } | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspaceMeta == null) {
|
if (workspaceMeta == null) {
|
||||||
@@ -108,30 +122,39 @@ export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEn
|
|||||||
return (
|
return (
|
||||||
<div className="mb-auto flex flex-col-reverse">
|
<div className="mb-auto flex flex-col-reverse">
|
||||||
<Button
|
<Button
|
||||||
color={expanded ? 'info' : 'secondary'}
|
color={expanded ? "info" : "secondary"}
|
||||||
size={size}
|
size={size}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setJustEnabledEncryption(true);
|
setError(null);
|
||||||
await enableEncryption(workspaceMeta.workspaceId);
|
try {
|
||||||
|
throw new Error("Platform secure storage error");
|
||||||
|
await enableEncryption(workspaceMeta.workspaceId);
|
||||||
|
setJustEnabledEncryption(true);
|
||||||
|
} catch (err) {
|
||||||
|
setError("Failed to enable encryption: " + err);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enable Encryption
|
Enable Encryption
|
||||||
</Button>
|
</Button>
|
||||||
{expanded ? (
|
{error && <Banner color="danger" className="mb-2">{error}</Banner>}
|
||||||
<Banner color="info" className="mb-6">
|
{expanded
|
||||||
<EncryptionHelp />
|
? (
|
||||||
</Banner>
|
<Banner color="info" className="mb-6">
|
||||||
) : (
|
<EncryptionHelp />
|
||||||
<Label htmlFor={null} help={<EncryptionHelp />}>
|
</Banner>
|
||||||
Workspace encryption
|
)
|
||||||
</Label>
|
: (
|
||||||
)}
|
<Label htmlFor={null} help={<EncryptionHelp />}>
|
||||||
|
Workspace encryption
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const setWorkspaceKeyMut = createFastMutation({
|
const setWorkspaceKeyMut = createFastMutation({
|
||||||
mutationKey: ['set-workspace-key'],
|
mutationKey: ["set-workspace-key"],
|
||||||
mutationFn: setWorkspaceKey,
|
mutationFn: setWorkspaceKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -144,15 +167,13 @@ function EnterWorkspaceKey({
|
|||||||
onEnabled?: () => void;
|
onEnabled?: () => void;
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
}) {
|
}) {
|
||||||
const [key, setKey] = useState<string>('');
|
const [key, setKey] = useState<string>("");
|
||||||
return (
|
return (
|
||||||
<VStack space={4} className="w-full">
|
<VStack space={4} className="w-full">
|
||||||
{error ? (
|
{error ? <Banner color="danger">{error}</Banner> : (
|
||||||
<Banner color="danger">{error}</Banner>
|
|
||||||
) : (
|
|
||||||
<Banner color="info">
|
<Banner color="info">
|
||||||
This workspace contains encrypted values but no key is configured. Please enter the
|
This workspace contains encrypted values but no key is configured.
|
||||||
workspace key to access the encrypted data.
|
Please enter the workspace key to access the encrypted data.
|
||||||
</Banner>
|
</Banner>
|
||||||
)}
|
)}
|
||||||
<HStack
|
<HStack
|
||||||
@@ -199,24 +220,35 @@ function KeyRevealer({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'w-full border border-border rounded-md pl-3 py-2 p-1',
|
"w-full border border-border rounded-md pl-3 py-2 p-1",
|
||||||
'grid gap-1 grid-cols-[minmax(0,1fr)_auto] items-center',
|
"grid gap-1 grid-cols-[minmax(0,1fr)_auto] items-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<VStack space={0.5}>
|
<VStack space={0.5}>
|
||||||
{!disableLabel && (
|
{!disableLabel && (
|
||||||
<span className="text-sm text-primary flex items-center gap-1">
|
<span className="text-sm text-primary flex items-center gap-1">
|
||||||
Workspace encryption key{' '}
|
Workspace encryption key{" "}
|
||||||
<IconTooltip iconSize="sm" size="lg" content={helpAfterEncryption} />
|
<IconTooltip
|
||||||
|
iconSize="sm"
|
||||||
|
size="lg"
|
||||||
|
content={helpAfterEncryption}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{encryptionKey && <HighlightedKey keyText={encryptionKey} show={show} />}
|
{encryptionKey && (
|
||||||
|
<HighlightedKey
|
||||||
|
keyText={encryptionKey}
|
||||||
|
show={show}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
<HStack>
|
<HStack>
|
||||||
{encryptionKey && <CopyIconButton text={encryptionKey} title="Copy workspace key" />}
|
{encryptionKey && (
|
||||||
|
<CopyIconButton text={encryptionKey} title="Copy workspace key" />
|
||||||
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
title={show ? 'Hide' : 'Reveal' + 'workspace key'}
|
title={show ? "Hide" : "Reveal" + "workspace key"}
|
||||||
icon={show ? 'eye_closed' : 'eye'}
|
icon={show ? "eye_closed" : "eye"}
|
||||||
onClick={() => setShow((v) => !v)}
|
onClick={() => setShow((v) => !v)}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -227,31 +259,32 @@ function KeyRevealer({
|
|||||||
function HighlightedKey({ keyText, show }: { keyText: string; show: boolean }) {
|
function HighlightedKey({ keyText, show }: { keyText: string; show: boolean }) {
|
||||||
return (
|
return (
|
||||||
<span className="text-xs font-mono [&_*]:cursor-auto [&_*]:select-text">
|
<span className="text-xs font-mono [&_*]:cursor-auto [&_*]:select-text">
|
||||||
{show ? (
|
{show
|
||||||
keyText.split('').map((c, i) => {
|
? (
|
||||||
return (
|
keyText.split("").map((c, i) => {
|
||||||
<span
|
return (
|
||||||
key={i}
|
<span
|
||||||
className={classNames(
|
key={i}
|
||||||
c.match(/[0-9]/) && 'text-info',
|
className={classNames(
|
||||||
c == '-' && 'text-text-subtle',
|
c.match(/[0-9]/) && "text-info",
|
||||||
)}
|
c == "-" && "text-text-subtle",
|
||||||
>
|
)}
|
||||||
{c}
|
>
|
||||||
</span>
|
{c}
|
||||||
);
|
</span>
|
||||||
})
|
);
|
||||||
) : (
|
})
|
||||||
<div className="text-text-subtle">•••••••••••••••••••••</div>
|
)
|
||||||
)}
|
: <div className="text-text-subtle">•••••••••••••••••••••</div>}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const helpAfterEncryption = (
|
const helpAfterEncryption = (
|
||||||
<p>
|
<p>
|
||||||
The following key is used for encryption operations within this workspace. It is stored securely
|
The following key is used for encryption operations within this workspace.
|
||||||
using your OS keychain, but it is recommended to back it up. If you share this workspace with
|
It is stored securely using your OS keychain, but it is recommended to back
|
||||||
others, you'll need to send them this key to access any encrypted values.
|
it up. If you share this workspace with others, you'll need to send
|
||||||
|
them this key to access any encrypted values.
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user