Skip to content

Commit

Permalink
feat: list flies (#3)
Browse files Browse the repository at this point in the history
* refactor: remove temp directory use and use a random hash

* refactor: revert directory naming to the merkle root, it just makes sense

* feat: implement merkle proof generation

* fix: merkle tree algorithm to correctly create trees when odd leafs are found

* feat: add page to list files and fetch files and generate the proof for each file
  • Loading branch information
kieranroneill authored Feb 25, 2024
1 parent b68ec35 commit 0946c33
Show file tree
Hide file tree
Showing 46 changed files with 824 additions and 312 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
</p>

<h1 align="center">
Aether
aether
</h1>

<p align="center">
Journey into the Aether where storage defies conventional bounds.
Journey into the aether where storage defies conventional bounds.
</p>

<p align="center">
Aether is a modern file storage platform that implements a state-of-the-art Merkle tree structure to store multiple files.
aether is a modern file storage platform that implements a state-of-the-art Merkle tree structure to store multiple files.
</p>

<p align="center">
Expand Down Expand Up @@ -86,7 +86,7 @@ make setup
make
```

> ⚠️ **NOTE:** The `make` command will run/re-run `make install`, but will not overwrite any `.env.*` that may have been edited in section [1.2.](#22-setting-up-environment-variables-optional)
> ⚠️ **NOTE:** The `make` command will run/re-run `make setup`, but will not overwrite any `.env.*` that may have been edited in section [1.2.](#22-setting-up-environment-variables-optional)
2. Navigate to [http://localhost:8080](http://localhost:8080) to access the web portal.

Expand All @@ -105,8 +105,8 @@ make
| `make dev-web` | Runs the web app using `next dev`. Intended for development purposes only. |
| `make clean` | Deletes the build directory. |
| `make install` | Installs the yarn and golang dependencies. |
| `make run` | Checks if the apps are correctly configured and runs Docker Compose. |
| `make setup` | Creates the `.env.*` files to the `.config/` directory. |
| `make run` | Checks if the apps are correctly configured and runs Docker Compose. Intended for development purposes only. |
| `make setup` | Creates `.env.*` files in the `.config/` directory. |

<sup>[Back to top ^][table-of-contents]</sup>

Expand Down
1 change: 1 addition & 0 deletions app/@types/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ declare namespace NodeJS {
readonly PORT: string;

// public
readonly NEXT_PUBLIC_CORE_URL: string;
readonly NEXT_PUBLIC_DESCRIPTION: string;
readonly NEXT_PUBLIC_LOG_LEVEL: string;
readonly NEXT_PUBLIC_TAGLINE: string;
Expand Down
4 changes: 2 additions & 2 deletions app/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const Header: FC<IProps> = ({ onNavigationClick }) => {
w="full"
>
{/*open navigation menu button*/}
<Tooltip label={`Open navigation menu`}>
<Tooltip label={`Open Navigation Menu`}>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Open navigation drawer"
Expand All @@ -56,7 +56,7 @@ const Header: FC<IProps> = ({ onNavigationClick }) => {
>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Change color mode"
aria-label="Change Color Mode"
color={defaultTextColor}
icon={colorMode === 'dark' ? <IoSunnyOutline /> : <IoMoonOutline />}
onClick={handlerColorChangeClick}
Expand Down
11 changes: 9 additions & 2 deletions app/components/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
IoCheckmarkCircleOutline,
IoChevronBackOutline,
IoCloudUploadOutline,
IoListOutline,
} from 'react-icons/io5';

// components
Expand All @@ -27,6 +28,7 @@ import NavigationLinkItem from './NavigationLinkItem';
// constants
import {
DEFAULT_GAP,
FILES_ROUTE,
INDEX_ROUTE,
UPLOAD_ROUTE,
VERIFY_ROUTE,
Expand All @@ -53,6 +55,11 @@ const Navigation: FC<IProps> = ({ isOpen, onClose }) => {
label: 'Upload',
route: UPLOAD_ROUTE,
},
{
icon: IoListOutline,
label: 'Files',
route: FILES_ROUTE,
},
{
icon: IoCheckmarkCircleOutline,
label: 'Verify',
Expand Down Expand Up @@ -82,7 +89,7 @@ const Navigation: FC<IProps> = ({ isOpen, onClose }) => {
{/*icon*/}
<IconButton
_hover={{ bg: 'transparent' }}
aria-label="Go to hom epage"
aria-label="Go To Home"
color={primaryColor}
icon={<AetherIcon h={12} w={12} />}
onClick={handleHomeClick}
Expand All @@ -93,7 +100,7 @@ const Navigation: FC<IProps> = ({ isOpen, onClose }) => {
<Spacer />

{/*close navigation menu button*/}
<Tooltip label={`Close navigation menu`}>
<Tooltip label={`Close Menu`}>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Close navigation drawer"
Expand Down
18 changes: 8 additions & 10 deletions app/components/UploadCompleteModal/UploadCompleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import type { IProps } from './types';
// utils
import downloadJSONFile from '@app/utils/downloadJSONFile';

const UploadCompleteModal: FC<IProps> = ({ merkleTreeRootHash, onClose }) => {
const UploadCompleteModal: FC<IProps> = ({ onClose, uploadResponse }) => {
// hooks
const defaultTextColor: string = useDefaultTextColor();
const logger: ILogger = useLogger();
Expand All @@ -44,24 +44,22 @@ const UploadCompleteModal: FC<IProps> = ({ merkleTreeRootHash, onClose }) => {
const handleDownloadMerkleRoot = () => {
const _functionName: string = 'handleDownloadMerkleRoot';

if (!merkleTreeRootHash) {
if (!uploadResponse) {
logger.debug(
`${UploadCompleteModal.displayName}#${_functionName}: no merkle tree root hash found, ignoring`
);

