auth fixes

This commit is contained in:
2023-05-24 14:45:56 +03:00
parent 4609ca23fa
commit ca289d2fa2
12 changed files with 3876 additions and 4199 deletions

View File

@ -54,10 +54,10 @@ class RegisterView(APIView):
password=password password=password
) )
refresh = RefreshToken.for_user(user) refresh = RefreshToken.for_user(user)
return { return Response({
'refresh': str(refresh), 'refresh': str(refresh),
'access': str(refresh.access_token), 'access': str(refresh.access_token),
} }, status=200)
# token, created = Token.objects.get_or_create(user=user) # token, created = Token.objects.get_or_create(user=user)
# return Response({ # return Response({
# 'token': token.key, # 'token': token.key,

View File

@ -23,7 +23,7 @@ class SnippetSerializer(serializers.ModelSerializer):
"id", "id",
"created", "created",
"title", "title",
"contetns", "content",
"linenos", "linenos",
"language", "language",
"style", "style",

File diff suppressed because it is too large Load Diff

View File

@ -11,10 +11,14 @@
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"mdb-react-ui-kit": "^6.0.0", "mdb-react-ui-kit": "^6.0.0",
"monaco-editor": "^0.36.1",
"monaco-editor-core": "^0.38.0",
"monaco-languageclient": "^6.0.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-monaco-editor": "^0.52.0",
"react-router-dom": "^6.11.1", "react-router-dom": "^6.11.1",
"react-scripts": "5.0.1", "react-scripts": "^5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {

View File

