new snippet

This commit is contained in:
2023-05-27 02:25:02 +03:00
parent 0053f57d52
commit 0932458d41
9 changed files with 164 additions and 44 deletions

View File

@ -25,7 +25,7 @@ SECRET_KEY = "django-insecure-o27968vc7m@z*=+$ykqo!b2v+ycc)r2^d8u=)ee)gi5bas-+wm
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['ovz1.ollyhearn.gmzem.vps.myjino.ru'] ALLOWED_HOSTS = ['ovz1.ollyhearn.gmzem.vps.myjino.ru', 'localhost', '127.0.0.1']
# Application definition # Application definition

View File

@ -15,7 +15,7 @@ services:
ports: ports:
- "3000" - "3000"
environment: environment:
- REACT_APP_DOMAIN=ovz1.ollyhearn.gmzem.vps.myjino.ru - REACT_APP_DOMAIN=localhost
volumes: volumes:
- ./react/app:/app:z - ./react/app:/app:z
# - ./docker_cache/node_modules:/app/node_modules # - ./docker_cache/node_modules:/app/node_modules

View File

@ -1,17 +1,21 @@
# stage1 - build react app first FROM node:20.1
FROM node:20-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY ./app/package.json /app/
COPY ./app/yarn.lock /app/
RUN yarn
COPY /app /app
RUN yarn build
# stage 2 - build the final image and copy the react build files ENV CI=true
FROM nginx:1.25.0-alpine ENV PORT=3000
COPY --from=build /app/build /usr/share/nginx/html ENV HOST=0.0.0.0
RUN rm /etc/nginx/conf.d/default.conf ENV WDS_SOCKET_PORT=0
COPY nginx/nginx.conf /etc/nginx/conf.d ENV REACT_APP_DOMAIN=127.0.0.1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] WORKDIR /app
COPY /app /app
# COPY package.json /app/package.json
# COPY package-lock.json /app/package-lock.json
# Same as npm install
RUN npm ci
# COPY . /app
#
RUN printenv
CMD [ "npm", "start" ]

View File

@ -5,25 +5,29 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { AuthProvider } from "./context/AuthContext"; import { AuthProvider } from "./context/AuthContext";
import AuthPage from './pages/AuthPage' import AuthPage from './pages/AuthPage'
import SnippetPage from './pages/SnippetPage' import SnippetPage from './pages/SnippetPage'
import NewSnippet from './pages/NewSnippetPage'
function App() { function App() {
return ( return (
<Router> <Router>
<AuthProvider> <AuthProvider>
<Routes> <Routes>
<Route path="/auth" element={<AuthPage />} /> <Route path="/auth" element={<AuthPage />} />
<Route path="/" element={<div className="App"> <Route path="/" element={<div className="App">
<HomePage /> <HomePage />
</div>} /> </div>} />
<Route path="/snippet/:id" element={<div className="App"> <Route path="/snippet/:id" element={<div className="App">
<SnippetPage /> <SnippetPage />
</div>} /> </div>} />
</Routes> <Route path="/new-snippet" element={<div className="App">
</AuthProvider> <NewSnippet />
</Router> </div>} />
</Routes>
</AuthProvider>
</Router>
); );
} }
export default App; export default App;

View File

@ -17,10 +17,11 @@ import {
} 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, useLocation } from 'react-router-dom';
import AuthContext from "../context/AuthContext"; import AuthContext from "../context/AuthContext";
export default function App() { export default function App() {
const location = useLocation();
const [showBasic, setShowBasic] = useState(false); const [showBasic, setShowBasic] = useState(false);
const { user, logoutUser } = useContext(AuthContext); const { user, logoutUser } = useContext(AuthContext);
@ -69,7 +70,7 @@ export default function App() {
</MDBNavbarItem> </MDBNavbarItem>
<MDBNavbarItem> <MDBNavbarItem>
<MDBNavbarLink disabled={user && false} href='#' tabIndex={-1} aria-disabled='true' background='#3b71ca'> <MDBNavbarLink disabled={user && false} href='/new-snippet' tabIndex={-1} aria-disabled='true' background='#3b71ca'>
New snippet New snippet
</MDBNavbarLink> </MDBNavbarLink>
</MDBNavbarItem> </MDBNavbarItem>

View File

@ -0,0 +1,86 @@
import React, { useContext, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
MDBCard,
MDBCardBody,
MDBCardTitle,
MDBCardText,
MDBBtn,
MDBInput,
} from 'mdb-react-ui-kit';
import 'mdb-react-ui-kit/dist/css/mdb.min.css';
import "@fortawesome/fontawesome-free/css/all.min.css";
import AuthContext from "../context/AuthContext";
import MonacoEditor from 'react-monaco-editor';
const NewSnippet = () => {
const { authTokens } = useContext(AuthContext);
const navigate = useNavigate();
const [ snippetContent, setSnippetContent ] = useState("");
const [ snippetTitle, setSnippetTitle ] = useState("");
const saveSnippet = async () => {
const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/`, {
method: "POST",
headers: {
"Authorization": `Bearer ${authTokens.access}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ "content": snippetContent, "title": snippetTitle })
});
const data = await response.json();
if (response.status === 201) {
alert("Snippet created!")
navigate(`/snippet/${data?.id}`)
} else {
alert(`Error updating snippet: ${data?.detail}`)
}
};
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>
<MDBInput type="text" placeholder='Your snippet title..' value={snippetTitle} onChange={(e) => setSnippetTitle(e.target.value)}/>
</MDBCardBody>
</MDBCard>
<div style={{textAlign: "left", height: "80vh"}}>
<MonacoEditor
height="80vh"
options={options}
value={snippetContent}
onChange={setSnippetContent}
/>
<MDBBtn className="mb-4 w-100" onClick={saveSnippet}>Create</MDBBtn>
</div>
</>
);
}
export default NewSnippet;

