diff --git a/frontend/index.html b/frontend/index.html index b72e945..9a9af3d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,7 +6,6 @@ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="shortcut icon" href="/src/assets/icons/toolshed-48x48.png" type="image/png"> <title>Toolshed</title> - <link href="/src/assets/base.css" rel="stylesheet"> </head> <body> <div id="app"></div> diff --git a/frontend/src/components/BaseLayout.vue b/frontend/src/components/BaseLayout.vue index 6413f2d..2bb7427 100644 --- a/frontend/src/components/BaseLayout.vue +++ b/frontend/src/components/BaseLayout.vue @@ -98,7 +98,7 @@ <ul class="list-inline"> <li class="list-inline-item"> <a class="text-muted" - target="_blank" href="https://github.com/gr4yj3d1/toolshed">Dev Docs</a> + target="_blank" href="/docs/">API Docs</a> </li> <li class="list-inline-item"> <a class="text-muted" diff --git a/frontend/src/store.js b/frontend/src/store.js index 871da94..c8ea103 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -72,28 +72,18 @@ export default createStore({ }, actions: { async login({commit, dispatch, state}, {username, password, remember}) { - //this.setRemember(remember) - this.commit('setRemember', remember); - /*const response = await fetch('http://10.23.42.128:8000/auth/token/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - username: username, password: password - }) - })*/ + commit('setRemember', remember); const data = await dispatch('apiLocalPost', { - target: '/token/', data: { + target: '/auth/token/', data: { username: username, password: password } }) if (data.token) { commit('setToken', data.token); - commit('setUser', username + '@example.com'); - const j = await dispatch('apiLocalGet', {target: '/keys/'}) + commit('setUser', username); + const j = await dispatch('apiLocalGet', {target: '/auth/keys/'}) const k = j.key - this.commit('setKey', k) + commit('setKey', k) await router.push({path: '/'}); return true; } else { @@ -120,6 +110,9 @@ export default createStore({ if (state.unreachable_neighbors.queryUnreachable(host)) { throw new Error('unreachable neighbor') } + if(!state.user || !state.keypair) { + throw new Error('no user or keypair') + } const url = host + target const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url), state.keypair.signSk) const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature) @@ -128,13 +121,16 @@ export default createStore({ headers: { 'Authorization': auth } - }).catch( err => state.unreachable_neighbors.unreachable(host) + }).catch(err => state.unreachable_neighbors.unreachable(host) ).then(response => response.json()) }, async apiFederatedPost({state}, {host, target, data}) { if (state.unreachable_neighbors.queryUnreachable(host)) { throw new Error('unreachable neighbor') } + if(!state.user || !state.keypair) { + throw new Error('no user or keypair') + } const url = host + target const json = JSON.stringify(data) const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url + json), state.keypair.signSk) @@ -146,24 +142,26 @@ export default createStore({ 'Content-Type': 'application/json' }, body: json - }).catch( err => state.unreachable_neighbors.unreachable(host) + }).catch(err => state.unreachable_neighbors.unreachable(host) ).then(response => response.json()) }, async apiLocalGet({state}, {target}) { const auth = state.token ? {'Authorization': 'Token ' + state.token} : {} - return await fetch('http://10.23.42.128:8000/auth' + target, { + return await fetch(target, { method: 'GET', - headers: auth + headers: auth, + credentials: 'omit' }).then(response => response.json()) }, async apiLocalPost({state}, {target, data}) { const auth = state.token ? {'Authorization': 'Token ' + state.token} : {} - return await fetch('http://10.23.42.128:8000/auth' + target, { + return await fetch(target, { method: 'POST', headers: { 'Content-Type': 'application/json', ...auth }, + credentials: 'omit', body: JSON.stringify(data) }).then(response => response.json()) } diff --git a/frontend/src/views/Register.vue b/frontend/src/views/Register.vue index 3396600..60b7a4e 100644 --- a/frontend/src/views/Register.vue +++ b/frontend/src/views/Register.vue @@ -19,34 +19,51 @@ <div class="card"> <div class="card-body"> <div class="m-sm-4"> - <form role="form" method="post" action=""> - <!--{% csrf_token %}--> - <div class="mb-3"> + <form role="form" method="post" @submit.prevent="do_register"> + <div :class="errors.username||errors.domain?['mb-3','is-invalid']:['mb-3']"> <label class="form-label">Username</label> - <input class="form-control form-control-lg" type="text" - name="username" placeholder="Enter your username"/> + <div class="input-group"> + <input class="form-control form-control-lg" + type="text" v-model="form.username" id="validationCustomUsername" + placeholder="Enter your username" required/> + + <div class="input-group-prepend"> + <span class="input-group-text form-control form-control-lg">@</span> + </div> + <select class="form-control form-control-lg" + id="exampleFormControlSelect1" + placeholder="Domain" v-model="form.domain" required> + <option v-for="domain in domains">{{ domain }}</option> + </select> + </div> + <div class="invalid-feedback"> + {{ errors.username }}{{ errors.domain }} + </div> </div> - <span class="text-error">{{ form.username.errors }}</span> - <div class="mb-3"> + + <div :class="errors.email?['mb-3','is-invalid']:['mb-3']"> <label class="form-label">Email</label> <input class="form-control form-control-lg" type="email" - name="email" placeholder="Enter your email"/> + v-model="form.email" placeholder="Enter your email"/> + <div class="invalid-feedback">{{ errors.email }}</div> </div> - <span class="text-error">{{ form.email.errors }}</span> - <div class="mb-3"> + + <div :class="errors.password?['mb-3','is-invalid']:['mb-3']"> <label class="form-label">Password</label> <input class="form-control form-control-lg" type="password" - name="password1" placeholder="Enter your password"/> + v-model="form.password" placeholder="Enter your password"/> + <div class="invalid-feedback">{{ errors.password }}</div> </div> - <span class="text-error">{{ form.password1.errors }}</span> - <div class="mb-3"> + + <div :class="errors.password2?['mb-3','is-invalid']:['mb-3']"> <label class="form-label">Password Check</label> <input class="form-control form-control-lg" type="password" - name="password2" placeholder="Enter your password again"/> + v-model="password2" placeholder="Enter your password again"/> + <div class="invalid-feedback">{{ errors.password2 }}</div> </div> - <span class="text-error">{{ form.password2.errors }}</span> + <div class="text-center mt-3"> - <button type="submit" name="register" class="btn btn-lg btn-primary"> + <button type="submit" class="btn btn-lg btn-primary"> Register </button> </div> @@ -54,7 +71,8 @@ <br/> <div class="text-center"> <p class="mb-0 text-muted"> - Already have an account? <router-link to="/login">Login</router-link> + Already have an account? + <router-link to="/login">Login</router-link> </p> </div> </div> @@ -73,16 +91,73 @@ export default { data() { return { msg: 'Register', + password2: '', form: { username: '', + domain: '', email: '', - password1: '', - password2: '',} + password: '', + }, + errors: { + username: null, + domain: null, + email: null, + password: null, + password2: null, + }, + domains: [] } + }, + methods: { + do_register() { + console.log('do_register'); + console.log(this.form); + if (this.form.password !== this.password2) { + this.errors.password2 = 'Passwords do not match'; + return; + } else { + this.errors.password2 = null; + } + fetch('/auth/register/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(this.form) + }) + .then(response => response.json()) + .then(data => { + if (data.errors) { + console.error('Error:', data.errors); + this.errors = data.errors; + return; + } + console.log('Success:', data); + this.msg = 'Success'; + this.$router.push('/login'); + }) + .catch((error) => { + console.error('Error:', error); + this.msg = 'Error'; + }); + } + }, + mounted() { + fetch('/api/domains/') + .then(response => response.json()) + .then(data => { + this.domains = data; + }); } } </script> <style scoped> +.is-invalid input, .is-invalid select { + border: 1px solid var(--bs-danger); +} +.is-invalid .invalid-feedback { + display: block; +} </style> \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 40c16c4..1c3c751 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -20,9 +20,23 @@ export default defineConfig({ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token, Authorization, Accept,charset,boundary,Content-Length', 'Access-Control-Allow-Credentials': 'true' - - - + }, + proxy: { + '^/api/': { + target: "http://127.0.0.1:8000/", + }, + '^/auth/': { + target: "http://127.0.0.1:8000/", + }, + '^/admin/': { + target: "http://127.0.0.1:8000/", + }, + '^/docs/': { + target: "http://127.0.0.1:8000/", + }, + '^/static/': { + target: "http://127.0.0.1:8000/", + } } } })