@ -16,7 +16,7 @@ function App() {
<Route path="/" element={<div className="App"> <Route path="/" element={<div className="App">
<HomePage /> <HomePage />
</div>} /> </div>} />
<Route path="/snippet/3" element={<div className="App"> <Route path="/snippet/:id" element={<div className="App">
<SnippetPage /> <SnippetPage />
</div>} /> </div>} />
</Routes> </Routes>

View File

@ -1,19 +1,19 @@
import React, { useState, useContext } from 'react'; import React, { useState, useContext } from 'react';
import { import {
MDBContainer, MDBContainer,
MDBNavbar, MDBNavbar,
MDBNavbarBrand, MDBNavbarBrand,
MDBNavbarToggler, MDBNavbarToggler,
MDBIcon, MDBIcon,
MDBNavbarNav, MDBNavbarNav,
MDBNavbarItem, MDBNavbarItem,
MDBNavbarLink, MDBNavbarLink,
MDBBtn, MDBBtn,
MDBDropdown, MDBDropdown,
MDBDropdownToggle, MDBDropdownToggle,
MDBDropdownMenu, MDBDropdownMenu,
MDBDropdownItem, MDBDropdownItem,
MDBCollapse, MDBCollapse,
} from 'mdb-react-ui-kit'; } from 'mdb-react-ui-kit';
import 'mdb-react-ui-kit/dist/css/mdb.min.css'; import 'mdb-react-ui-kit/dist/css/mdb.min.css';
import "@fortawesome/fontawesome-free/css/all.min.css"; import "@fortawesome/fontawesome-free/css/all.min.css";
@ -21,66 +21,70 @@ import { Link } from 'react-router-dom';
import AuthContext from "../context/AuthContext"; import AuthContext from "../context/AuthContext";
export default function App() { export default function App() {
const [showBasic, setShowBasic] = useState(false); const [showBasic, setShowBasic] = useState(false);
const { user } = useContext(AuthContext); const { user, logoutUser } = useContext(AuthContext);
return ( const handleLogout = () => {
<MDBNavbar expand='lg' light bgColor='light'> confirm("You are about to logout") && logoutUser();
<MDBContainer fluid> };
<Link to="/">
<MDBNavbarBrand>CodeBox</MDBNavbarBrand>
</Link>
<MDBNavbarToggler return (
aria-controls='navbarSupportedContent' <MDBNavbar expand='lg' light bgColor='light'>
aria-expanded='false' <MDBContainer fluid>
aria-label='Toggle navigation' <Link to="/">
onClick={() => setShowBasic(!showBasic)} <MDBNavbarBrand>CodeBox</MDBNavbarBrand>
> </Link>
<MDBIcon icon='bars' fas />
</MDBNavbarToggler>
<MDBCollapse navbar show={showBasic}> <MDBNavbarToggler
<MDBNavbarNav className='mr-auto mb-2 mb-lg-0'> aria-controls='navbarSupportedContent'
<MDBNavbarItem> aria-expanded='false'
<MDBNavbarLink active aria-current='page' href='#'> aria-label='Toggle navigation'
Home onClick={() => setShowBasic(!showBasic)}
</MDBNavbarLink> >
</MDBNavbarItem> <MDBIcon icon='bars' fas />
<MDBNavbarItem> </MDBNavbarToggler>
<MDBNavbarLink href='#'>News</MDBNavbarLink>
</MDBNavbarItem>
<MDBNavbarItem> <MDBCollapse navbar show={showBasic}>
<MDBDropdown> <MDBNavbarNav className='mr-auto mb-2 mb-lg-0'>
<MDBDropdownToggle tag='a' className='nav-link' role='button'> <MDBNavbarItem>
Snippets <MDBNavbarLink active aria-current='page' href='#'>
</MDBDropdownToggle> Home
<MDBDropdownMenu> </MDBNavbarLink>
<MDBDropdownItem link>Public snippets</MDBDropdownItem> </MDBNavbarItem>
<MDBDropdownItem link>My snippets</MDBDropdownItem> <MDBNavbarItem>
<MDBDropdownItem link>Shared to me snippets</MDBDropdownItem> <MDBNavbarLink href='#'>News</MDBNavbarLink>
</MDBDropdownMenu> </MDBNavbarItem>
</MDBDropdown>
</MDBNavbarItem>
<MDBNavbarItem> <MDBNavbarItem>
<MDBNavbarLink disabled={user && false} href='#' tabIndex={-1} aria-disabled='true' background='#3b71ca'> <MDBDropdown>
New snippet <MDBDropdownToggle tag='a' className='nav-link' role='button'>
</MDBNavbarLink> Snippets
</MDBNavbarItem> </MDBDropdownToggle>
</MDBNavbarNav> <MDBDropdownMenu>
<MDBNavbarNav right> <MDBDropdownItem link>Public snippets</MDBDropdownItem>
{ user && ( <MDBDropdownItem link>My snippets</MDBDropdownItem>
<MDBNavbarItem> <MDBDropdownItem link>Shared to me snippets</MDBDropdownItem>
<MDBNavbarLink href='#' aria-disabled='true' background='#3b71ca'> </MDBDropdownMenu>
{user?.username} </MDBDropdown>
</MDBNavbarLink> </MDBNavbarItem>
</MDBNavbarItem>
)} <MDBNavbarItem>
</MDBNavbarNav> <MDBNavbarLink disabled={user && false} href='#' tabIndex={-1} aria-disabled='true' background='#3b71ca'>
</MDBCollapse> New snippet
</MDBContainer> </MDBNavbarLink>
</MDBNavbar> </MDBNavbarItem>
); </MDBNavbarNav>
<MDBNavbarNav right>
{ user && (
<MDBNavbarItem>
<MDBNavbarLink href='#' aria-disabled='true' background='#3b71ca' onClick={handleLogout}>
{user?.username}
</MDBNavbarLink>
</MDBNavbarItem>
)}
</MDBNavbarNav>
</MDBCollapse>
</MDBContainer>
</MDBNavbar>
);
} }

View File

@ -1,15 +1,15 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import {
MDBContainer, MDBContainer,
MDBTabs, MDBTabs,
MDBTabsItem, MDBTabsItem,
MDBTabsLink, MDBTabsLink,
MDBTabsContent, MDBTabsContent,
MDBTabsPane, MDBTabsPane,
MDBBtn, MDBBtn,
MDBIcon, MDBIcon,
MDBInput, MDBInput,
MDBCheckbox MDBCheckbox
} }
from 'mdb-react-ui-kit'; from 'mdb-react-ui-kit';
import 'mdb-react-ui-kit/dist/css/mdb.min.css'; import 'mdb-react-ui-kit/dist/css/mdb.min.css';
@ -19,75 +19,82 @@ import AuthContext from "../context/AuthContext";
const LoginForm = () => { const LoginForm = () => {
const [justifyActive, setJustifyActive] = useState('tab1'); const [justifyActive, setJustifyActive] = useState('tab1');
const handleJustifyClick = (value) => { const handleJustifyClick = (value) => {
if (value === justifyActive) { if (value === justifyActive) {
return; return;
} }
setJustifyActive(value); setJustifyActive(value);
}; };
const [username, setUsername] = useState("") const [username, setUsername] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [password2, setPassword2] = useState("")
const { loginUser } = useContext(AuthContext); const { loginUser, registerUser } = useContext(AuthContext);
const handleLoginSubmit = () => { const handleLoginSubmit = () => {
username.length > 0 && loginUser(username, password); username.length > 0 && loginUser(username, password);
}; };
const handleRegisterSubmit = () => {
password === password2 && password.length > 0 ?
username.length > 0 && registerUser(username, password, password2)
: alert("Passwords empty or do not match!")
};
return ( return (
<MDBContainer className="p-3 my-5 d-flex flex-column w-50"> <MDBContainer className="p-3 my-5 d-flex flex-column w-50">
<MDBTabs pills justify className='mb-3 d-flex flex-row justify-content-between'> <MDBTabs pills justify className='mb-3 d-flex flex-row justify-content-between'>
<MDBTabsItem> <MDBTabsItem>
<MDBTabsLink onClick={() => handleJustifyClick('tab1')} active={justifyActive === 'tab1'}> <MDBTabsLink onClick={() => handleJustifyClick('tab1')} active={justifyActive === 'tab1'}>
Login Login
</MDBTabsLink> </MDBTabsLink>
</MDBTabsItem> </MDBTabsItem>
<MDBTabsItem> <MDBTabsItem>
<MDBTabsLink onClick={() => handleJustifyClick('tab2')} active={justifyActive === 'tab2'}> <MDBTabsLink onClick={() => handleJustifyClick('tab2')} active={justifyActive === 'tab2'}>
Register Register
</MDBTabsLink> </MDBTabsLink>
</MDBTabsItem> </MDBTabsItem>
</MDBTabs> </MDBTabs>
<MDBTabsContent> <MDBTabsContent>
<MDBTabsPane show={justifyActive === 'tab1'}> <MDBTabsPane show={justifyActive === 'tab1'}>
<MDBInput wrapperClass='mb-4' label='Username' id='form1' type='username' value={username} onInput={e => setUsername(e.target.value)}/> <MDBInput wrapperClass='mb-4' label='Username' id='form1' type='text' onInput={e => setUsername(e.target.value)}/>
<MDBInput wrapperClass='mb-4' label='Password' id='form2' type='password'value={password} onInput={e => setPassword(e.target.value)}/> <MDBInput wrapperClass='mb-4' label='Password' id='form2' type='password' onInput={e => setPassword(e.target.value)}/>
<div className="d-flex justify-content-between mx-4 mb-4"> <div className="d-flex justify-content-between mx-4 mb-4">
<MDBCheckbox name='flexCheck' value='' id='flexCheckDefault' label='Remember me' /> <MDBCheckbox name='flexCheck' value='' id='flexCheckDefault' label='Remember me' />
</div> </div>
<MDBBtn <MDBBtn
className="mb-4 w-100" className="mb-4 w-100"
onClick={handleLoginSubmit} onClick={handleLoginSubmit}
> >
Sign in Sign in
</MDBBtn> </MDBBtn>
<p className="text-center">Not a member? <a href="#!" onClick={() => handleJustifyClick('tab2')} active={justifyActive === 'tab2'}>Register</a></p> <p className="text-center">Not a member? <a href="#!" onClick={() => handleJustifyClick('tab2')} active={justifyActive === 'tab2'}>Register</a></p>
</MDBTabsPane> </MDBTabsPane>
<MDBTabsPane show={justifyActive === 'tab2'}> <MDBTabsPane show={justifyActive === 'tab2'}>
<MDBInput wrapperClass='mb-4' label='Username' id='form1' type='text'/> <MDBInput wrapperClass='mb-4' label='Username' id='form1' type='text' onInput={e => setUsername(e.target.value)}/>
<MDBInput wrapperClass='mb-4' label='Password' id='form1' type='password'/> <MDBInput wrapperClass='mb-4' label='Password' id='form1' type='password' onInput={e => setPassword(e.target.value)}/>
<MDBInput wrapperClass='mb-4' label='Repeat password' id='form1' type='password' onInput={e => setPassword2(e.target.value)}/>
<MDBBtn className="mb-4 w-100">Sign up</MDBBtn> <MDBBtn className="mb-4 w-100" onClick={handleRegisterSubmit}>Sign up</MDBBtn>
<p className="text-center">Already have an account? <a href="#!" onClick={() => handleJustifyClick('tab1')} active={justifyActive === 'tab1'}>Login</a></p> <p className="text-center">Already have an account? <a href="#!" onClick={() => handleJustifyClick('tab1')} active={justifyActive === 'tab1'}>Login</a></p>
</MDBTabsPane> </MDBTabsPane>
</MDBTabsContent> </MDBTabsContent>
</MDBContainer> </MDBContainer>
); );
} }
export default LoginForm; export default LoginForm;