View File

@ -11,9 +11,10 @@ import "@fortawesome/fontawesome-free/css/all.min.css";
import AuthContext from "../context/AuthContext"; import AuthContext from "../context/AuthContext";
import MonacoEditor from 'react-monaco-editor'; import MonacoEditor from 'react-monaco-editor';
const Snippet = ({ id }) => { const Snippet = ({ id = 0 }) => {
const { authTokens } = useContext(AuthContext); const { authTokens } = useContext(AuthContext);
const [ snippet, setSnippet ] = useState(""); const [ snippet, setSnippet ] = useState(null);
const [ snippetContent, setSnippetContent ] = useState("");
const getSnippet = async (id) => { const getSnippet = async (id) => {
const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/${id}`, { const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/${id}`, {
method: "GET", method: "GET",
@ -26,18 +27,20 @@ const Snippet = ({ id }) => {
if (response.status === 200) { if (response.status === 200) {
console.log(data) console.log(data)
setSnippet(data) setSnippet(data)
setSnippetContent(data?.content)
} else { } else {
console.log(data, authTokens) console.log(data, authTokens)
} }
}; };
const updateSnippet = async (id, snippet) => { const updateSnippet = async () => {
console.log(snippet, snippetContent);
const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/${id}`, { const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/${id}`, {
method: "POST", method: "PATCH",
headers: { headers: {
"Authorization": `Bearer ${authTokens.access}`, "Authorization": `Bearer ${authTokens.access}`,
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
body: snippet body: JSON.stringify({...snippet, "content": snippetContent})
}); });
const data = await response.json(); const data = await response.json();
@ -75,7 +78,7 @@ const Snippet = ({ id }) => {
}; };
return ( return snippet ? (
<> <>
<MDBCard> <MDBCard>
<MDBCardBody> <MDBCardBody>
@ -86,11 +89,19 @@ const Snippet = ({ id }) => {
<MonacoEditor <MonacoEditor
height="80vh" height="80vh"
options={options} options={options}
value={snippet.content} value={snippetContent}
onChange={setSnippetContent}
/> />
<MDBBtn className="mb-4 w-100" onClick={updateSnippet}>Save</MDBBtn> <MDBBtn className="mb-4 w-100" onClick={updateSnippet}>Save</MDBBtn>
</div> </div>
</> </>
) : (
<MDBCard>
<MDBCardBody>
<MDBCardTitle>Snippet not found!</MDBCardTitle>
<MDBCardText>Create your own at New Snippet </MDBCardText>
</MDBCardBody>
</MDBCard>
); );
} }

View File

@ -40,8 +40,10 @@ export const AuthProvider = ({ children }) => {
setUser(jwt_decode(data.access)); setUser(jwt_decode(data.access));
localStorage.setItem("authTokens", JSON.stringify(data)); localStorage.setItem("authTokens", JSON.stringify(data));
history("/"); history("/");
} else { }
alert(response.data?.message);
if (response.status === 401) {
alert("Given credentials invalid!")
} }
}; };

View File

@ -0,0 +1,12 @@
import BasicPage from './BasicPage'
import NewSnippet from '../components/NewSnippet'
const NewSnippetPage = () => {
return (
<BasicPage style={{margin: "20%"}}>
<NewSnippet />
</BasicPage>
);
}
export default NewSnippetPage;