Skip to content

Commit

Permalink
Merge pull request #207 from pacholoamit/fix/disk-analysis-ms-defende…
Browse files Browse the repository at this point in the history
…r-exclusion

fix/disk analysis ms defender exclusion
  • Loading branch information
pacholoamit authored Jun 16, 2024
2 parents 663d7d4 + ded3caa commit 44b61a1
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 86 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/).

## 0.9.10-1
- Added option for Windows users to enable microsoft defender exclusions for better performance
- Set midnight to original theme
- Fix issue where slate shows gradient background

## 0.9.9
- Fix issue where some popover components are transparent
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@
"vite-plugin-eslint": "^1.8.1"
},
"packageManager": "[email protected]"
}
}
3 changes: 2 additions & 1 deletion src-tauri/capabilities/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"os:allow-hostname",
"os:allow-os-type",
"os:allow-platform",
"window:allow-start-dragging"
"window:allow-start-dragging",
"autostart:allow-is-enabled"
]
}
2 changes: 1 addition & 1 deletion src-tauri/gen/schemas/capabilities.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"main":{"identifier":"main","description":"permissions for pachtop","local":true,"windows":["main"],"permissions":["store:allow-get","store:allow-set","store:allow-save","os:allow-arch","os:allow-hostname","os:allow-os-type","os:allow-platform","window:allow-start-dragging"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default"]}}
{"main":{"identifier":"main","description":"permissions for pachtop","local":true,"windows":["main"],"permissions":["store:allow-get","store:allow-set","store:allow-save","os:allow-arch","os:allow-hostname","os:allow-os-type","os:allow-platform","window:allow-start-dragging","autostart:allow-is-enabled"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default"]}}
51 changes: 50 additions & 1 deletion src-tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,55 @@ pub fn delete_folder(path: String) {
}
}

#[tauri::command]
pub fn add_pachtop_exclusion() -> Result<(), String> {
#[cfg(target_os = "windows")]
{
// Check if the script is running as administrator
let output = std::process::Command::new("powershell")
.arg("-Command")
.arg("([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)")
.output()
.map_err(|e| format!("Failed to check admin privileges: {}", e))?;

if output.status.success() && String::from_utf8_lossy(&output.stdout).trim() == "True" {
// If running as administrator, run the PowerShell command
let status = std::process::Command::new("powershell")
.arg("-Command")
.arg("Add-MpPreference -ExclusionProcess \"pachtop.exe\"")
.status()
.map_err(|e| format!("Failed to add exclusion process: {}", e))?;

if !status.success() {
return Err("Failed to add exclusion process: Command did not succeed".into());
}
} else {
// If not running as administrator, run the command with elevated privileges without creating a command prompt window
let status = std::process::Command::new("powershell")
.arg("-Command")
.arg("Start-Process powershell -ArgumentList '-Command \"Add-MpPreference -ExclusionProcess \\\"pachtop.exe\\\"\"' -Verb RunAs -WindowStyle Hidden")
.status()
.map_err(|e| format!("Failed to run as administrator: {}", e))?;

if !status.success() {
return Err("Failed to run as administrator: Command did not succeed".into());
}
}

Ok(())
}

#[cfg(target_os = "macos")]
{
Ok(())
}

#[cfg(target_os = "linux")]
{
Ok(())
}
}

#[tauri::command]
// Slow version
pub async fn disk_scan(
Expand Down Expand Up @@ -280,7 +329,7 @@ pub async fn disk_turbo_scan(
}

#[tauri::command]
// Multithreaded fast version, uses high cpu/memory
// Get data from hashmap
pub async fn disk_analysis_flattened(
state: tauri::State<'_, AppState>,
path: String,
Expand Down
38 changes: 7 additions & 31 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ mod app;
mod dirstat;
mod metrics;
mod models;
mod tray;
mod utils;

use app::AppState;

use std::time::Duration;
// use tauri::api::path::cache_dir;
use tauri::{menu::MenuItemBuilder, Manager};
use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
// use tauri_plugin_log::LogTarget;

Expand Down Expand Up @@ -92,43 +93,17 @@ fn build_and_run_app(app: AppState) {

// BUILD TRAY - TODO MOVE TO DIFFERENT FILE

let quit = MenuItemBuilder::with_id("quit", "Quit").build(app)?;

let icon = app.default_window_icon().unwrap().clone();

let menu = tauri::menu::MenuBuilder::new(app).items(&[&quit]).build()?;

let _tray = tauri::tray::TrayIconBuilder::with_id("tray")
.tooltip("Pachtop")
.icon(icon)
.menu(&menu)
.on_menu_event(move |app, event| match event.id().as_ref() {
"quit" => {
app.exit(0);
}
_ => {}
})
.on_tray_icon_event(|tray, event| match event {
tauri::tray::TrayIconEvent::Click { button, .. } => match button {
tauri::tray::MouseButton::Left => {
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
_ => {}
},
_ => {}
})
.build(app)?;
let _ = tray::create_tray(app);

Ok(())
})
.on_window_event(|window, event| match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
window.hide().unwrap();
api.prevent_close();
}
tauri::WindowEvent::DragDrop { .. } => todo!(),

_ => {}
})
.manage(app)
Expand All @@ -138,6 +113,7 @@ fn build_and_run_app(app: AppState) {
app::delete_folder,
app::disk_turbo_scan,
app::disk_analysis_flattened,
app::add_pachtop_exclusion,
app::disk_scan,
])
.run(tauri::generate_context!())
Expand Down
39 changes: 39 additions & 0 deletions src-tauri/src/tray.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use anyhow::Ok;
use tauri::{menu::MenuItemBuilder, App, Manager};

pub fn create_tray(app: &mut App) -> anyhow::Result<()> {
let quit = MenuItemBuilder::with_id("quit", "Quit").build(app)?;

let icon = app.default_window_icon().unwrap().clone();

let menu = tauri::menu::MenuBuilder::new(app).items(&[&quit]).build()?;

let _tray = tauri::tray::TrayIconBuilder::with_id("tray")
.tooltip("Pachtop")
.icon(icon)
.menu(&menu)
.on_menu_event(move |app, event| match event.id().as_ref() {
"quit" => {
app.exit(0);
}
"example" => todo!(),
_ => {}
})
.on_tray_icon_event(|tray, event| match event {
tauri::tray::TrayIconEvent::Click { button, .. } => match button {
tauri::tray::MouseButton::Left => {
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
tauri::tray::MouseButton::Middle => todo!(),
_ => {}
},
tauri::tray::TrayIconEvent::Leave { .. } => todo!(),
_ => {}
})
.build(app)?;
Ok(())
}
2 changes: 1 addition & 1 deletion src-tauri/src/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn hex_color_to_colorref(color: HexColor) -> COLORREF {
// TODO: Remove this unsafe, This operation doesn't need to be unsafe!
unsafe { COLORREF(transmute::<[u8; 4], u32>([color.r, color.g, color.b, 0])) }
}

#[allow(dead_code)]
struct WinThemeAttribute {
flag: u32,
mask: u32,
Expand Down
6 changes: 2 additions & 4 deletions src-tauri/ws-systemicons/src/windows/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ pub fn get_icon(ext: &str, size: i32) -> Result<Vec<u8>, Error> {
+ mask_bytes_count;

let image_bytes_count = bitmap_bytes_count + mask_bytes_count;
let mut bytes = Vec::<u8>::with_capacity(complete_size);
bytes.set_len(complete_size);
let mut bytes: Vec<u8> = vec![0; complete_size];

let iconheader = ICONHEADER {
id_count: 1, // number of ICONDIRs
Expand Down Expand Up @@ -259,8 +258,7 @@ fn write_icon_data_to_memory(
bitmap_byte_count: usize,
) {
unsafe {
let mut icon_data = Vec::<u8>::with_capacity(bitmap_byte_count);
icon_data.set_len(bitmap_byte_count);
let mut icon_data: Vec<u8> = vec![0; bitmap_byte_count];

GetBitmapBits(
h_bitmap,
Expand Down
54 changes: 54 additions & 0 deletions src/components/exclusion-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useState } from "react";

import { commands } from "@/lib";
import store from "@/lib/store";
import notification from "@/utils/notification";
import { Button, Code, Modal, Space, Stack, Text, Title } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";

const ExclusionModal = () => {
const [opened, { open, close }] = useDisclosure(true);
const [isLoading, setIsLoading] = useState(false);

const handleAddExclusion = async () => {
setIsLoading(true);
try {
await commands.add_pachtop_exclusion();

notification.success({
title: "Pachtop added to exclusion list",
message: "Pachtop has been added to the exclusion list in Microsoft Defender.",
});
await store.then((s) => s.isDefenderExclusionEnabled.set(true));
} catch (err) {
notification.error({
title: "Failed to add Pachtop to exclusion list",
message: "Please try again or add Pachtop to the exclusion list manually by re-opening the app",
});

return close();
} finally {
setIsLoading(false);
close();
}
};

return (
<Modal opened={opened} onClose={close} withCloseButton={false} centered>
<Stack>
<Title order={3}>Add Pachtop to Exclusion list!</Title>
<Text size={"sm"}>This is a setup screen specifically for windows users, So you should feel special.</Text>
<Text size={"sm"}>
In order for Pachtop to be blazingly fast & performant, we need to add Pachtop to the exclusion list in
Microsoft Defender.
</Text>
<Space />
<Button variant="white" onClick={handleAddExclusion} loading={isLoading}>
Make Pachtop blazingly fast 🚀
</Button>
</Stack>
</Modal>
);
};

export default ExclusionModal;
17 changes: 17 additions & 0 deletions src/hooks/useIsFirstRun.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useState } from "react";

import useEffectAsync from "@/hooks/useEffectAsync";
import store from "@/lib/store";

const useIsFirstRun = () => {
const [isFirstRun, setIsFirstRun] = useState(false);

useEffectAsync(async () => {
const firstRun = await store.then((s) => s.isFirstRun.get());
setIsFirstRun(firstRun);
}, []);

return isFirstRun;
};

export default useIsFirstRun;
5 changes: 3 additions & 2 deletions src/lib/commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { invoke } from "@/lib/helpers";
import { Command, DiskItem, KillProcessOpts, KillProcessResult, ScanOpts } from "@/lib/types";
import { invoke } from '@/lib/helpers';
import { Command, DiskItem, KillProcessOpts, KillProcessResult, ScanOpts } from '@/lib/types';

// TODO: Fix types
export const commands = {
Expand All @@ -8,4 +8,5 @@ export const commands = {
turboScan: (path: ScanOpts): Promise<DiskItem> => invoke(Command.DiskTurboScan, path as any),
scan: (path: ScanOpts): Promise<DiskItem> => invoke(Command.DiskScan, path as any),
disk_analysis_flattened: (path: ScanOpts): Promise<DiskItem[]> => invoke(Command.DiskAnalysisFlattened, path as any),
add_pachtop_exclusion: (): Promise<void> => invoke(Command.AddPachtopExclusion, {}),
};
44 changes: 39 additions & 5 deletions src/lib/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Store } from "tauri-plugin-store";

import { appDataDir } from "@tauri-apps/api/path";
import { platform } from "@tauri-apps/plugin-os";

// Currently not being used, implement on PostHog maybe?
const userId = (store: Store) => {
return {
Expand Down Expand Up @@ -33,25 +36,56 @@ const theme = (store: Store) => {

const isFirstRun = (store: Store) => {
return {
get: async () => await store.get<boolean>("isFirstRun"),
get: async () => {
const count = (await store.get<number>("sessions")) || 0;
return count === 0;
},
};
};

const sessions = (store: Store) => {
return {
get: async () => await store.get<number>("sessions"),
increment: async () => {
const currentSessions = (await store.get<number>("sessions")) ?? 0;
await store.set("sessions", currentSessions + 1);
},
};
};

const isDefenderExclusionEnabled = (store: Store) => {
return {
get: async () => await store.get<boolean>("isDefenderExclusionEnabled"),
set: async (value: boolean) => {
await store.set("isFirstRun", value);
await store.set("isDefenderExclusionEnabled", value);
await store.save();
},
};
};

const createStore = (path: string) => {
const store = new Store(path);
const createStore = async (name: string) => {
const currentPlatform = await platform();
const path = await appDataDir();

const storePath = currentPlatform === "windows" ? `${path}\\${name}` : `${path}/${name}`;

const store = new Store(storePath);
console.log("Store path: ", storePath);

return {
userId: userId(store),
windowColor: windowColor(store),
theme: theme(store),
isFirstRun: isFirstRun(store),
sessions: sessions(store),
isDefenderExclusionEnabled: isDefenderExclusionEnabled(store),
};
};

const store = createStore(".pachtop.dat");
const initializeStore = async () => {
return await createStore(".pachtop.dat");
};

const store = initializeStore();

export default store;
Loading

0 comments on commit 44b61a1

Please sign in to comment.