mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 12:13:27 +00:00
core startup error handling (#579)
* core startup error handling * lazy load `PasswordMeter` so that the huge `@zxcvbn-ts` isn't in the core bundle * please clippy --------- Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
parent
9aa2832530
commit
7192ead2c2
|
@ -57,7 +57,7 @@ pub(super) async fn setup<R: Runtime>(
|
|||
.expect("Error with HTTP server!");
|
||||
});
|
||||
|
||||
app.plugin(spacedrive_plugin_init(&auth_token, listen_addr))
|
||||
app.plugin(tauri_plugin(&auth_token, listen_addr))
|
||||
}
|
||||
|
||||
async fn auth_middleware<B>(
|
||||
|
@ -83,11 +83,8 @@ async fn auth_middleware<B>(
|
|||
(StatusCode::UNAUTHORIZED, "Unauthorized!").into_response()
|
||||
}
|
||||
|
||||
pub fn spacedrive_plugin_init<R: Runtime>(
|
||||
auth_token: &str,
|
||||
listen_addr: SocketAddr,
|
||||
) -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("spacedrive")
|
||||
fn tauri_plugin<R: Runtime>(auth_token: &str, listen_addr: SocketAddr) -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("spacedrive-linux")
|
||||
.js_init_script(format!(
|
||||
r#"window.__SD_CUSTOM_SERVER_AUTH_TOKEN__ = "{auth_token}"; window.__SD_CUSTOM_URI_SERVER__ = "http://{listen_addr}";"#
|
||||
))
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::{error::Error, path::PathBuf, sync::Arc, time::Duration};
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use sd_core::{custom_uri::create_custom_uri_endpoint, Node};
|
||||
use sd_core::{custom_uri::create_custom_uri_endpoint, Node, NodeError};
|
||||
|
||||
use tauri::{api::path, async_runtime::block_on, Manager, RunEvent};
|
||||
use tauri::{api::path, async_runtime::block_on, plugin::TauriPlugin, Manager, RunEvent, Runtime};
|
||||
use tokio::{task::block_in_place, time::sleep};
|
||||
use tracing::{debug, error};
|
||||
|
||||
|
@ -26,8 +26,14 @@ async fn app_ready(app_handle: tauri::AppHandle) {
|
|||
window.show().unwrap();
|
||||
}
|
||||
|
||||
pub fn tauri_error_plugin<R: Runtime>(err: NodeError) -> TauriPlugin<R> {
|
||||
tauri::plugin::Builder::new("spacedrive")
|
||||
.js_init_script(format!(r#"window.__SD_ERROR__ = "{err}";"#))
|
||||
.build()
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
async fn main() -> tauri::Result<()> {
|
||||
let data_dir = path::data_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("./"))
|
||||
.join("spacedrive");
|
||||
|
@ -35,21 +41,32 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
#[cfg(debug_assertions)]
|
||||
let data_dir = data_dir.join("dev");
|
||||
|
||||
let (node, router) = Node::new(data_dir).await?;
|
||||
let result = Node::new(data_dir).await;
|
||||
|
||||
let app = tauri::Builder::default().plugin(rspc::integrations::tauri::plugin(router, {
|
||||
let node = Arc::clone(&node);
|
||||
move || node.get_request_context()
|
||||
}));
|
||||
let app = tauri::Builder::default();
|
||||
let (node, app) = match result {
|
||||
Ok((node, router)) => {
|
||||
// This is a super cringe workaround for: https://github.com/tauri-apps/tauri/issues/3725 & https://bugs.webkit.org/show_bug.cgi?id=146351#c5
|
||||
let endpoint = create_custom_uri_endpoint(Arc::clone(&node));
|
||||
|
||||
// This is a super cringe workaround for: https://github.com/tauri-apps/tauri/issues/3725 & https://bugs.webkit.org/show_bug.cgi?id=146351#c5
|
||||
let endpoint = create_custom_uri_endpoint(Arc::clone(&node));
|
||||
#[cfg(target_os = "linux")]
|
||||
let app = app_linux::setup(app, Arc::clone(&node), endpoint).await;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let app = app_linux::setup(app, Arc::clone(&node), endpoint).await;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let app = app.register_uri_scheme_protocol(
|
||||
"spacedrive",
|
||||
endpoint.tauri_uri_scheme("spacedrive"),
|
||||
);
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let app = app.register_uri_scheme_protocol("spacedrive", endpoint.tauri_uri_scheme("spacedrive"));
|
||||
let app = app.plugin(rspc::integrations::tauri::plugin(router, {
|
||||
let node = Arc::clone(&node);
|
||||
move || node.get_request_context()
|
||||
}));
|
||||
|
||||
(Some(node), app)
|
||||
}
|
||||
Err(err) => (None, app.plugin(tauri_error_plugin(err))),
|
||||
};
|
||||
|
||||
let app = app
|
||||
.setup(|app| {
|
||||
|
@ -104,7 +121,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
});
|
||||
|
||||
block_in_place(|| block_on(node.shutdown()));
|
||||
if let Some(node) = &node {
|
||||
block_in_place(|| block_on(node.shutdown()));
|
||||
}
|
||||
app_handler.exit(0);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useEffect } from 'react';
|
|||
import { getDebugState, hooks, queryClient } from '@sd/client';
|
||||
import SpacedriveInterface, { OperatingSystem, Platform, PlatformProvider } from '@sd/interface';
|
||||
import { KeybindEvent } from '@sd/interface';
|
||||
import { ErrorPage } from '@sd/interface';
|
||||
import '@sd/ui/style';
|
||||
|
||||
const client = hooks.createClient({
|
||||
|
@ -33,6 +34,7 @@ async function getOs(): Promise<OperatingSystem> {
|
|||
|
||||
let customUriServerUrl = (window as any).__SD_CUSTOM_URI_SERVER__ as string | undefined;
|
||||
const customUriAuthToken = (window as any).__SD_CUSTOM_URI_TOKEN__ as string | undefined;
|
||||
const startupError = (window as any).__SD_ERROR__ as string | undefined;
|
||||
|
||||
if (customUriServerUrl && !customUriServerUrl?.endsWith('/')) {
|
||||
customUriServerUrl += '/';
|
||||
|
@ -79,6 +81,10 @@ export default function App() {
|
|||
};
|
||||
}, []);
|
||||
|
||||
if (startupError) {
|
||||
return <ErrorPage message={startupError} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<hooks.Provider client={client} queryClient={queryClient}>
|
||||
<PlatformProvider platform={platform}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { relativeAliasResolver } from '@sd/config/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { defineConfig } from 'vite';
|
||||
import { createHtmlPlugin } from 'vite-plugin-html';
|
||||
import svg from 'vite-plugin-svgr';
|
||||
|
@ -18,11 +18,11 @@ export default defineConfig({
|
|||
svg({ svgrOptions: { icon: true } }),
|
||||
createHtmlPlugin({
|
||||
minify: true
|
||||
}),
|
||||
visualizer({
|
||||
gzipSize: true,
|
||||
brotliSize: true
|
||||
})
|
||||
// visualizer({
|
||||
// gzipSize: true,
|
||||
// brotliSize: true
|
||||
// })
|
||||
],
|
||||
resolve: {
|
||||
alias: [relativeAliasResolver]
|
||||
|
|
|
@ -4,11 +4,27 @@ import { useDebugState } from '@sd/client';
|
|||
import { Button } from '@sd/ui';
|
||||
|
||||
export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||
const onClick = () => {
|
||||
captureException(error);
|
||||
resetErrorBoundary();
|
||||
};
|
||||
return (
|
||||
<ErrorPage
|
||||
message={error.message}
|
||||
sendReportBtn={() => {
|
||||
captureException(error);
|
||||
resetErrorBoundary();
|
||||
}}
|
||||
reloadBtn={resetErrorBoundary}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ErrorPage({
|
||||
reloadBtn,
|
||||
sendReportBtn,
|
||||
message
|
||||
}: {
|
||||
reloadBtn?: () => void;
|
||||
sendReportBtn?: () => void;
|
||||
message: string;
|
||||
}) {
|
||||
const debug = useDebugState();
|
||||
|
||||
return (
|
||||
|
@ -19,19 +35,23 @@ export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
|||
>
|
||||
<p className="text-ink-faint m-3 text-sm font-bold">APP CRASHED</p>
|
||||
<h1 className="text-ink text-2xl font-bold">We're past the event horizon...</h1>
|
||||
<pre className="text-ink m-2">Error: {error.message}</pre>
|
||||
<pre className="text-ink m-2">Error: {message}</pre>
|
||||
{debug.enabled && (
|
||||
<pre className="text-ink-dull m-2 text-sm">
|
||||
Check the console (CMD/CRTL + OPTION + i) for stack trace.
|
||||
</pre>
|
||||
)}
|
||||
<div className="text-ink flex flex-row space-x-2">
|
||||
<Button variant="accent" className="mt-2" onClick={resetErrorBoundary}>
|
||||
Reload
|
||||
</Button>
|
||||
<Button variant="gray" className="mt-2" onClick={onClick}>
|
||||
Send report
|
||||
</Button>
|
||||
{reloadBtn && (
|
||||
<Button variant="accent" className="mt-2" onClick={reloadBtn}>
|
||||
Reload
|
||||
</Button>
|
||||
)}
|
||||
{sendReportBtn && (
|
||||
<Button variant="gray" className="mt-2" onClick={sendReportBtn}>
|
||||
Send report
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { ArrowsClockwise, Clipboard, Eye, EyeSlash } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
import { lazy, useState } from 'react';
|
||||
import { Algorithm, useBridgeMutation } from '@sd/client';
|
||||
import { Button, Dialog, Select, SelectOption, UseDialogProps, useDialog } from '@sd/ui';
|
||||
import { forms } from '@sd/ui';
|
||||
import { getHashingAlgorithmSettings } from '~/screens/settings/library/KeysSetting';
|
||||
import { generatePassword } from '../key/KeyMounter';
|
||||
import { PasswordMeter } from '../key/PasswordMeter';
|
||||
|
||||
const PasswordMeter = lazy(() => import('../key/PasswordMeter'));
|
||||
|
||||
const { Input, z, useZodForm } = forms;
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { ArrowsClockwise, Clipboard, Eye, EyeSlash } from 'phosphor-react';
|
||||
import { useState } from 'react';
|
||||
import { lazy, useState } from 'react';
|
||||
import { Algorithm, useLibraryMutation } from '@sd/client';
|
||||
import { Button, Dialog, Input, Select, SelectOption, UseDialogProps, useDialog } from '@sd/ui';
|
||||
import { useZodForm, z } from '@sd/ui/src/forms';
|
||||
import { getHashingAlgorithmSettings } from '~/screens/settings/library/KeysSetting';
|
||||
import { showAlertDialog } from '~/util/dialog';
|
||||
import { generatePassword } from '../key/KeyMounter';
|
||||
import { PasswordMeter } from '../key/PasswordMeter';
|
||||
|
||||
const PasswordMeter = lazy(() => import('../key/PasswordMeter'));
|
||||
|
||||
export type MasterPasswordChangeDialogProps = UseDialogProps;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ const options = {
|
|||
};
|
||||
zxcvbnOptions.setOptions(options);
|
||||
|
||||
export const PasswordMeter = (props: { password: string }) => {
|
||||
export default function PasswordMeterInner(props: { password: string }) {
|
||||
const ratings = ['Poor', 'Weak', 'Good', 'Strong', 'Perfect'];
|
||||
|
||||
const zx = zxcvbn(props.password);
|
||||
|
@ -54,4 +54,4 @@ export const PasswordMeter = (props: { password: string }) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { cx } from '@sd/ui';
|
|||
|
||||
export default function DragRegion(props: PropsWithChildren & { className?: string }) {
|
||||
return (
|
||||
<div data-tauri-drag-region className={cx('flex flex-shrink-0 w-full h-5', props.className)}>
|
||||
<div data-tauri-drag-region className={cx('flex h-5 w-full flex-shrink-0', props.className)}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useState } from 'react';
|
||||
import { lazy, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { getOnboardingStore, useBridgeMutation, useOnboardingStore } from '@sd/client';
|
||||
import { Button, Card, forms } from '@sd/ui';
|
||||
import { PasswordMeter } from '../key/PasswordMeter';
|
||||
import { useUnlockOnboardingScreen } from './OnboardingProgress';
|
||||
import { OnboardingContainer, OnboardingDescription, OnboardingTitle } from './OnboardingRoot';
|
||||
|
||||
const PasswordMeter = lazy(() => import('../key/PasswordMeter'));
|
||||
|
||||
const { PasswordShowHideInput, z, useZodForm, Form } = forms;
|
||||
|
||||
const schema = z.object({
|
||||
|
|
|
@ -11,8 +11,8 @@ export const Shortcut: React.FC<ShortcutProps> = (props) => {
|
|||
return (
|
||||
<kbd
|
||||
className={clsx(
|
||||
`px-1 border border-b-2`,
|
||||
`rounded-md text-xs font-ink-dull font-bold`,
|
||||
`border border-b-2 px-1`,
|
||||
`font-ink-dull rounded-md text-xs font-bold`,
|
||||
`border-app-line dark:border-transparent`,
|
||||
className
|
||||
)}
|
||||
|
|
|
@ -6,7 +6,7 @@ export const SubtleButton: React.FC<{ icon?: React.FC }> = (props) => {
|
|||
return (
|
||||
<Button className="!p-[5px]" variant="subtle">
|
||||
{/* @ts-expect-error */}
|
||||
<Icon weight="bold" className="w-3 h-3" />
|
||||
<Icon weight="bold" className="h-3 w-3" />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,3 +4,5 @@ export { KeybindEvent } from './util/keybind';
|
|||
export * from './util/Platform';
|
||||
|
||||
export default SpacedriveInterface;
|
||||
|
||||
export { ErrorPage } from './ErrorFallback';
|
||||
|
|
|
@ -18,7 +18,7 @@ export default function DebugScreen() {
|
|||
const { mutate: identifyFiles } = useLibraryMutation('jobs.identifyUniqueFiles');
|
||||
return (
|
||||
<ScreenContainer>
|
||||
<div className="flex flex-col p-5 pt-2 space-y-5 pb-7">
|
||||
<div className="flex flex-col space-y-5 p-5 pt-2 pb-7">
|
||||
<h1 className="text-lg font-bold ">Developer Debugger</h1>
|
||||
{/* <div className="flex flex-row pb-4 space-x-2">
|
||||
<Button
|
||||
|
|
|
@ -40,7 +40,7 @@ function DropItem(props: DropItemProps) {
|
|||
}
|
||||
if (brandIconSrc) {
|
||||
icon = (
|
||||
<div className="flex items-center justify-center h-full p-3">
|
||||
<div className="flex h-full items-center justify-center p-3">
|
||||
<img className="rounded-full " src={brandIconSrc} alt={props.name} />
|
||||
</div>
|
||||
);
|
||||
|
@ -48,18 +48,18 @@ function DropItem(props: DropItemProps) {
|
|||
} else {
|
||||
//
|
||||
const Icon = props.icon || User;
|
||||
icon = <Icon className={clsx('w-8 h-8 m-3', !props.name && 'opacity-20')} />;
|
||||
icon = <Icon className={clsx('m-3 h-8 w-8', !props.name && 'opacity-20')} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(classes.honeycombItem, 'overflow-hidden bg-app-box/20 hover:bg-app-box/50')}
|
||||
className={clsx(classes.honeycombItem, 'bg-app-box/20 hover:bg-app-box/50 overflow-hidden')}
|
||||
>
|
||||
<div className="relative flex flex-col items-center justify-center w-full h-full group ">
|
||||
<div className="group relative flex h-full w-full flex-col items-center justify-center ">
|
||||
<SubtleButtonContainer className="absolute left-[12px] top-[55px]">
|
||||
<SubtleButton icon={Star} />
|
||||
</SubtleButtonContainer>
|
||||
<div className="rounded-full w-14 h-14 bg-app-button">{icon}</div>
|
||||
<div className="bg-app-button h-14 w-14 rounded-full">{icon}</div>
|
||||
<SubtleButtonContainer className="absolute right-[12px] top-[55px] rotate-90">
|
||||
<SubtleButton />
|
||||
</SubtleButtonContainer>
|
||||
|
@ -69,7 +69,7 @@ function DropItem(props: DropItemProps) {
|
|||
{props.connectionType && (
|
||||
<Pill
|
||||
className={clsx(
|
||||
'!text-white uppercase',
|
||||
'uppercase !text-white',
|
||||
props.connectionType === 'lan' && 'bg-green-500',
|
||||
props.connectionType === 'p2p' && 'bg-blue-500'
|
||||
)}
|
||||
|
@ -89,7 +89,7 @@ export default function SpacedropScreen() {
|
|||
return (
|
||||
<ScreenContainer
|
||||
dragRegionChildren={
|
||||
<div className="flex flex-row items-center justify-center w-full h-8 pt-3">
|
||||
<div className="flex h-8 w-full flex-row items-center justify-center pt-3">
|
||||
<SearchBar className="ml-[13px]" ref={searchRef} />
|
||||
{/* <Button variant="outline">Add</Button> */}
|
||||
</div>
|
||||
|
|
|
@ -14,13 +14,13 @@ export default function AboutSpacedrive() {
|
|||
return (
|
||||
<SettingsContainer>
|
||||
<div className="flex flex-row items-center">
|
||||
<img src={Logo} className="w-[88px] h-[88px] mr-8" />
|
||||
<img src={Logo} className="mr-8 h-[88px] w-[88px]" />
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-2xl font-bold">
|
||||
Spacedrive {os !== 'unknown' && <>for {currentPlatformNiceName}</>}
|
||||
</h1>
|
||||
<span className="mt-1 text-sm text-ink-dull">The file manager from the future.</span>
|
||||
<span className="mt-1 text-xs text-ink-faint/80">
|
||||
<span className="text-ink-dull mt-1 text-sm">The file manager from the future.</span>
|
||||
<span className="text-ink-faint/80 mt-1 text-xs">
|
||||
v{buildInfo.data?.version || '-.-.-'} - {buildInfo.data?.commit || 'dev'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue