From 0495bfd295ea35c47abd588728b8493231207902 Mon Sep 17 00:00:00 2001 From: CodeServer Date: Mon, 11 Apr 2022 10:36:56 +0100 Subject: [PATCH] implemented RepositoriesProvider --- src/App.tsx | 2 + src/ProtectedRoute.tsx | 3 +- src/components/Login.tsx | 27 ++-- src/components/fetchRepositories.tsx | 125 +++++++++++++++++ src/components/home/Home.tsx | 125 +---------------- src/components/home/RepositoryEntry.tsx | 8 +- src/components/navbar/NavBar.tsx | 127 +++++++++++++++++- .../repositoryInfo/RepositoryInfo.tsx | 15 +++ src/context/AuthProvider.tsx | 28 ++-- src/context/RepositoriesProvider.tsx | 21 +++ src/hooks/useAuth.tsx | 11 +- src/hooks/useRepositories.tsx | 8 ++ src/index.tsx | 5 +- 13 files changed, 343 insertions(+), 162 deletions(-) create mode 100644 src/components/fetchRepositories.tsx create mode 100644 src/components/repositoryInfo/RepositoryInfo.tsx create mode 100644 src/context/RepositoriesProvider.tsx create mode 100644 src/hooks/useRepositories.tsx diff --git a/src/App.tsx b/src/App.tsx index 78ef8e8..8230f8d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import CheckENVRoute from "./CheckENVRoute"; import Home from "./components/home/Home"; import Login from "./components/Login"; +import RepositoryInfo from "./components/repositoryInfo/RepositoryInfo"; import ProtectedRoute from "./ProtectedRoute"; const App: React.FC = () => { @@ -13,6 +14,7 @@ const App: React.FC = () => { } /> }> } /> + } /> diff --git a/src/ProtectedRoute.tsx b/src/ProtectedRoute.tsx index 1427265..29b6158 100644 --- a/src/ProtectedRoute.tsx +++ b/src/ProtectedRoute.tsx @@ -3,9 +3,10 @@ import NavBar from "./components/navbar/NavBar"; import useAuth from "./hooks/useAuth"; const ProtectedRoute = () => { - const auth = useAuth(); + const { auth } = useAuth(); const location = useLocation(); + return ((auth.loginNeeded !== undefined && !auth.loginNeeded) || auth?.isLoggedIn) ? ( <> diff --git a/src/components/Login.tsx b/src/components/Login.tsx index dde00a7..237b839 100644 --- a/src/components/Login.tsx +++ b/src/components/Login.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { InputLabel, Stack, Typography } from "@mui/material"; +import { InputLabel, Stack } from "@mui/material"; import Container from "@mui/material/Container"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; @@ -12,7 +12,7 @@ interface LocationState { from: { pathname: string }; } const Login: React.FC = () => { - const setAuth = useAuth(); + const { setAuth } = useAuth(); const navigate = useNavigate(); const location = useLocation(); @@ -24,8 +24,12 @@ const Login: React.FC = () => { const [hasError, setHasError] = useState(false); const handleSkipLogin = () => { - setAuth["loginNeeded"] = false; - setAuth["isLoggedIn"] = false; + setAuth({ + loginNeeded: false, + isLoggedIn: false, + username: "", + password: "" + }); navigate(from, { replace: true }); } @@ -55,12 +59,15 @@ const Login: React.FC = () => { if (status === undefined) { setHasError(true); } else { - setAuth["loginNeeded"] = true - setAuth["isLoggedIn"] = true; - // setAuth["username"] = username; - // setAuth["password"] = password; - setAuth["username"] = "pusher"; - setAuth["password"] = "pusher"; + setAuth({ + loginNeeded: true, + isLoggedIn: true, + // username: username, + // password: password, + username: "pusher", + password: "pusher" + }); + setHasError(false); navigate(from, { replace: true }); console.log("logged in!"); diff --git a/src/components/fetchRepositories.tsx b/src/components/fetchRepositories.tsx new file mode 100644 index 0000000..a9be195 --- /dev/null +++ b/src/components/fetchRepositories.tsx @@ -0,0 +1,125 @@ +import axios from "../api/axios"; +import useAuth from "../hooks/useAuth"; +import useRepositories from "../hooks/useRepositories"; +import { loginInfo } from "../context/AuthProvider"; +import Tag from "../interfaces/Tag"; +import Repository from "../interfaces/Repositoriy"; +import Layer from "../interfaces/Layer"; + +// const [repositories, setRepositories] = useState([]); + +const getTagInfo = async(newRepositories: Repository[], auth: loginInfo) => { + let promises: Promise[] = []; + newRepositories.forEach((repository: Repository) => { + repository.tags.forEach((tag: Tag) => { + let promise = axios.get( + "/" + repository.name + "/manifests/" + tag.label, + { + headers:{ + Accept: "application/vnd.docker.distribution.manifest.v2+json", + }, + auth: { + username: auth.username, + password: auth.password + } + } + ).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'], + { + headers:{ + Accept: "application/vnd.docker.distribution.manifest.v2+json", + }, + auth: { + username: auth.username, + password: auth.password + } + } + ).then((res) => { + tag.architecture = res?.data?.architecture; + tag.os = res?.data?.os; + tag.created = new Date(res?.data?.created); + }); + }); + promises.push(promise); + }); + }); + return Promise.all(promises); +}; + +const getTags = async(newRepositories: Repository[], auth: loginInfo) => { + let promises: Promise[] = []; + newRepositories.forEach((repository: Repository) => { + let promise = axios.get( + "/" + repository.name + "/tags/list", + { + auth: { + username: auth.username, + password: auth.password + } + } + ).then((res) => { + let tags: Tag[] = []; + res?.data?.tags.forEach((tagLabel: string) => { + let tag: Tag = { + label: tagLabel, + architecture: "", + os: "", + created: undefined, + schemaVersion: undefined, + mediaType: "", + config: undefined, + layers: undefined, + size: undefined, + digest: "" + } + tags.push(tag); + }); + repository.tags = tags; + }); + promises.push(promise); + }); + await Promise.all(promises); + return getTagInfo(newRepositories, auth); +}; + +const listRepositories = async() => { + const { auth } = useAuth(); + const { setRepositories } = useRepositories(); + + const response = await axios.get( + "/_catalog", + { + auth: { + username: auth.username, + password: auth.password + } + } + ); + + let newRepositories: Repository[] = []; + response?.data?.repositories.forEach((repositoriyName: string) => { + let newRepository: Repository = { + name: repositoriyName, + tags: [] + } + newRepositories.push(newRepository); + }); + await getTags(newRepositories, auth) + + setRepositories(newRepositories); + // setRepositories(newRepositories); + +}; + +export { listRepositories }; \ No newline at end of file diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 71c2081..f00deb8 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -1,132 +1,11 @@ import React from "react"; -import { useState, useEffect } from "react"; import { List } from "@mui/material"; import RepositoryItem from "./RepositoryEntry"; -import axios from "../../api/axios"; -import useAuth from "../../hooks/useAuth"; -import Tag from "../../interfaces/Tag"; +import useRepositories from "../../hooks/useRepositories"; 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) => { - repository.tags.forEach((tag: Tag) => { - let promise = axios.get( - "/" + repository.name + "/manifests/" + tag.label, - { - headers:{ - Accept: "application/vnd.docker.distribution.manifest.v2+json", - }, - auth: { - username: auth.username, - password: auth.password - } - } - ).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'], - { - headers:{ - Accept: "application/vnd.docker.distribution.manifest.v2+json", - }, - auth: { - username: auth.username, - password: auth.password - } - } - ).then((res) => { - tag.architecture = res?.data?.architecture; - tag.os = res?.data?.os; - tag.created = new Date(res?.data?.created); - }); - }); - promises.push(promise); - }); - }); - return Promise.all(promises); - }; - - const getTags = async(newRepositories: Repository[]) => { - let promises: Promise[] = []; - newRepositories.forEach((repository: Repository) => { - let promise = axios.get( - "/" + repository.name + "/tags/list", - { - auth: { - username: auth.username, - password: auth.password - } - } - ).then((res) => { - let tags: Tag[] = []; - res?.data?.tags.forEach((tagLabel: string) => { - let tag: Tag = { - label: tagLabel, - architecture: "", - os: "", - created: undefined, - schemaVersion: undefined, - mediaType: "", - config: undefined, - layers: undefined, - size: undefined, - digest: "" - } - tags.push(tag); - }); - repository.tags = tags; - }); - promises.push(promise); - }); - await Promise.all(promises); - return getTagInfo(newRepositories); - }; - - const listRepositories = async() => { - - const response = await axios.get( - "/_catalog", - { - auth: { - username: auth.username, - password: auth.password - } - } - ); - - let newRepositories: Repository[] = []; - response?.data?.repositories.forEach((repositoriyName: string) => { - let newRepository: Repository = { - name: repositoriyName, - tags: [] - } - newRepositories.push(newRepository); - }); - await getTags(newRepositories) - setRepositories(newRepositories); - - }; - - useEffect(() => { - setInterval(() => listRepositories(), 1000); - }, []); + const { repositories } = useRepositories(); return(
diff --git a/src/components/home/RepositoryEntry.tsx b/src/components/home/RepositoryEntry.tsx index 74c7448..44f717f 100644 --- a/src/components/home/RepositoryEntry.tsx +++ b/src/components/home/RepositoryEntry.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { ListItemButton, Box, Typography, Grid, ListItemText } from "@mui/material"; +import { ListItemButton, Grid, ListItemText } from "@mui/material"; import Tag from "../../interfaces/Tag"; import Repository from "../../interfaces/Repositoriy"; import { printSize, printTimePassed } from "../../utils"; @@ -18,12 +18,10 @@ const RepositoryItem: React.FC = (props: Props) => { */} - (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.created ? printTimePassed(tag.created) : "")).join(" ")} /> - (tag.label === "latest")).map((tag: Tag) => (tag.size ? printSize(tag.size) : "")).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 index f544f6f..0edf113 100644 --- a/src/components/navbar/NavBar.tsx +++ b/src/components/navbar/NavBar.tsx @@ -1,9 +1,17 @@ import React from "react"; +import { useEffect } from "react"; import { AppBar, Container, Toolbar, Typography, Box, IconButton, Menu, MenuItem , Tooltip, Avatar, Snackbar, Alert} from "@mui/material"; +import axios from "../../api/axios"; import useAuth from "../../hooks/useAuth"; +import useRepositories from "../../hooks/useRepositories"; +import { loginInfo } from "../../context/AuthProvider"; +import Tag from "../../interfaces/Tag"; +import Repository from "../../interfaces/Repositoriy"; +import Layer from "../../interfaces/Layer"; const NavBar: React.FC = () => { - const auth = useAuth(); + const { auth } = useAuth(); + const { setRepositories } = useRepositories(); const [anchorElUser, setAnchorElUser] = React.useState<(EventTarget & HTMLButtonElement) | null>(null); const [snackbarOpen, setSnackbarOpen] = React.useState(false); @@ -21,6 +29,123 @@ const NavBar: React.FC = () => { const handleClose = () => { setSnackbarOpen(false); }; + + + const getTagInfo = async(newRepositories: Repository[], auth: loginInfo) => { + let promises: Promise[] = []; + newRepositories.forEach((repository: Repository) => { + repository.tags.forEach((tag: Tag) => { + let promise = axios.get( + "/" + repository.name + "/manifests/" + tag.label, + { + headers:{ + Accept: "application/vnd.docker.distribution.manifest.v2+json", + }, + auth: { + username: auth.username, + password: auth.password + } + } + ).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'], + { + headers:{ + Accept: "application/vnd.docker.distribution.manifest.v2+json", + }, + auth: { + username: auth.username, + password: auth.password + } + } + ).then((res) => { + tag.architecture = res?.data?.architecture; + tag.os = res?.data?.os; + tag.created = new Date(res?.data?.created); + }); + }); + promises.push(promise); + }); + }); + return Promise.all(promises); + }; + + const getTags = async(newRepositories: Repository[], auth: loginInfo) => { + let promises: Promise[] = []; + newRepositories.forEach((repository: Repository) => { + let promise = axios.get( + "/" + repository.name + "/tags/list", + { + auth: { + username: auth.username, + password: auth.password + } + } + ).then((res) => { + let tags: Tag[] = []; + res?.data?.tags.forEach((tagLabel: string) => { + let tag: Tag = { + label: tagLabel, + architecture: "", + os: "", + created: undefined, + schemaVersion: undefined, + mediaType: "", + config: undefined, + layers: undefined, + size: undefined, + digest: "" + } + tags.push(tag); + }); + repository.tags = tags; + }); + promises.push(promise); + }); + await Promise.all(promises); + return getTagInfo(newRepositories, auth); + }; + + const listRepositories = async() => { + + const response = await axios.get( + "/_catalog", + { + auth: { + username: auth.username, + password: auth.password + } + } + ); + + let newRepositories: Repository[] = []; + response?.data?.repositories.forEach((repositoriyName: string) => { + let newRepository: Repository = { + name: repositoriyName, + tags: [] + } + newRepositories.push(newRepository); + }); + await getTags(newRepositories, auth) + + setRepositories(newRepositories); + // setRepositories(newRepositories); + + }; + + useEffect(() => { + setInterval(() => listRepositories(), 1000); + }, []); return ( diff --git a/src/components/repositoryInfo/RepositoryInfo.tsx b/src/components/repositoryInfo/RepositoryInfo.tsx new file mode 100644 index 0000000..a31cdd8 --- /dev/null +++ b/src/components/repositoryInfo/RepositoryInfo.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { useLocation } from "react-router-dom"; +import { ListItemButton, Box, Typography, Grid, ListItemText } from "@mui/material"; +import Tag from "../../interfaces/Tag"; +import Repository from "../../interfaces/Repositoriy"; + +const RepositoryInfo: React.FC = () => { + const location = useLocation() + + return ( +

{location.pathname.slice(12,location.pathname.length)}

+ ); +} + +export default RepositoryInfo; \ No newline at end of file diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx index 2c99a2c..dc9c55c 100644 --- a/src/context/AuthProvider.tsx +++ b/src/context/AuthProvider.tsx @@ -1,26 +1,32 @@ import { createContext, useState } from "react"; -import Repository from "../interfaces/Repositoriy"; -interface loginInfo { +export interface loginInfo { loginNeeded: boolean; isLoggedIn: boolean; username: string; password: string; - repositories: Repository[]; } -const AuthContext = createContext({ - loginNeeded: true, - isLoggedIn: false, - username: "", - password: "", - repositories: [] +interface MyAuthState { + auth: loginInfo; + setAuth: React.Dispatch> +} + + +const AuthContext = createContext({ + auth: { + loginNeeded: true, + isLoggedIn: false, + username: "", + password: "" + }, + setAuth: () => {} }); export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - const [auth] = useState({ loginNeeded: true, isLoggedIn: false, username: "", password: "", repositories: [] }); + const [auth, setAuth] = useState({ loginNeeded: true, isLoggedIn: false, username: "", password: "" }); - return {children}; + return {children}; }; export default AuthContext; diff --git a/src/context/RepositoriesProvider.tsx b/src/context/RepositoriesProvider.tsx new file mode 100644 index 0000000..48743c2 --- /dev/null +++ b/src/context/RepositoriesProvider.tsx @@ -0,0 +1,21 @@ +import { createContext, useState } from "react"; +import Repository from "../interfaces/Repositoriy"; + + +interface MyRepositoriesState { + repositories: Repository[]; + setRepositories: React.Dispatch> +} + +const RepositoriesContext = createContext({ + repositories: [], + setRepositories: () => {} +}); + +export const RepositoriesProvider = ({ children }: { children: React.ReactNode }) => { + const [repositories, setRepositories] = useState([]); + + return {children}; +}; + +export default RepositoriesContext; \ No newline at end of file diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index 32e8aee..e1d0b6d 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -1,17 +1,8 @@ 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 = () => { - return useContext(AuthContext); + return useContext(AuthContext); }; export default useAuth; \ No newline at end of file diff --git a/src/hooks/useRepositories.tsx b/src/hooks/useRepositories.tsx new file mode 100644 index 0000000..5030ccf --- /dev/null +++ b/src/hooks/useRepositories.tsx @@ -0,0 +1,8 @@ +import { useContext } from "react"; +import RepositoriesContext from "../context/RepositoriesProvider"; + +const useRepositories = () => { + return useContext(RepositoriesContext); +}; + +export default useRepositories; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index e838861..504e2b1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,12 +2,15 @@ import ReactDOM from "react-dom"; import App from "./App"; import "./style/style.css"; import { AuthProvider } from "./context/AuthProvider"; +import { RepositoriesProvider } from "./context/RepositoriesProvider"; const Index: React.FC = () => { return ( - + + + ); };