Skip to content

Commit

Permalink
Merge pull request #18 from CodeDead/feature/native-password-generator
Browse files Browse the repository at this point in the history
Feature/native password generator
  • Loading branch information
CodeDead committed Oct 23, 2022
2 parents 44d0a56 + 6031257 commit 5808831
Show file tree
Hide file tree
Showing 16 changed files with 553 additions and 292 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"devDependencies": {
"@tauri-apps/cli": "^1.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.25.0",
"eslint": "^8.26.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.1.1", features = ["api-all"] }
open = "3.0.3"
rand = "0.8.5"
unicode-segmentation = "1.10.0"

[features]
# by default Tauri runs in production mode
Expand Down
82 changes: 65 additions & 17 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,81 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]

use rand::Rng;
use std::fs;
use unicode_segmentation::UnicodeSegmentation;

fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![open_website, save_string_to_disk])
.run(tauri::generate_context!())
.expect("error while running tauri application");
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
open_website,
save_string_to_disk,
generate_passwords
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

#[tauri::command]
fn open_website(website: &str) -> Result<String, String> {
match open::that(website) {
Ok(_) => {
Ok(String::from("Success"))
match open::that(website) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
Err(e) => {
Err(e.to_string())
}
}
}

#[tauri::command]
fn save_string_to_disk(content: &str, path: &str) -> Result<String, String> {
match fs::write(path, content) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
match fs::write(path, content) {
Ok(_) => Ok(String::from("Success")),
Err(e) => Err(e.to_string()),
}
}

#[tauri::command]
async fn generate_passwords(
min_length: u64,
max_length: u64,
character_set: &str,
amount: u64,
allow_duplicates: bool,
) -> Result<Vec<String>, String> {
let mut password_list: Vec<String> = Vec::new();
let mut max_count: f64 = 0.0;
let char_count = character_set.graphemes(true).count();

if !allow_duplicates {
let mut current = min_length;
while current <= max_length {
max_count += (char_count as f64).powf(current as f64);
current += 1;
}
}

let mut rng = rand::thread_rng();
let chars = character_set.chars();
for _n in 0..amount {
let mut can_continue = false;
while !can_continue {
let mut password = String::from("");
let length = rng.gen_range(min_length..(max_length + 1));
for _j in 0..length {
let index = rng.gen_range(0..char_count);
password.push(chars.clone().nth(index).unwrap());
}

if allow_duplicates || (!allow_duplicates && !password_list.contains(&password)) {
password_list.push(password);
can_continue = true;
}

if !can_continue && !allow_duplicates && password_list.len() as f64 == max_count {
return Ok(password_list);
}
}
}

Ok(password_list)
}
29 changes: 16 additions & 13 deletions src/components/App/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import ThemeSelector from '../../utils/ThemeSelector';
import TopBar from '../TopBar';
import ClippedDrawer from '../ClippedDrawer';
import UpdateDialog from '../UpdateDialog';
import { openWebSite, setError, setUpdate } from '../../reducers/MainReducer/Actions';
import {
openWebSite, setError, setLoading, setUpdate,
} from '../../reducers/MainReducer/Actions';
import Updater from '../../utils/Updater';
import AlertDialog from '../AlertDialog';
import packageJson from '../../../package.json';
Expand All @@ -33,10 +35,9 @@ const NotFound = lazy(() => import('../../routes/NotFound'));
const App = () => {
const [state, d1] = useContext(MainContext);
const {
themeIndex, themeType, update, languageIndex, autoUpdate, error,
themeIndex, themeType, update, languageIndex, autoUpdate, error, loading,
} = state;

const [loading, setLoading] = useState(false);
const [snackOpen, setSnackOpen] = useState(false);
const language = state.languages[languageIndex];

Expand Down Expand Up @@ -82,7 +83,7 @@ const App = () => {
d1(setError(e));
})
.finally(() => {
setLoading(false);
d1(setLoading(false));
});
};