return;
}

// create a data uri from the json and download ir
downloadJSONFile('root', {
root: merkleTreeRootHash,
});
// create a data uri from the json and download it - use the merkle root as the file name
downloadJSONFile(uploadResponse.root, uploadResponse);
};

return (
<Modal
closeOnOverlayClick={false}
isOpen={!!merkleTreeRootHash}
isOpen={!!uploadResponse}
onClose={onClose}
>
<ModalOverlay />
Expand All @@ -82,19 +80,19 @@ const UploadCompleteModal: FC<IProps> = ({ merkleTreeRootHash, onClose }) => {

{/*merkle tree root hash*/}
<HStack alignItems="center" spacing={1} w="full">
{!merkleTreeRootHash ? (
{!uploadResponse ? (
<Skeleton height="20px" w="full" />
) : (
<>
{/*merkle tree root hash*/}
<Code borderRadius="md" flexGrow={1} wordBreak="break-word">
{merkleTreeRootHash}
{uploadResponse.root}
</Code>

{/*copy button*/}
<CopyIconButton
ariaLabel={`Copy hash`}
value={merkleTreeRootHash}
value={uploadResponse.root}
/>
</>
)}
Expand Down
5 changes: 4 additions & 1 deletion app/components/UploadCompleteModal/types/IProps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// types
import type { IUploadResponse } from '@app/types';

interface IProps {
merkleTreeRootHash: string | null;
onClose: () => void;
uploadResponse: IUploadResponse | null;
}

export default IProps;
3 changes: 3 additions & 0 deletions app/constants/Paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const FILES_PATH: string = 'files';
export const UPLOAD_PATH: string = 'upload';
export const VERSIONS_PATH: string = 'versions';
1 change: 1 addition & 0 deletions app/constants/Routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const FILES_ROUTE: string = '/files';
export const INDEX_ROUTE: string = '/';
export const UPLOAD_ROUTE: string = '/upload';
export const VERIFY_ROUTE: string = '/verify';
1 change: 1 addition & 0 deletions app/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './Dimensions';
export * from './Links';
export * from './Paths';
export * from './Routes';
export * from './Styles';
6 changes: 6 additions & 0 deletions app/enums/LeafPositionEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum LeafPositionEnum {
Left = 0,
Right = 1,
}

export default LeafPositionEnum;
1 change: 1 addition & 0 deletions app/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as LeafPositionEnum } from './LeafPositionEnum';
179 changes: 179 additions & 0 deletions app/files/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
'use client';
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Code,
Heading,
HStack,
IconButton,
Skeleton,
Spacer,
Stack,
Text,
Tooltip,
VStack,
} from '@chakra-ui/react';
import { NextPage } from 'next';
import React from 'react';
import { IoDownloadOutline } from 'react-icons/io5';

// components
import CopyIconButton from '@app/components/CopyIconButton';

// constants
import { DEFAULT_GAP } from '@app/constants';

// hooks
import useButtonHoverBackgroundColor from '@app/hooks/useButtonHoverBackgroundColor';
import useDefaultTextColor from '@app/hooks/useDefaultTextColor';
import useFiles from '@app/hooks/useFiles';
import useSubTextColor from '@app/hooks/useSubTextColor';

// types
import type { IFileResponse } from '@app/types';

// utils
import downloadJSONFile from '@app/utils/downloadJSONFile';

const FilesPage: NextPage = () => {
// hooks
const buttonHoverBackgroundColor: string = useButtonHoverBackgroundColor();
const defaultTextColor: string = useDefaultTextColor();
const subTextColor: string = useSubTextColor();
const { files, loading } = useFiles();
// handlers
const handleDownloadProofClick = (file: IFileResponse) => () =>
downloadJSONFile(file.hash, file.proof);
// renders
const renderContent = () => {
let fileKeys: string[];

if (loading) {
return Array.from({ length: 3 }, (_, index) => (
<Skeleton h="20px" key={`files-page-skeleton-item-${index}`} w="full" />
));
}

if (files) {
fileKeys = Object.keys(files);

if (fileKeys.length > 0) {
return (
<Accordion allowMultiple={true} allowToggle={true} w="full">
{fileKeys.map((key, fileKeyIndex) => (
<AccordionItem key={`files-page-${key}-${fileKeyIndex}`}>
{/*accordian button*/}
<AccordionButton p={DEFAULT_GAP / 2}>
<Code
color={defaultTextColor}
fontSize="md"
maxW={650}
noOfLines={1}
textAlign="left"
>
{key}
</Code>

<Spacer />

<AccordionIcon />
</AccordionButton>

{/*list of files*/}
<AccordionPanel p={0}>
{files[key].map((file, index) => (
<HStack
alignItems="center"
justifyContent="space-between"
key={`files-page-${key}-item-${index}`}
p={DEFAULT_GAP / 2}
spacing={DEFAULT_GAP / 3}
w="full"
>
{/*name*/}
<Text
color={subTextColor}
fontSize="sm"
maxW={500}
noOfLines={1}
textAlign="left"
>
{file.name}
</Text>

<Spacer />

{/*copy hash button*/}
<CopyIconButton
ariaLabel={`Copy Hash`}
size="md"
value={file.hash}
/>

{/*download proof button*/}
<Tooltip label={`Download Proof`}>
<IconButton
_hover={{ bg: buttonHoverBackgroundColor }}
aria-label="Download file proof"
color={defaultTextColor}
icon={<IoDownloadOutline />}
onClick={handleDownloadProofClick(file)}
size="md"
variant="ghost"
/>
</Tooltip>
</HStack>
))}
</AccordionPanel>
</AccordionItem>
))}
</Accordion>
);
}
}

// when there are no files returned
return (
<Stack alignItems="center" flexGrow={1} justify="center" w="full">
<Text color={defaultTextColor} textAlign="center">
{`No files found!`}
</Text>
</Stack>
);
};

return (
<VStack
alignItems="center"
justifyContent="flex-start"
flexGrow={1}
spacing={DEFAULT_GAP}
w="full"
>
{/*heading*/}
<Heading color={defaultTextColor} size="lg" textAlign="center" w="full">
{`Files`}
</Heading>

{/*description*/}
<Text color={defaultTextColor} size="md" textAlign="center" w="full">
{`Below is a list of files, grouped by their merkle tree roots. You can download a file's proof and use the root you received to verify the file's integrity.`}
</Text>

<VStack
alignItems="center"
flexGrow={1}
justify="flex-start"
spacing={DEFAULT_GAP - 2}
w="full"
>
{renderContent()}
</VStack>
</VStack>
);
};

export default FilesPage;
2 changes: 2 additions & 0 deletions app/hooks/useFiles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './useFiles';
export * from './types';
Loading

0 comments on commit 0946c33

Please sign in to comment.