Skip to content

Commit

Permalink
Explore end-to-end encryption for data (v2)
Browse files Browse the repository at this point in the history
This enables end-to-end encryption via Etebase. Everything works, but still lacks billing control.

Currently deployed to https://v2.budgetzen.net
  • Loading branch information
BrunoBernardino committed Jan 2, 2022
1 parent 397d625 commit 780d919
Show file tree
Hide file tree
Showing 46 changed files with 22,024 additions and 17,053 deletions.
11 changes: 10 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
]
}
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NODE_PATH=/
AWS_ACCESS_KEY_ID=accesskey
AWS_SECRET_ACCESS_KEY=sshhh
NEXT_PUBLIC_ETEBASE_SERVER_URL=https://api.etebase.com/example
4 changes: 3 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const eslint = {
'airbnb',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'prettier',
],
rules: {
semi: 2,
Expand Down Expand Up @@ -69,6 +69,8 @@ const eslint = {
'function-paren-newline': 'off',
'no-confusing-arrow': 'off',
'react/jsx-curly-newline': 'off',
'react/function-component-definition': 'off',
'react/jsx-no-useless-fragment': 'off',
},
parserOptions: {
ecmaFeatures: {
Expand Down
6 changes: 5 additions & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
github: [BrunoBernardino]
custom: ["https://paypal.me/brunobernardino", "https://gist.github.com/BrunoBernardino/ff5b54c13dd96ac7f9fee6fbfd825b09"]
custom:
[
'https://paypal.me/brunobernardino',
'https://gist.github.com/BrunoBernardino/ff5b54c13dd96ac7f9fee6fbfd825b09',
]
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ node_modules
**/public/workbox-*.js.map
**/public/worker-*.js.map
**/public/fallback-*.js
*.tsbuildinfo
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact=true
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v12.16.1
v16.13.1
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: install
install:
-cp -n .env.sample .env
npm install
npm install --legacy-peer-deps

.PHONY: start
start:
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Budget Zen - Web App
# Budget Zen v2 - Web App

[![](https://github.com/BrunoBernardino/budgetzen-web/workflows/Run%20Tests/badge.svg)](https://github.com/BrunoBernardino/budgetzen-web/actions?workflow=Run+Tests)

This is the web app for the [Budget Zen app](https://budgetzen.net), built with Next.js and deployed to AWS with Vercel.
This is the web app for the [Budget Zen app](https://budgetzen.net), built with Next.js and deployed to AWS with Serverless.

It runs completely in the browser, using `localStorage` and `IndexedDB`.
It's v2, meaning it is [end-to-end encrypted via etebase](https://etebase.com), and requires an email + [token](https://budgetzen.net/get-sync-token) to work.

It's not thoroughly tested just yet, so it's available but not announced.
It also means it's not compatible with Budget Zen v1, which you can still get locally from [this commit](https://github.com/BrunoBernardino/budgetzen-web/tree/397d625469b7dfd8d1968c847b32e607ee7c8ee9).

## Development

Expand All @@ -15,12 +15,13 @@ make install # installs dependencies
make start # starts the app
make pretty # prettifies the code
make test # runs linting and tests
make deploy # deploys to app.budgetzen.net (requires `serverless` to be installed globally)
make deploy # deploys to v2.budgetzen.net (requires `serverless` to be installed globally)
```

## TODOs

- [ ] Allow using app without a Sync Token
- [ ] Implement billing in signup
- [ ] Implement fetching in chunks + incrementally, via stoken?
- [ ] Implement encrypted session?
- [ ] Improve UI/UX in general
- [ ] Improve dark/light mode
- [ ] Improve mobile view (collapse panels and show tab bar to navigate between them?)
35 changes: 19 additions & 16 deletions components/BudgetModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import Rodal from 'rodal';
import Swal from 'sweetalert2';
import { RxDatabase } from 'rxdb';
import * as Etebase from 'etebase';

import Button from 'components/Button';
import { showNotification } from 'lib/utils';
Expand All @@ -18,7 +18,7 @@ interface BudgetModalProps {
month: string;
value: number;
reloadData: () => Promise<void>;
db: RxDatabase;
etebase: Etebase.Account;
}

const Container = styled.section`
Expand Down Expand Up @@ -59,17 +59,13 @@ const Input = styled.input`
}
`;

const StyledButton = styled(Button)`
margin: 20px 0;
`;

const BudgetModal = (props: BudgetModalProps) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = useState(props.name);
const [month, setMonth] = useState(`${props.month}-01`);
const [value, setValue] = useState(props.value.toString());

const { id, isOpen, reloadData, db } = props;
const { id, isOpen, reloadData, etebase } = props;

const onClose = useCallback(() => {
const { onClose: closeModal } = props;
Expand All @@ -91,10 +87,10 @@ const BudgetModal = (props: BudgetModalProps) => {
id: id || 'newBudget',
value: Number.parseFloat(value.replace(',', '.')),
name,
month: month ? month.substr(0, 7) : '',
month: month ? month.substring(0, 7) : '',
};

const success = await saveBudget(db, parsedBudget);
const success = await saveBudget(etebase, parsedBudget);

setIsSubmitting(false);

Expand All @@ -115,8 +111,7 @@ const BudgetModal = (props: BudgetModalProps) => {
const confirmationResult = await Swal.fire({
icon: 'warning',
title: 'Are you sure?',
text:
'Are you sure you want to delete this budget?\n\nThis action is irreversible.',
text: 'Are you sure you want to delete this budget?\n\nThis action is irreversible.',
showDenyButton: true,
showCancelButton: true,
confirmButtonText: 'Yes!',
Expand All @@ -129,7 +124,7 @@ const BudgetModal = (props: BudgetModalProps) => {

setIsSubmitting(true);

const success = await deleteBudget(db, id);
const success = await deleteBudget(etebase, id);

setIsSubmitting(false);

Expand Down Expand Up @@ -185,14 +180,22 @@ const BudgetModal = (props: BudgetModalProps) => {
onKeyDown={onKeyDown}
/>

<StyledButton onClick={() => addBudget()} type="primary">
<Button
onClick={() => addBudget()}
type="primary"
style={{ margin: '20px 0' }}
>
{id ? 'Save Budget' : 'Add Budget'}
</StyledButton>
</Button>

{Boolean(id) && (
<StyledButton onClick={() => removeBudget()} type="delete">
<Button
onClick={() => removeBudget()}
type="delete"
style={{ margin: '20px 0' }}
>
Delete Budget
</StyledButton>
</Button>
)}
</Container>
</Rodal>
Expand Down
2 changes: 1 addition & 1 deletion components/Button/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`Button renders the button as expected 1`] = `
<button
className="Button__StyledButton-sc-1hxbnbg-0 Button Button--primary "
className="Button__StyledButton-sc-1hxbnbg-0 undefined Button Button--primary "
disabled={false}
type="button"
>
Expand Down
25 changes: 10 additions & 15 deletions components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { forwardRef } from 'react';
import styled from 'styled-components';

import './style.scss';
import styles from './styles.module.scss';

interface ButtonProps {
element?: 'button' | 'a';
Expand All @@ -11,24 +11,19 @@ interface ButtonProps {
onClick?: any;
className?: string;
width?: '' | 'large' | 'tiny';
style?: any;
}

const Button: React.FC<ButtonProps> = forwardRef(
(props: ButtonProps, ref: any) => {
const {
element,
className,
type,
isDisabled,
width,
...remainingProps
} = props;
const { element, className, type, isDisabled, width, ...remainingProps } =
props;

if (element === 'button') {
const StyledButton = styled.button.attrs({
className: `${className || ''} Button Button--${type} ${
width ? `Button--${width}` : ''
}`,
className: `${styles[className] || ''} ${styles.Button} ${
styles[`Button--${type}`]
} ${width ? styles[`Button--${width}`] : ''}`,
})``;
return (
<StyledButton
Expand All @@ -41,9 +36,9 @@ const Button: React.FC<ButtonProps> = forwardRef(
}

const StyledAnchor = styled.a.attrs({
className: `${className || ''} Button Button--${type} ${
width ? `Button--${width}` : ''
}`,
className: `${styles[className] || ''} ${styles.Button} ${
styles[`Button--${type}`]
} ${width ? styles[`Button--${width}`] : ''}`,
})``;

return <StyledAnchor ref={ref} {...remainingProps} />;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '__variables';
@import 'styles/__variables';

$transition-speed: 140ms;

Expand Down
33 changes: 18 additions & 15 deletions components/ExpenseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import Rodal from 'rodal';
import Swal from 'sweetalert2';
import { RxDatabase } from 'rxdb';
import * as Etebase from 'etebase';

import Button from 'components/Button';
import { showNotification } from 'lib/utils';
Expand All @@ -20,7 +20,7 @@ interface ExpenseModalProps {
date: string;
budgets: T.Budget[];
reloadData: () => Promise<void>;
db: RxDatabase;
etebase: Etebase.Account;
}

const Container = styled.section`
Expand Down Expand Up @@ -82,18 +82,14 @@ const Select = styled.select`
}
`;

const StyledButton = styled(Button)`
margin: 20px 0;
`;

const ExpenseModal = (props: ExpenseModalProps) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [description, setDescription] = useState(props.description);
const [cost, setCost] = useState(props.cost.toString());
const [budget, setBudget] = useState(props.budget);
const [date, setDate] = useState(props.date);

const { id, isOpen, budgets, reloadData, db } = props;
const { id, isOpen, budgets, reloadData, etebase } = props;

const onClose = useCallback(() => {
const { onClose: closeModal } = props;
Expand All @@ -120,7 +116,7 @@ const ExpenseModal = (props: ExpenseModalProps) => {
date,
};

const success = await saveExpense(db, parsedExpense);
const success = await saveExpense(etebase, parsedExpense);

setIsSubmitting(false);

Expand All @@ -141,8 +137,7 @@ const ExpenseModal = (props: ExpenseModalProps) => {
const confirmationResult = await Swal.fire({
icon: 'warning',
title: 'Are you sure?',
text:
'Are you sure you want to delete this expense?\n\nThis action is irreversible.',
text: 'Are you sure you want to delete this expense?\n\nThis action is irreversible.',
showDenyButton: true,
showCancelButton: true,
confirmButtonText: 'Yes!',
Expand All @@ -155,7 +150,7 @@ const ExpenseModal = (props: ExpenseModalProps) => {

setIsSubmitting(true);

const success = await deleteExpense(db, id);
const success = await deleteExpense(etebase, id);

setIsSubmitting(false);

Expand Down Expand Up @@ -234,14 +229,22 @@ const ExpenseModal = (props: ExpenseModalProps) => {
onKeyDown={onKeyDown}
/>

<StyledButton onClick={() => addExpense()} type="primary">
<Button
onClick={() => addExpense()}
type="primary"
style={{ margin: '20px 0' }}
>
{id ? 'Save Expense' : 'Add Expense'}
</StyledButton>
</Button>

{Boolean(id) && (
<StyledButton onClick={() => removeExpense()} type="delete">
<Button
onClick={() => removeExpense()}
type="delete"
style={{ margin: '20px 0' }}
>
Delete Expense
</StyledButton>
</Button>
)}
</Container>
</Rodal>
Expand Down
9 changes: 2 additions & 7 deletions components/FilterBudgetModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,8 @@ const BudgetName = styled.span`
`;

const FilterBudgetModal = (props: FilterBudgetModalProps) => {
const {
isOpen,
onClose,
budgets,
onFilterBudgetToggle,
filterBudgets,
} = props;
const { isOpen, onClose, budgets, onFilterBudgetToggle, filterBudgets } =
props;
return (
<Rodal visible={isOpen} onClose={onClose} animation="slideDown">
<Container>
Expand Down
Loading

0 comments on commit 780d919

Please sign in to comment.