Expand Down Expand Up @@ -121,15 +122,17 @@ const App = () => {
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
<Toolbar />
<Suspense fallback={<LoadingBar />}>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/advanced" element={<Advanced />} />
<Route exact path="/generate" element={<Generate />} />
<Route exact path="/advisor" element={<Advisor />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
{loading ? <LoadingBar /> : (
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/advanced" element={<Advanced />} />
<Route exact path="/generate" element={<Generate />} />
<Route exact path="/advisor" element={<Advisor />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
)}
</Suspense>
</Box>
</Box>
Expand Down
1 change: 1 addition & 0 deletions src/contexts/MainContextProvider/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const initState = {
update: null,
error: null,
languageSelector,
loading: false,
};

export const MainContext = createContext(initState);
Expand Down
1 change: 1 addition & 0 deletions src/reducers/MainReducer/Actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export const SET_AUTO_UPDATE = 'SET_AUTO_UPDATE';
export const SET_UPDATE = 'SET_UPDATE';
export const SET_ERROR = 'SET_ERROR';
export const SET_LANGUAGE_SELECTOR = 'SET_LANGUAGE_SELECTOR';
export const SET_LOADING = 'SET_LOADING';
6 changes: 6 additions & 0 deletions src/reducers/MainReducer/Actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SET_ERROR,
SET_LANGUAGE_INDEX,
SET_LANGUAGE_SELECTOR,
SET_LOADING,
SET_PAGE_INDEX,
SET_THEME_INDEX,
SET_THEME_TYPE,
Expand Down Expand Up @@ -67,3 +68,8 @@ export const setLanguageSelector = (value) => ({
type: SET_LANGUAGE_SELECTOR,
payload: value,
});

export const setLoading = (value) => ({
type: SET_LOADING,
payload: value,
});
6 changes: 6 additions & 0 deletions src/reducers/MainReducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SET_ERROR,
SET_LANGUAGE_INDEX,
SET_LANGUAGE_SELECTOR,
SET_LOADING,
SET_PAGE_INDEX,
SET_THEME_INDEX,
SET_THEME_TYPE,
Expand Down Expand Up @@ -65,6 +66,11 @@ const MainReducer = (state, action) => {
...state,
languageSelector: action.payload,
};
case SET_LOADING:
return {
...state,
loading: action.payload,
};
case SET_ERROR:
return {
...state,
Expand Down
16 changes: 16 additions & 0 deletions src/reducers/PasswordReducer/Actions/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { invoke } from '@tauri-apps/api';
import {
SET_ALLOW_DUPLICATES,
SET_BRACKETS,
Expand Down Expand Up @@ -78,3 +79,18 @@ export const setAllowDuplicates = (value) => ({
type: SET_ALLOW_DUPLICATES,
payload: value,
});

// eslint-disable-next-line max-len
export const generatePasswordArray = (min, max, characterSet, amount, allowDuplicates, worker) => {
// eslint-disable-next-line no-underscore-dangle
if (window.__TAURI__) {
return invoke('generate_passwords', {
minLength: parseFloat(min),
maxLength: parseFloat(max),
characterSet,
amount: parseFloat(amount),
allowDuplicates,
});
}
return worker.PasswordGenerator(min, max, characterSet, amount, allowDuplicates);
};
3 changes: 3 additions & 0 deletions src/reducers/PasswordReducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
} from './Actions/actionTypes';

const PasswordReducer = (state, action) => {
if (!action || !action.type) {
return state;
}
switch (action.type) {
case SET_PASSWORD_LENGTH_MIN:
if (parseFloat(action.payload) < 1) return state;
Expand Down
13 changes: 9 additions & 4 deletions src/routes/Advanced/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import FormGroup from '@mui/material/FormGroup';
import { createWorkerFactory, useWorker } from '@shopify/react-web-worker';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import { MainContext } from '../../contexts/MainContextProvider';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import {
generatePasswordArray,
setAllowDuplicates,
setCharacterSet, setPasswords,
setUseAdvanced,
Expand Down Expand Up @@ -67,13 +68,17 @@ const Advanced = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
navigate('/generate');
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
14 changes: 9 additions & 5 deletions src/routes/Generate/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { save } from '@tauri-apps/api/dialog';
import { invoke } from '@tauri-apps/api/tauri';
import { MainContext } from '../../contexts/MainContextProvider';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import PasswordStrength from '../../utils/PasswordStrength';
import { setPasswords } from '../../reducers/PasswordReducer/Actions';
import { generatePasswordArray, setPasswords } from '../../reducers/PasswordReducer/Actions';
import MuiVirtualizedTable from '../../components/MuiVirtualizedTable';
import ExportDialog from '../../components/ExportDialog';

Expand Down Expand Up @@ -104,12 +104,16 @@ const Generate = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
13 changes: 9 additions & 4 deletions src/routes/Home/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import TextField from '@mui/material/TextField';
import { useNavigate } from 'react-router-dom';
import { createWorkerFactory, useWorker } from '@shopify/react-web-worker';
import { MainContext } from '../../contexts/MainContextProvider';
import { setError, setPageIndex } from '../../reducers/MainReducer/Actions';
import { setError, setLoading, setPageIndex } from '../../reducers/MainReducer/Actions';
import { PasswordContext } from '../../contexts/PasswordContextProvider';
import {
generatePasswordArray,
setBrackets,
setCapitalLetters,
setNumbers,
Expand Down Expand Up @@ -83,13 +84,17 @@ const Home = () => {
return;
}

worker.PasswordGenerator(min, max, simpleCharacterSet, amount, allowDuplicates)
d1(setLoading(true));
generatePasswordArray(min, max, simpleCharacterSet, amount, allowDuplicates, worker)
.then((res) => {
d2(setPasswords(res));
navigate('/generate');
})
.catch((e) => {
d1(setError(e));
.catch((err) => {
d1(setError(err));
})
.finally(() => {
d1(setLoading(false));
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils/PasswordGenerator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function PasswordGenerator(minLength, maxLength, characterSet, amount, al
// We've reached the end of the line
if (canContinue === false && allowDuplicates === false
&& passwordArray.length === maxCount) {
break;
return passwordArray;
}
}
}
Expand Down
Loading

0 comments on commit 5808831

Please sign in to comment.