View File

@ -1,31 +1,33 @@
import React from 'react'; import React, { useContext } from 'react';
import { import {
MDBCard, MDBCard,
MDBCardBody, MDBCardBody,
MDBCardTitle, MDBCardTitle,
MDBCardText, MDBCardText,
MDBCardImage, MDBCardImage,
MDBBtn, MDBBtn,
MDBRipple MDBRipple
} from 'mdb-react-ui-kit'; } from 'mdb-react-ui-kit';
import 'mdb-react-ui-kit/dist/css/mdb.min.css'; import 'mdb-react-ui-kit/dist/css/mdb.min.css';
import "@fortawesome/fontawesome-free/css/all.min.css"; import "@fortawesome/fontawesome-free/css/all.min.css";
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import AuthContext from "../context/AuthContext";
const MainCard = () => { const MainCard = () => {
return ( const { user } = useContext(AuthContext);
<MDBCard> return (
<MDBCardBody> <MDBCard>
<MDBCardTitle>Welcome to CodeBox!</MDBCardTitle> <MDBCardBody>
<MDBCardText> <MDBCardTitle>Welcome to CodeBox!</MDBCardTitle>
CodeBox is a snippet service to let you securely store your code snippets and share them with your friends, colleagues and others! Register and start sharing your code for free! <MDBCardText>
</MDBCardText> CodeBox is a snippet service to let you securely store your code snippets and share them with your friends, colleagues and others! Register and start sharing your code for free!
</MDBCardText>
<Link to='/auth'>Register</Link> { !user && (
<Link to='/auth'>Register</Link>
</MDBCardBody> )}
</MDBCard> </MDBCardBody>
); </MDBCard>
);
} }

