diff --git a/README.md b/README.md index b1d5c72..834a3af 100644 --- a/README.md +++ b/README.md @@ -90,16 +90,6 @@ location ~* "^/[0-9a-z!?@_-]{1,99}$" { proxy_set_header X-Real-IP $remote_addr; rewrite ^/(.*)$ https://my-api-url.com/api/item/$1 redirect; } -location /status/ { - auth_basic "Administrator Login"; - auth_basic_user_file /etc/nginx/.htpasswd; - proxy_pass http://127.0.0.1:7000/status; - proxy_set_header x-real-ip $remote_addr; - # Upgrade for Websockets - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; -} location /socket.io { proxy_pass http://127.0.0.1:7000; proxy_set_header x-real-ip $remote_addr; @@ -127,7 +117,13 @@ By default, the Yo backend API is open which would allow anyone who knew your AP - Before leaving Auth0, create a user account for your application under User & Roles. - When starting the Yo client, pass `REACT_APP_AUTH=true` as an ENV variable to enforce user logins. - When starting the Yo server, pass `AUTH=true` as an ENV variable to enable authentication checks. -- Navigate to Yo and login with the previously created user. If successful, you should be logged into the dashboard successfully. +- Navigate to Yo and login with the previously created user. If successful, you should be logged into the dashboard successfully. + +By default, sign-ups via the Auth0 UI are disabled. If you would like to allow user-signup however, you can force this on by passing `REACT_APP_SIGNUPS=true` during Yo client startup. + +## Extras +- If you're using PM2 to manage your node processes, you can use the included `yo-pm2.yaml` to start and deploy the app. +- Yo can also be deployed via Docker using the included `docker-compose.yaml` file. Enter the Yo root directory and run `docker-compose up` to deploy the Yo client, backend and ## ☑ TODO @@ -135,10 +131,11 @@ By default, the Yo backend API is open which would allow anyone who knew your AP - [x] Client Dockerfile - [x] Server Dockerfile - [x] API Authentication -- [ ] Better Error Handling when Navigating to Unset Names +- [x] Edit/Delete Functionality +- [ ] Build and Deploy App +- [ ] Better Error Handling when Navigating to Unset Links - [ ] Pass through for Query Parameters - [ ] Swipeable Tabs -- [ ] Edit/Delete Functionality - [ ] Further refactor Home.js ## Contributers diff --git a/client/package.json b/client/package.json index 64d67f8..aaeffbf 100644 --- a/client/package.json +++ b/client/package.json @@ -21,6 +21,7 @@ "moment": "^2.24.0", "querystringify": "^2.1.1", "react": "^16.8.6", + "react-bootstrap-sweetalert": "^4.4.1", "react-copy-to-clipboard": "^5.0.1", "react-dom": "^16.8.6", "react-router-dom": "^5.0.0", diff --git a/client/src/components/home/Home.js b/client/src/components/home/Home.js index c67a810..7a13dd6 100644 --- a/client/src/components/home/Home.js +++ b/client/src/components/home/Home.js @@ -8,6 +8,7 @@ import io from 'socket.io-client'; import {CopyToClipboard} from 'react-copy-to-clipboard'; import Auth0Lock from 'auth0-lock'; import axios from "axios"; +import SweetAlert from "react-bootstrap-sweetalert"; const socket = io(config.socketUrl); @@ -49,12 +50,16 @@ class Home extends Component { this.getLiveYos = this.getLiveYos.bind(this); this.hideErrorDiags = this.hideErrorDiags.bind(this); this.handleCopy = this.handleCopy.bind(this); + this.handleEdit = this.handleEdit.bind(this); + this.handleDelete = this.handleDelete.bind(this); + this.handleUpdate = this.handleUpdate.bind(this); + this.handleCancel = this.handleCancel.bind(this); } handleUserInput(e) { const name = e.target.name; const value = e.target.value; - this.setState({ [name]: value }); + this.setState({ [name]: value, editingOriginalUrl: value }); } extractHostname(url) { @@ -187,6 +192,91 @@ class Home extends Component { }); } + handleUpdate() { + var accessToken = null; + try{ accessToken = localStorage.getItem("accessToken") }catch(e) { accessToken = null }; + var updEndpoint = this.state.apiUrl + "update/" + this.state.editingLink; + var body = { originalUrl: this.state.editingOriginalUrl } + axios.post(updEndpoint, body, { 'headers' : {'Content-Type': 'application/json', 'Authorization': "Bearer " + accessToken} }) + .then( + this.setState({ + alert: ( + + This link was successfully updated. + + ) + }), + setTimeout(() => { + this.handleCancel() + }, 5000) + ) + .catch(err => + this.setState({ + alert: ( + + There was an error while updating {this.state.editingLink}. + + ) + }) + ) + } + + handleDelete() { + var accessToken = null; + try{ accessToken = localStorage.getItem("accessToken") }catch(e) { accessToken = null }; + var delEndpoint = this.state.apiUrl + "delete/" + this.state.editingLink; + axios.post(delEndpoint, null, { 'headers' : {'Content-Type': 'application/json', 'Authorization': "Bearer " + accessToken} }) + .then( + this.setState({ + alert: ( + + This link was successfully deleted. + + ) + }), + setTimeout(() => { + this.handleCancel() + }, 5000) + ) + .catch(err => + this.setState({ + alert: ( + + There was an error while deleting {this.state.editingLink}. + + ) + }) + ) + } + + handleCancel() { + this.setState({alert: null}); + } + renderButton() { if (!this.state.showLoading) { return ( @@ -249,7 +339,7 @@ class Home extends Component { allowedConnections: ["Username-Password-Authentication"], rememberLastLogin: false, allowForgotPassword: false, - allowSignUp: false, + allowSignUp: process.env.REACT_APP_SIGNUPS || false, closable: false, languageDictionary: {"title":"Yo - The URL Shortener"}, theme: { @@ -465,7 +555,7 @@ class Home extends Component {

- +
@@ -474,17 +564,62 @@ class Home extends Component { href="#!" style={{float: "left"}} className="modal-close waves-effect waves-red red darken-2 btn" - onClick={e => - window.confirm("Are you sure you want to permanently delete this link?") && - console.log("Deleted!") - } + onClick={e => + this.setState({ + alert: ( + + Are you sure? This is permanent! + + ) + }) + } > Delete Cancel - Update + + this.setState({ + alert: ( + + Do you want to update the current URL? + + ) + }) + } + > + Update + + {this.state.alert} diff --git a/client/src/config/config.js.example b/client/src/config/config.js.example index 6c7bf2f..52742c0 100644 --- a/client/src/config/config.js.example +++ b/client/src/config/config.js.example @@ -1,5 +1,5 @@ module.exports = { - apiUrl: "https://yo-api.mysite.io/api/", + apiUrl: "https://yo-api.mysite.io/api/", // Include the trailing slash! socketUrl: "https://yo-api.mysite.io", baseUrl: "https://yo.mysite.io", blockedNames: ['socket.io'], // Items listed here will be blocked as linkNames diff --git a/client/yarn.lock b/client/yarn.lock index e540ab2..df9e7a3 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -4391,17 +4391,7 @@ fstream-ignore@^1.0.5: inherits "2" minimatch "^3.0.0" -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -fstream@^1.0.12: +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.12, fstream@^1.0.2: version "1.0.12" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== @@ -8264,6 +8254,13 @@ react-app-polyfill@^1.0.1: regenerator-runtime "0.13.2" whatwg-fetch "3.0.0" +react-bootstrap-sweetalert@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/react-bootstrap-sweetalert/-/react-bootstrap-sweetalert-4.4.1.tgz#94410a79c6d6a1e7ce4ede810904c90d08f6a01a" + integrity sha512-czUP6EPXb6GZopxxkixm+a5OaV+kbdxhFbsHaFNIF3l9jBypHCv29TrV8OOruAwrG+GpCmEd8LzzyYPZ0aL9TA== + dependencies: + object-assign "^4.1.0" + react-copy-to-clipboard@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz#8eae107bb400be73132ed3b6a7b4fb156090208e" diff --git a/images/architecture.png b/images/architecture.png index 0054ec8..c95ae00 100644 Binary files a/images/architecture.png and b/images/architecture.png differ