finished login

This commit is contained in:
CodeServer 2022-04-07 23:49:27 +01:00
parent 867b4b19ef
commit 53556b40ef
18 changed files with 261 additions and 29 deletions

1
env/local.env vendored
View File

@ -1 +1,2 @@
ENV=local ENV=local
REGISTRY_URL=https://registry.docker.cofan.cloud

View File

@ -1,17 +1,20 @@
import React from "react"; import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import CheckENVRoute from "./CheckENVRoute";
import Home from "./components/Home"; import Home from "./components/Home";
import Login from "./components/Login";
import ProtectedRoute from "./ProtectedRoute"; import ProtectedRoute from "./ProtectedRoute";
const App: React.FC = () => { const App: React.FC = () => {
return ( return (
<Router> <Router>
<Routes> <Routes>
{/* <Route path="/login" element={<Login />} /> <Route element={<CheckENVRoute />}>
<Route element={<ProtectedRoute />}> <Route path="/login" element={<Login />} />
<Route path="/" element={<Home />} /> <Route element={<ProtectedRoute />}>
</Route> */} <Route path="/" element={<Home />} />
<Route path="/" element={<Home />} /> </Route>
</Route>
</Routes> </Routes>
</Router> </Router>
); );

16
src/CheckENVRoute.tsx Normal file
View File

@ -0,0 +1,16 @@
import { Outlet } from "react-router-dom";
import ErrorENV from "./components/ErrorENV"
import React from "react";
const CheckENVRoute: React.FC = () => {
return process.env.REGISTRY_URL ? (
<>
<Outlet />
</>
) : (
<ErrorENV />
);
};
export default CheckENVRoute;

View File

@ -1,19 +1,13 @@
import { useLocation, Outlet, Navigate } from "react-router-dom"; import { useLocation, Outlet, Navigate } from "react-router-dom";
import useAuth from "./hooks/useAuth"; import useAuth from "./hooks/useAuth";
import { Box } from "@mui/material";
import React from "react";
const ProtectedRoute = () => { const ProtectedRoute = () => {
const auth = useAuth(); const auth = useAuth();
const location = useLocation(); const location = useLocation();
return auth?.isLoggedIn ? ( return ((auth.loginNeeded !== undefined && !auth.loginNeeded) || auth?.isLoggedIn) ? (
<> <>
<Box id="drawer-container" sx={{ display: "flex", height: "100%" }}> <Outlet />
<Box sx={{ flexGrow: 1 }}>
<Outlet />
</Box>
</Box>
</> </>
) : ( ) : (
<Navigate to="/login" state={{ from: location }} replace /> <Navigate to="/login" state={{ from: location }} replace />

View File

@ -1,7 +1,11 @@
import axios from "axios"; import axios from "axios";
export default (url: string) => { const axiosAlt = (url: string) => {
return axios.create({ return axios.create({
baseURL: url + "/v2" baseURL: url + "/v2"
}) })
}; };
export default axiosAlt(process.env.REGISTRY_URL ? process.env.REGISTRY_URL : "");
export { axiosAlt };

View File

@ -0,0 +1,9 @@
import React from "react";
const Home: React.FC = () => {
return(
<h1>Environment variable REGISTRY_URL not set!</h1>
);
};
export default Home;

View File

@ -1,9 +1,12 @@
import React from "react"; import React from "react";
import RegistryURL from "./RegistryURL"; import axios from "../api/axios";
const Home: React.FC = () => { const Home: React.FC = () => {
return( return(
<RegistryURL /> <h1>Home</h1>
); );
}; };

125
src/components/Login.tsx Normal file
View File

@ -0,0 +1,125 @@
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { InputLabel, Stack, Typography } from "@mui/material";
import Container from "@mui/material/Container";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import LoginIcon from "@mui/icons-material/Login";
import useAuth from "../hooks/useAuth";
import axios from "../api/axios";
interface LocationState {
from: { pathname: string };
}
const Login: React.FC = () => {
const setAuth = useAuth();
const navigate = useNavigate();
const location = useLocation();
const state = location.state as LocationState;
const from = state?.from?.pathname || "/";
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [hasError, setHasError] = useState(false);
const handleSkipLogin = () => {
setAuth["loginNeeded"] = false;
setAuth["isLoggedIn"] = false;
navigate(from, { replace: true });
}
const handleLogin = async (e: React.SyntheticEvent) => {
e.preventDefault();
try {
const response = await axios.get(
"/",
{
// headers: {
// 'Content-Type': 'application/json',
// 'Authorization': 'Basic dXNlcjp1c2Vy'
// }
auth: {
username: username,
password: password
}
}
);
const status = response?.status;
if (status === undefined) {
setHasError(true);
} else {
setAuth["loginNeeded"] = true
setAuth["isLoggedIn"] = true;
setAuth["username"] = username;
setAuth["password"] = password;
setHasError(false);
navigate(from, { replace: true });
console.log("logged in!");
}
} catch (error) {
console.log(error);
setHasError(true);
}
setUsername(username);
setPassword("");
};
return (
<Container className="login">
<Stack className="grid-container" spacing={2}>
<InputLabel className="login-label" >Login to Docker Registry</InputLabel>
<TextField
className="username-input"
id="outlined-basic"
label="Username"
variant="outlined"
value={username}
placeholder="Username"
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<TextField
className="password-input"
id="outlined-basic"
label="Password"
variant="outlined"
value={password}
placeholder="Password"
error={hasError}
helperText={hasError ? "Username or Password Incorrect!" : ""}
type="password"
onChange={(event) => {
setPassword(event.target.value);
}}
/>
<Stack direction="row" justifyContent="space-between" spacing={2}>
<Button
className="skip-login-btn"
variant="contained"
type="submit"
onClick={handleSkipLogin}
>
Skip Login
</Button>
<Button
endIcon={<LoginIcon />}
className="login-btn"
variant="contained"
type="submit"
onClick={handleLogin}
>
Login
</Button>
</Stack>
</Stack>
</Container>
);
};
export default Login;

View File

@ -16,8 +16,7 @@ const RegistryURL: React.FC = () => {
e.preventDefault(); e.preventDefault();
try { try {
let axoisInstance = axios(registryURL); // axios.interceptors.response.use(
// axoisInstance.interceptors.response.use(
// response => response, // response => response,
// error => { // error => {
// if (error.response.status === 401) { // if (error.response.status === 401) {
@ -28,7 +27,7 @@ const RegistryURL: React.FC = () => {
// return error; // return error;
// } // }
// ); // );
let response = await axoisInstance.get( let response = await axios.get(
`/` `/`
); );
console.log(registryURL); console.log(registryURL);

View File

@ -1,17 +1,21 @@
import { createContext, useState } from "react"; import { createContext, useState } from "react";
interface loginInfo { interface loginInfo {
uuid: string; loginNeeded: boolean;
isLoggedIn: boolean; isLoggedIn: boolean;
username: string;
password: string;
} }
const AuthContext = createContext<loginInfo>({ const AuthContext = createContext<loginInfo>({
uuid: "", loginNeeded: true,
isLoggedIn: false, isLoggedIn: false,
username: "",
password: ""
}); });
export const AuthProvider = ({ children }: { children: React.ReactNode }) => { export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const [auth] = useState<loginInfo>({ uuid: "", isLoggedIn: false }); const [auth] = useState<loginInfo>({ loginNeeded: true, isLoggedIn: false, username: "", password: "" });
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>; return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
}; };

View File

@ -2,8 +2,10 @@ import { useContext } from "react";
import AuthContext from "../context/AuthProvider"; import AuthContext from "../context/AuthProvider";
interface loginInfo { interface loginInfo {
uuid: string; loginNeeded: boolean;
isLoggedIn: boolean; isLoggedIn: boolean;
username: string;
password: string;
} }
const useAuth = () => { const useAuth = () => {

View File

@ -0,0 +1,21 @@
.login
.grid-container
align-items: center
margin: auto
margin-top: 20%
max-width: 40% !important
> *
width: 98%
margin: 0.5% 0
.login-label
display: inline-block
text-align: center
margin: 5% 0
font-size: 200%
.login-btn
margin: 5% 0
width: 40%
// background-color: #ed7f88
.skip-login-btn
margin: 5% 0
background-color: #ed7f88

View File

@ -1,2 +1,3 @@
@import "sectionMain" @import "sectionHome"
@import "login"
@import "URL" @import "URL"

View File

@ -0,0 +1 @@
.sectionHome

View File

@ -1 +0,0 @@
.sectionMain

View File

@ -6,6 +6,37 @@ a {
text-decoration: none; text-decoration: none;
} }
.login .grid-container {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin: auto;
margin-top: 20%;
max-width: 40% !important;
}
.login .grid-container > * {
width: 98%;
margin: 0.5% 0;
}
.login .grid-container .login-label {
display: inline-block;
text-align: center;
margin: 5% 0;
font-size: 200%;
}
.login .grid-container .login-btn {
margin: 5% 0;
width: 40%;
}
.login .grid-container .skip-login-btn {
margin: 5% 0;
background-color: #ed7f88;
}
.get-url { .get-url {
max-width: 70%; max-width: 70%;
margin: auto; margin: auto;

View File

@ -1,6 +1,6 @@
{ {
"version": 3, "version": 3,
"mappings": "AG8CA,AAAA,WAAW,CAAC;EACR,KAAK,EAAE,IAAI;CAAG;;AAElB,AAAA,CAAC,CAAC;EACE,eAAe,EAAE,IAAI;CAAG;;AMlD5B,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;;AMlD5B,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": [ "sources": [
"style.sass", "style.sass",
"_variables.sass", "_variables.sass",
@ -10,7 +10,8 @@
"layouts/_header.sass", "layouts/_header.sass",
"layouts/_footer.sass", "layouts/_footer.sass",
"modules/_modules-dir.sass", "modules/_modules-dir.sass",
"modules/_sectionMain.sass", "modules/_sectionHome.sass",
"modules/_login.sass",
"modules/_URL.sass" "modules/_URL.sass"
], ],
"names": [], "names": [],

18
src/utils.tsx Normal file
View File

@ -0,0 +1,18 @@
import axios from "./api/axios"
const checkValidURL = async (url: string) => {
try {
let response = await axios.get(
`/`
);
console.log(url);
} catch (error: any) {
console.log(error);
if (error.response !== undefined) {
return false;
}
}
return true;
}
export { checkValidURL };