View File

@ -1,24 +1,97 @@
import React from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { import {
MDBCard, MDBCard,
MDBCardBody, MDBCardBody,
MDBCardTitle, MDBCardTitle,
MDBCardText, MDBCardText,
MDBBtn,
} from 'mdb-react-ui-kit'; } from 'mdb-react-ui-kit';
import 'mdb-react-ui-kit/dist/css/mdb.min.css'; import 'mdb-react-ui-kit/dist/css/mdb.min.css';
import "@fortawesome/fontawesome-free/css/all.min.css"; import "@fortawesome/fontawesome-free/css/all.min.css";
import AuthContext from "../context/AuthContext";
import MonacoEditor from 'react-monaco-editor';
const Snippet = ({title, text}) => { const Snippet = ({ id }) => {
return ( const { authTokens } = useContext(AuthContext);
<MDBCard> const [ snippet, setSnippet ] = useState("");
<MDBCardBody> const getSnippet = async (id) => {
<MDBCardTitle>{title}</MDBCardTitle> const response = await fetch(`http://127.0.0.1/api/snippets/${id}`, {
<MDBCardText style={{textAlign: "left"}}> method: "GET",
{text} headers: {
</MDBCardText> "Authorization": `Bearer ${authTokens.access}`,
</MDBCardBody> }
</MDBCard> });
); const data = await response.json();
if (response.status === 200) {
console.log(data)
setSnippet(data)
} else {
console.log(data, authTokens)
}
};
const updateSnippet = async (id, snippet) => {
const response = await fetch(`http://127.0.0.1/api/snippets/${id}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${authTokens.access}`,
"Content-Type": "application/json"
},
body: snippet
});
const data = await response.json();
if (response.status === 200) {
alert("Snippet updated!")
} else {
alert(`Error updating snippet: ${data}`)
}
};
useEffect(() => {
getSnippet(id);
}, []);
const options = {
autoIndent: 'full',
contextmenu: true,
fontFamily: 'monospace',
fontSize: 13,
lineHeight: 24,
hideCursorInOverviewRuler: true,
matchBrackets: 'always',
minimap: {
enabled: true,
},
scrollbar: {
horizontalSliderSize: 4,
verticalSliderSize: 18,
},
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false,
cursorStyle: 'line',
automaticLayout: true,
};
return (
<>
<MDBCard>
<MDBCardBody>
<MDBCardTitle>{snippet?.title}</MDBCardTitle>
</MDBCardBody>
</MDBCard>
<div style={{textAlign: "left", height: "80vh"}}>
<MonacoEditor
height="80vh"
options={options}
value={snippet.content}
/>
<MDBBtn className="mb-4 w-100" onClick={updateSnippet}>Save</MDBBtn>
</div>
</>
);
} }

