From 0932458d4174e25d98ac701c1b0a2d706fc44442 Mon Sep 17 00:00:00 2001 From: Olly Hearn Date: Sat, 27 May 2023 02:25:02 +0300 Subject: [PATCH] new snippet --- api/app/backend/settings.py | 2 +- docker-compose.yml | 2 +- react/Dockerfile | 36 ++++++----- react/app/src/App.js | 34 +++++----- react/app/src/components/Header.jsx | 5 +- react/app/src/components/NewSnippet.jsx | 86 +++++++++++++++++++++++++ react/app/src/components/Snippet.jsx | 25 +++++-- react/app/src/context/AuthContext.jsx | 6 +- react/app/src/pages/NewSnippetPage.jsx | 12 ++++ 9 files changed, 164 insertions(+), 44 deletions(-) create mode 100644 react/app/src/components/NewSnippet.jsx create mode 100644 react/app/src/pages/NewSnippetPage.jsx diff --git a/api/app/backend/settings.py b/api/app/backend/settings.py index bc3a639..e0ec6ee 100644 --- a/api/app/backend/settings.py +++ b/api/app/backend/settings.py @@ -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! 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 diff --git a/docker-compose.yml b/docker-compose.yml index c96998e..daef764 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: ports: - "3000" environment: - - REACT_APP_DOMAIN=ovz1.ollyhearn.gmzem.vps.myjino.ru + - REACT_APP_DOMAIN=localhost volumes: - ./react/app:/app:z # - ./docker_cache/node_modules:/app/node_modules diff --git a/react/Dockerfile b/react/Dockerfile index 30eeb59..017d34f 100644 --- a/react/Dockerfile +++ b/react/Dockerfile @@ -1,17 +1,21 @@ -# stage1 - build react app first -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 +FROM node:20.1 -# stage 2 - build the final image and copy the react build files -FROM nginx:1.25.0-alpine -COPY --from=build /app/build /usr/share/nginx/html -RUN rm /etc/nginx/conf.d/default.conf -COPY nginx/nginx.conf /etc/nginx/conf.d -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] +ENV CI=true +ENV PORT=3000 +ENV HOST=0.0.0.0 +ENV WDS_SOCKET_PORT=0 +ENV REACT_APP_DOMAIN=127.0.0.1 + +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" ] diff --git a/react/app/src/App.js b/react/app/src/App.js index 6fda761..901bde0 100644 --- a/react/app/src/App.js +++ b/react/app/src/App.js @@ -5,25 +5,29 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { AuthProvider } from "./context/AuthContext"; import AuthPage from './pages/AuthPage' import SnippetPage from './pages/SnippetPage' +import NewSnippet from './pages/NewSnippetPage' function App() { - return ( + return ( - - - - } /> - - - } /> - - - } /> - - - + + + + } /> + + + } /> + + + } /> + + + } /> + + + - ); + ); } export default App; diff --git a/react/app/src/components/Header.jsx b/react/app/src/components/Header.jsx index 6eb58bd..d60702d 100644 --- a/react/app/src/components/Header.jsx +++ b/react/app/src/components/Header.jsx @@ -17,10 +17,11 @@ import { } from 'mdb-react-ui-kit'; import 'mdb-react-ui-kit/dist/css/mdb.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"; export default function App() { + const location = useLocation(); const [showBasic, setShowBasic] = useState(false); const { user, logoutUser } = useContext(AuthContext); @@ -69,7 +70,7 @@ export default function App() { - + New snippet diff --git a/react/app/src/components/NewSnippet.jsx b/react/app/src/components/NewSnippet.jsx new file mode 100644 index 0000000..f37911b --- /dev/null +++ b/react/app/src/components/NewSnippet.jsx @@ -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 ( + <> + + + setSnippetTitle(e.target.value)}/> + + +
+ + Create +
+ + ); +} + + +export default NewSnippet; diff --git a/react/app/src/components/Snippet.jsx b/react/app/src/components/Snippet.jsx index 662b396..2608f77 100644 --- a/react/app/src/components/Snippet.jsx +++ b/react/app/src/components/Snippet.jsx @@ -11,9 +11,10 @@ import "@fortawesome/fontawesome-free/css/all.min.css"; import AuthContext from "../context/AuthContext"; import MonacoEditor from 'react-monaco-editor'; -const Snippet = ({ id }) => { +const Snippet = ({ id = 0 }) => { const { authTokens } = useContext(AuthContext); - const [ snippet, setSnippet ] = useState(""); + const [ snippet, setSnippet ] = useState(null); + const [ snippetContent, setSnippetContent ] = useState(""); const getSnippet = async (id) => { const response = await fetch(`http://${process.env.REACT_APP_DOMAIN}/api/snippets/${id}`, { method: "GET", @@ -26,18 +27,20 @@ const Snippet = ({ id }) => { if (response.status === 200) { console.log(data) setSnippet(data) + setSnippetContent(data?.content) } else { 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}`, { - method: "POST", + method: "PATCH", headers: { "Authorization": `Bearer ${authTokens.access}`, "Content-Type": "application/json" }, - body: snippet + body: JSON.stringify({...snippet, "content": snippetContent}) }); const data = await response.json(); @@ -75,7 +78,7 @@ const Snippet = ({ id }) => { }; - return ( + return snippet ? ( <> @@ -86,11 +89,19 @@ const Snippet = ({ id }) => { Save + ) : ( + + + Snippet not found! + Create your own at New Snippet + + ); } diff --git a/react/app/src/context/AuthContext.jsx b/react/app/src/context/AuthContext.jsx index 7f16a7d..bd6baf5 100644 --- a/react/app/src/context/AuthContext.jsx +++ b/react/app/src/context/AuthContext.jsx @@ -40,8 +40,10 @@ export const AuthProvider = ({ children }) => { setUser(jwt_decode(data.access)); localStorage.setItem("authTokens", JSON.stringify(data)); history("/"); - } else { - alert(response.data?.message); + } + + if (response.status === 401) { + alert("Given credentials invalid!") } }; diff --git a/react/app/src/pages/NewSnippetPage.jsx b/react/app/src/pages/NewSnippetPage.jsx new file mode 100644 index 0000000..ddf28d7 --- /dev/null +++ b/react/app/src/pages/NewSnippetPage.jsx @@ -0,0 +1,12 @@ +import BasicPage from './BasicPage' +import NewSnippet from '../components/NewSnippet' + +const NewSnippetPage = () => { + return ( + + + + ); +} + +export default NewSnippetPage;