diff --git a/src/ProtectedRoute.tsx b/src/ProtectedRoute.tsx
index 79ea547..1427265 100644
--- a/src/ProtectedRoute.tsx
+++ b/src/ProtectedRoute.tsx
@@ -1,4 +1,5 @@
import { useLocation, Outlet, Navigate } from "react-router-dom";
+import NavBar from "./components/navbar/NavBar";
import useAuth from "./hooks/useAuth";
const ProtectedRoute = () => {
@@ -7,6 +8,7 @@ const ProtectedRoute = () => {
return ((auth.loginNeeded !== undefined && !auth.loginNeeded) || auth?.isLoggedIn) ? (
<>
+
>
) : (
diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx
index fee1aca..71c2081 100644
--- a/src/components/home/Home.tsx
+++ b/src/components/home/Home.tsx
@@ -6,12 +6,14 @@ import axios from "../../api/axios";
import useAuth from "../../hooks/useAuth";
import Tag from "../../interfaces/Tag";
import Repository from "../../interfaces/Repositoriy";
+import Layer from "../../interfaces/Layer";
const Home: React.FC = () => {
const auth = useAuth();
const [repositories, setRepositories] = useState([]);
+ auth['repositories'] = repositories;
const getTagInfo = async(newRepositories: Repository[]) => {
let promises: Promise[] = [];
newRepositories.forEach((repository: Repository) => {
@@ -28,7 +30,16 @@ const Home: React.FC = () => {
}
}
).then((response) => {
+ tag.schemaVersion = response?.data['schemaVersion'];
+ tag.mediaType = response?.data['mediaType'];
tag.digest = response?.headers['docker-content-digest'];
+ tag.config = response?.data?.config;
+ tag.layers = response?.data?.layers;
+ let size = 0;
+ response?.data?.layers.forEach((layer: Layer) => {
+ size += layer.size;
+ });
+ tag.size = size;
return axios.get(
"/" + repository.name + "/blobs/" + response?.data?.config['digest'],
{
@@ -71,6 +82,11 @@ const Home: React.FC = () => {
architecture: "",
os: "",
created: undefined,
+ schemaVersion: undefined,
+ mediaType: "",
+ config: undefined,
+ layers: undefined,
+ size: undefined,
digest: ""
}
tags.push(tag);
diff --git a/src/components/home/RepositoryEntry.tsx b/src/components/home/RepositoryEntry.tsx
index 13e9bc5..74c7448 100644
--- a/src/components/home/RepositoryEntry.tsx
+++ b/src/components/home/RepositoryEntry.tsx
@@ -1,8 +1,8 @@
import React from "react";
-import { ListItemButton, Box, Typography, tableFooterClasses } from "@mui/material";
+import { ListItemButton, Box, Typography, Grid, ListItemText } from "@mui/material";
import Tag from "../../interfaces/Tag";
import Repository from "../../interfaces/Repositoriy";
-import { printTimePassed } from "../../utils";
+import { printSize, printTimePassed } from "../../utils";
interface Props {
repository: Repository;
@@ -14,10 +14,18 @@ const RepositoryItem: React.FC = (props: Props) => {
-
- {props.repository.name}
- {"Last updated: " + props.repository.tags.filter((tag: Tag) => (tag.label === "latest")).map((tag: Tag) => (tag.created ? printTimePassed(tag.created) : "")).join(" ")}
-
+ {/*
+ */}
+
+
+ (tag.label === "latest")).map((tag: Tag) => (tag.created ? printTimePassed(tag.created) : "")).join(" ")} />
+ {/* {props.repository.name}
+ {"Last updated: " + props.repository.tags.filter((tag: Tag) => (tag.label === "latest")).map((tag: Tag) => (tag.created ? printTimePassed(tag.created) : "")).join(" ")} */}
+
+
+ (tag.label === "latest")).map((tag: Tag) => (tag.size ? printSize(tag.size) : "")).join(" ")} />
+
+
);
}
diff --git a/src/components/navbar/NavBar.tsx b/src/components/navbar/NavBar.tsx
new file mode 100644
index 0000000..f544f6f
--- /dev/null
+++ b/src/components/navbar/NavBar.tsx
@@ -0,0 +1,81 @@
+import React from "react";
+import { AppBar, Container, Toolbar, Typography, Box, IconButton, Menu, MenuItem , Tooltip, Avatar, Snackbar, Alert} from "@mui/material";
+import useAuth from "../../hooks/useAuth";
+
+const NavBar: React.FC = () => {
+ const auth = useAuth();
+ const [anchorElUser, setAnchorElUser] = React.useState<(EventTarget & HTMLButtonElement) | null>(null);
+ const [snackbarOpen, setSnackbarOpen] = React.useState(false);
+
+ const handleCloseUserMenu = () => {
+ setAnchorElUser(null);
+ };
+
+ const handleClick = () => {
+ const url = process.env.REGISTRY_URL ? process.env.REGISTRY_URL : "";
+ const urlSplit = url.split('/');
+ navigator.clipboard.writeText(urlSplit[urlSplit.length - 1]);
+ setSnackbarOpen(true);
+ };
+
+ const handleClose = () => {
+ setSnackbarOpen(false);
+ };
+
+ return (
+
+
+
+
+ Docker Registry
+
+
+
+ Your registry instance is at {process.env.REGISTRY_URL ? process.env.REGISTRY_URL : ""}
+
+
+
+
+ Registry address copied!
+
+
+
+
+
+ {setAnchorElUser(event.currentTarget)}} sx={{ p: 0 }}>
+
+
+
+
+
+
+
+
+ );
+};
+
+export default NavBar;
\ No newline at end of file
diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx
index 48a149e..2c99a2c 100644
--- a/src/context/AuthProvider.tsx
+++ b/src/context/AuthProvider.tsx
@@ -1,21 +1,24 @@
import { createContext, useState } from "react";
+import Repository from "../interfaces/Repositoriy";
interface loginInfo {
loginNeeded: boolean;
isLoggedIn: boolean;
username: string;
password: string;
+ repositories: Repository[];
}
const AuthContext = createContext({
loginNeeded: true,
isLoggedIn: false,
username: "",
- password: ""
+ password: "",
+ repositories: []
});
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
- const [auth] = useState({ loginNeeded: true, isLoggedIn: false, username: "", password: "" });
+ const [auth] = useState({ loginNeeded: true, isLoggedIn: false, username: "", password: "", repositories: [] });
return {children};
};
diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx
index 2602ce8..32e8aee 100644
--- a/src/hooks/useAuth.tsx
+++ b/src/hooks/useAuth.tsx
@@ -1,11 +1,13 @@
import { useContext } from "react";
import AuthContext from "../context/AuthProvider";
+import Repository from "../interfaces/Repositoriy";
interface loginInfo {
loginNeeded: boolean;
isLoggedIn: boolean;
username: string;
password: string;
+ repositories: Repository[];
}
const useAuth = () => {
diff --git a/src/interfaces/Layer.tsx b/src/interfaces/Layer.tsx
new file mode 100644
index 0000000..e0b36cf
--- /dev/null
+++ b/src/interfaces/Layer.tsx
@@ -0,0 +1,7 @@
+interface Layer {
+ mediaType: string;
+ size: number;
+ digest: string;
+};
+
+export default Layer;
\ No newline at end of file
diff --git a/src/interfaces/Tag.tsx b/src/interfaces/Tag.tsx
index cfe6c29..6ced8cd 100644
--- a/src/interfaces/Tag.tsx
+++ b/src/interfaces/Tag.tsx
@@ -1,8 +1,21 @@
+import Layer from "./Layer"
+
+interface Config {
+ mediaType: string;
+ size: number;
+ digest: string;
+};
+
interface Tag {
label: string;
architecture: string;
os: string;
created: Date | undefined;
+ schemaVersion: number | undefined;
+ mediaType: string;
+ config: Config | undefined;
+ layers: Layer[] | undefined;
+ size: number | undefined;
digest: string;
}
diff --git a/src/style/layouts/_header.sass b/src/style/layouts/_header.sass
index e69de29..1dcd5e2 100644
--- a/src/style/layouts/_header.sass
+++ b/src/style/layouts/_header.sass
@@ -0,0 +1,5 @@
+.navbar
+ margin: 0
+ .centerText
+ text-align: center
+ width: 90%
\ No newline at end of file
diff --git a/src/style/style.css b/src/style/style.css
index 4bc1fd9..be2d42e 100644
--- a/src/style/style.css
+++ b/src/style/style.css
@@ -6,6 +6,15 @@ a {
text-decoration: none;
}
+.navbar {
+ margin: 0;
+}
+
+.navbar .centerText {
+ text-align: center;
+ width: 90%;
+}
+
.sectionHome .repositoryList {
max-width: 70%;
margin: auto;
diff --git a/src/style/style.css.map b/src/style/style.css.map
index a8443ca..8d4b21a 100644
--- a/src/style/style.css.map
+++ b/src/style/style.css.map
@@ -1,6 +1,6 @@
{
"version": 3,
- "mappings": "AG8CA,AAAA,WAAW,CAAC;EACR,KAAK,EAAE,IAAI;CAAG;;AAElB,AAAA,CAAC,CAAC;EACE,eAAe,EAAE,IAAI;CAAG;;AKlD5B,AACI,YADQ,CACR,eAAe,CAAC;EACZ,SAAS,EAAE,GAAG;EACd,MAAM,EAAE,IAAI;CAAG;;ACHvB,AACI,MADE,CACF,eAAe,CAAC;EACZ,WAAW,EAAE,MAAM;EACnB,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,GAAG;EACf,SAAS,EAAE,cAAc;CAeS;;AApB1C,AAMQ,MANF,CACF,eAAe,GAKT,CAAC,CAAC;EACA,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,MAAM;CAAG;;AAR7B,AASQ,MATF,CACF,eAAe,CAQX,YAAY,CAAC;EACT,OAAO,EAAE,YAAY;EACrB,UAAU,EAAE,MAAM;EAClB,MAAM,EAAE,IAAI;EACZ,SAAS,EAAE,IAAI;CAAG;;AAb9B,AAcQ,MAdF,CACF,eAAe,CAaX,UAAU,CAAC;EACP,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,GAAG;CACpB;;AAjBF,AAkBQ,MAlBF,CACF,eAAe,CAiBX,eAAe,CAAC;EACZ,MAAM,EAAE,IAAI;EACZ,gBAAgB,EAAE,OAAO;CAAG;;ACpBxC,AAAA,QAAQ,CAAC;EACL,SAAS,EAAE,GAAG;EACd,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,GAAG;CAEU;;AAL7B,AAII,QAJI,CAIJ,eAAe,CAAC;EACZ,UAAU,EAAE,IAAI;CAAG",
+ "mappings": "AG8CA,AAAA,WAAW,CAAC;EACR,KAAK,EAAE,IAAI;CAAG;;AAElB,AAAA,CAAC,CAAC;EACE,eAAe,EAAE,IAAI;CAAG;;AElD5B,AAAA,OAAO,CAAC;EACJ,MAAM,EAAE,CAAC;CAGU;;AAJvB,AAEI,OAFG,CAEH,WAAW,CAAC;EACR,UAAU,EAAE,MAAM;EAClB,KAAK,EAAE,GAAG;CAAG;;AGJrB,AACI,YADQ,CACR,eAAe,CAAC;EACZ,SAAS,EAAE,GAAG;EACd,MAAM,EAAE,IAAI;CAAG;;ACHvB,AACI,MADE,CACF,eAAe,CAAC;EACZ,WAAW,EAAE,MAAM;EACnB,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,GAAG;EACf,SAAS,EAAE,cAAc;CAeS;;AApB1C,AAMQ,MANF,CACF,eAAe,GAKT,CAAC,CAAC;EACA,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,MAAM;CAAG;;AAR7B,AASQ,MATF,CACF,eAAe,CAQX,YAAY,CAAC;EACT,OAAO,EAAE,YAAY;EACrB,UAAU,EAAE,MAAM;EAClB,MAAM,EAAE,IAAI;EACZ,SAAS,EAAE,IAAI;CAAG;;AAb9B,AAcQ,MAdF,CACF,eAAe,CAaX,UAAU,CAAC;EACP,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,GAAG;CACpB;;AAjBF,AAkBQ,MAlBF,CACF,eAAe,CAiBX,eAAe,CAAC;EACZ,MAAM,EAAE,IAAI;EACZ,gBAAgB,EAAE,OAAO;CAAG;;ACpBxC,AAAA,QAAQ,CAAC;EACL,SAAS,EAAE,GAAG;EACd,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,GAAG;CAEU;;AAL7B,AAII,QAJI,CAIJ,eAAe,CAAC;EACZ,UAAU,EAAE,IAAI;CAAG",
"sources": [
"style.sass",
"_variables.sass",
diff --git a/src/utils.tsx b/src/utils.tsx
index f7a14cf..1c661c6 100644
--- a/src/utils.tsx
+++ b/src/utils.tsx
@@ -15,6 +15,10 @@ const checkValidURL = async (url: string) => {
return true;
}
+const printTwoDecimalPlaces = (num: number): string => {
+ return (Math.round(num * 100) / 100).toFixed(2);
+}
+
const printTimePassed = (date: Date): string => {
const currentTime = Date.now();
const secondsPassed = (currentTime - date.getTime()) / 1000;
@@ -37,4 +41,12 @@ const printTimePassed = (date: Date): string => {
}
};
-export { checkValidURL , printTimePassed };
\ No newline at end of file
+const printSize = (sizeInByte: number): string => {
+ if (sizeInByte < 1073741824) {
+ return printTwoDecimalPlaces(sizeInByte / 1048576) + " MiB";
+ } else {
+ return printTwoDecimalPlaces(sizeInByte / 1073741824) + " GiB";
+ }
+};
+
+export { checkValidURL, printTwoDecimalPlaces, printTimePassed, printSize };
\ No newline at end of file