View File

@ -7,89 +7,94 @@ const AuthContext = createContext();
export default AuthContext; export default AuthContext;
export const AuthProvider = ({ children }) => { export const AuthProvider = ({ children }) => {
const [authTokens, setAuthTokens] = useState(() => const [authTokens, setAuthTokens] = useState(() =>
localStorage.getItem("authTokens") localStorage.getItem("authTokens")
? JSON.parse(localStorage.getItem("authTokens")) ? JSON.parse(localStorage.getItem("authTokens"))
: null : null
); );
const [user, setUser] = useState(() => const [user, setUser] = useState(() =>
localStorage.getItem("authTokens") localStorage.getItem("authTokens")
? jwt_decode(localStorage.getItem("authTokens")) ? jwt_decode(localStorage.getItem("authTokens"))
: null : null
); );
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const history = useNavigate(); const history = useNavigate();
const loginUser = async (username, password) => { const loginUser = async (username, password) => {
const response = await fetch("http://127.0.0.1/api/auth/token/", { const response = await fetch("http://127.0.0.1/api/auth/token/", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({
username, username,
password password
}) })
}); });
const data = await response.json(); const data = await response.json();
if (response.status === 200) { if (response.status === 200) {
setAuthTokens(data); console.log(data);
setUser(jwt_decode(data.access)); setAuthTokens(data);
localStorage.setItem("authTokens", JSON.stringify(data)); setUser(jwt_decode(data.access));
history("/"); localStorage.setItem("authTokens", JSON.stringify(data));
} else { history("/");
alert("Something went wrong!"); } else {
} alert(response.data?.message);
}; }
};
const registerUser = async (username, password, password2) => { const registerUser = async (username, password, password2) => {
const response = await fetch("http://127.0.0.1/api/auth/register/", { const response = await fetch("http://127.0.0.1/api/auth/register/", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({
username, username,
password, password,
password2 password2
}) })
}); });
if (response.status === 201) { const data = await response.json();
history("/login"); if (response.status === 200) {
} else { setAuthTokens(data);
alert("Something went wrong!"); setUser(jwt_decode(data.access));
} localStorage.setItem("authTokens", JSON.stringify(data));
}; history("/auth");
} else {
alert(data?.message);
}
};
const logoutUser = () => { const logoutUser = () => {
setAuthTokens(null); setAuthTokens(null);
setUser(null); setUser(null);
localStorage.removeItem("authTokens"); localStorage.removeItem("authTokens");
history("/"); history("/");
}; };
const contextData = { const contextData = {
user, user,
setUser, setUser,
authTokens, authTokens,
setAuthTokens, setAuthTokens,
registerUser, registerUser,
loginUser, loginUser,
logoutUser logoutUser
}; };
useEffect(() => { useEffect(() => {
if (authTokens) { if (authTokens) {
setUser(jwt_decode(authTokens.access)); setUser(jwt_decode(authTokens.access));
} }
setLoading(false); setLoading(false);
}, [authTokens, loading]); }, [authTokens, loading]);
return ( return (
<AuthContext.Provider value={contextData}> <AuthContext.Provider value={contextData}>
{loading ? null : children} {loading ? null : children}
</AuthContext.Provider> </AuthContext.Provider>
); );
}; };

View File

@ -12,3 +12,10 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }
.me {
position:absolute; left:0; top:0;
width:100%; height:100%; max-height:100% !important;
margin:0; padding:0;
overflow:hidden;
}

View File

@ -1,12 +1,14 @@
import { useParams } from 'react-router-dom'
import BasicPage from './BasicPage' import BasicPage from './BasicPage'
import Snippet from '../components/Snippet' import Snippet from '../components/Snippet'
const Page = () => { const SnippetPage = () => {
const { id } = useParams();
return ( return (
<BasicPage style={{margin: "20%"}}> <BasicPage style={{margin: "20%"}}>
<Snippet title="Тестовая заметка" text="Тестовый тест" /> <Snippet id={id} />
</BasicPage> </BasicPage>
); );
} }
export default Page; export default SnippetPage;