This commit is contained in:
j3d1 2023-05-11 12:30:57 +02:00
parent d21f4a331b
commit bbbf15cfb3
5 changed files with 130 additions and 44 deletions

View file

@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <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"> <link rel="shortcut icon" href="/src/assets/icons/toolshed-48x48.png" type="image/png">
<title>Toolshed</title> <title>Toolshed</title>
<link href="/src/assets/base.css" rel="stylesheet">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View file

@ -98,7 +98,7 @@
<ul class="list-inline"> <ul class="list-inline">
<li class="list-inline-item"> <li class="list-inline-item">
<a class="text-muted" <a class="text-muted"
target="_blank" href="https://github.com/gr4yj3d1/toolshed">Dev Docs</a> target="_blank" href="/docs/">API Docs</a>
</li> </li>
<li class="list-inline-item"> <li class="list-inline-item">
<a class="text-muted" <a class="text-muted"

View file

@ -72,28 +72,18 @@ export default createStore({
}, },
actions: { actions: {
async login({commit, dispatch, state}, {username, password, remember}) { async login({commit, dispatch, state}, {username, password, remember}) {
//this.setRemember(remember) commit('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
})
})*/
const data = await dispatch('apiLocalPost', { const data = await dispatch('apiLocalPost', {
target: '/token/', data: { target: '/auth/token/', data: {
username: username, password: password username: username, password: password
} }
}) })
if (data.token) { if (data.token) {
commit('setToken', data.token); commit('setToken', data.token);
commit('setUser', username + '@example.com'); commit('setUser', username);
const j = await dispatch('apiLocalGet', {target: '/keys/'}) const j = await dispatch('apiLocalGet', {target: '/auth/keys/'})
const k = j.key const k = j.key
this.commit('setKey', k) commit('setKey', k)
await router.push({path: '/'}); await router.push({path: '/'});
return true; return true;
} else { } else {
@ -120,6 +110,9 @@ export default createStore({
if (state.unreachable_neighbors.queryUnreachable(host)) { if (state.unreachable_neighbors.queryUnreachable(host)) {
throw new Error('unreachable neighbor') throw new Error('unreachable neighbor')
} }
if(!state.user || !state.keypair) {
throw new Error('no user or keypair')
}
const url = host + target const url = host + target
const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url), state.keypair.signSk) const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url), state.keypair.signSk)
const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature) const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature)
@ -128,13 +121,16 @@ export default createStore({
headers: { headers: {
'Authorization': auth 'Authorization': auth
} }
}).catch( err => state.unreachable_neighbors.unreachable(host) }).catch(err => state.unreachable_neighbors.unreachable(host)
).then(response => response.json()) ).then(response => response.json())
}, },
async apiFederatedPost({state}, {host, target, data}) { async apiFederatedPost({state}, {host, target, data}) {
if (state.unreachable_neighbors.queryUnreachable(host)) { if (state.unreachable_neighbors.queryUnreachable(host)) {
throw new Error('unreachable neighbor') throw new Error('unreachable neighbor')
} }
if(!state.user || !state.keypair) {
throw new Error('no user or keypair')
}
const url = host + target const url = host + target
const json = JSON.stringify(data) const json = JSON.stringify(data)
const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url + json), state.keypair.signSk) 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' 'Content-Type': 'application/json'
}, },
body: json body: json
}).catch( err => state.unreachable_neighbors.unreachable(host) }).catch(err => state.unreachable_neighbors.unreachable(host)
).then(response => response.json()) ).then(response => response.json())
}, },
async apiLocalGet({state}, {target}) { async apiLocalGet({state}, {target}) {
const auth = state.token ? {'Authorization': 'Token ' + state.token} : {} 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', method: 'GET',
headers: auth headers: auth,
credentials: 'omit'
}).then(response => response.json()) }).then(response => response.json())
}, },
async apiLocalPost({state}, {target, data}) { async apiLocalPost({state}, {target, data}) {
const auth = state.token ? {'Authorization': 'Token ' + state.token} : {} 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', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...auth ...auth
}, },
credentials: 'omit',
body: JSON.stringify(data) body: JSON.stringify(data)
}).then(response => response.json()) }).then(response => response.json())
} }

View file

@ -19,34 +19,51 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="m-sm-4"> <div class="m-sm-4">
<form role="form" method="post" action=""> <form role="form" method="post" @submit.prevent="do_register">
<!--{% csrf_token %}--> <div :class="errors.username||errors.domain?['mb-3','is-invalid']:['mb-3']">
<div class="mb-3">
<label class="form-label">Username</label> <label class="form-label">Username</label>
<input class="form-control form-control-lg" type="text" <div class="input-group">
name="username" placeholder="Enter your username"/> <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> </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> <label class="form-label">Email</label>
<input class="form-control form-control-lg" type="email" <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> </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> <label class="form-label">Password</label>
<input class="form-control form-control-lg" type="password" <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> </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> <label class="form-label">Password Check</label>
<input class="form-control form-control-lg" type="password" <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> </div>
<span class="text-error">{{ form.password2.errors }}</span>
<div class="text-center mt-3"> <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 Register
</button> </button>
</div> </div>
@ -54,7 +71,8 @@
<br/> <br/>
<div class="text-center"> <div class="text-center">
<p class="mb-0 text-muted"> <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> </p>
</div> </div>
</div> </div>
@ -73,16 +91,73 @@ export default {
data() { data() {
return { return {
msg: 'Register', msg: 'Register',
password2: '',
form: { form: {
username: '', username: '',
domain: '',
email: '', email: '',
password1: '', password: '',
password2: '',} },
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> </script>
<style scoped> <style scoped>
.is-invalid input, .is-invalid select {
border: 1px solid var(--bs-danger);
}
.is-invalid .invalid-feedback {
display: block;
}
</style> </style>

View file

@ -20,9 +20,23 @@ export default defineConfig({
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', '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-Headers': 'Origin, Content-Type, X-Auth-Token, Authorization, Accept,charset,boundary,Content-Length',
'Access-Control-Allow-Credentials': 'true' '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/",
}
} }
} }
}) })