This commit is contained in:
j3d1 2023-05-13 18:51:53 +02:00
parent 297ad7bd99
commit 905452cdf3
6 changed files with 109 additions and 43 deletions

View file

@ -32,8 +32,8 @@ export default {
} }
}, },
mounted() { mounted() {
console.log(this.$route) //console.log(this.$route)
console.log(this.$route.params.query) //console.log(this.$route.params.query)
this.query = decodeURIComponent(this.$route.params.query || encodeURIComponent("")); this.query = decodeURIComponent(this.$route.params.query || encodeURIComponent(""));
} }
} }

View file

@ -19,6 +19,7 @@ class FallBackResolver {
endpoints: this._servers, endpoints: this._servers,
} }
) )
if(result.answers.length === 0) throw new Error('No answer');
const first = result.answers[0]; const first = result.answers[0];
this._cache[key] = {time: Date.now(), ...first}; // TODO hadle multiple answers this._cache[key] = {time: Date.now(), ...first}; // TODO hadle multiple answers
localStorage.setItem('dns-cache', JSON.stringify(this._cache)); localStorage.setItem('dns-cache', JSON.stringify(this._cache));

View file

@ -67,6 +67,11 @@ export default createStore({
} else { } else {
} }
router.push('/'); router.push('/');
/*if (this.$route.query.redirect) {
router.push({path: this.$route.query.redirect});
} else {
router.push({path: '/'});
}*/
} }
} }
}, },
@ -112,7 +117,7 @@ export default createStore({
if (!state.user || !state.keypair) { if (!state.user || !state.keypair) {
throw new Error('no user or keypair') throw new Error('no user or keypair')
} }
const url = host + target const url = "http://" + host + target // TODO https
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)
return await fetch(url, { return await fetch(url, {
@ -124,13 +129,14 @@ export default createStore({
).then(response => response.json()) ).then(response => response.json())
}, },
async apiFederatedPost({state}, {host, target, data}) { async apiFederatedPost({state}, {host, target, data}) {
console.log('apiFederatedPost', 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) { if (!state.user || !state.keypair) {
throw new Error('no user or keypair') throw new Error('no user or keypair')
} }
const url = host + target const url = "http://" + host + target // TODO https
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)
const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature) const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature)
@ -163,6 +169,20 @@ export default createStore({
credentials: 'omit', credentials: 'omit',
body: JSON.stringify(data) body: JSON.stringify(data)
}).then(response => response.json()) }).then(response => response.json())
},
async requestFriend({state, dispatch}, {username}) {
console.log('requesting friend ' + username)
if(username in state.friends) {
return true;
}
const server = await dispatch('getFriendServer', {username})
const data = await dispatch('apiFederatedPost', {
host: server[0],
target: '/api/friendrequests/',
data: {befriender: state.user, befriendee: username, befriender_key: nacl.to_hex(state.keypair.signPk)}
})
console.log(data)
return true;
} }
}, },
getters: { getters: {

View file

@ -15,29 +15,45 @@
<tr> <tr>
<th style="width:40%;">Name</th> <th style="width:40%;">Name</th>
<th class="d-none d-md-table-cell" style="width:25%">Server</th> <th class="d-none d-md-table-cell" style="width:25%">Server</th>
<th>Actions</th> <th>
<a @click="fetchFriends" class="align-middle">
<b-icon-arrow-clockwise></b-icon-arrow-clockwise>
Refresh
</a>
<a @click="showNewFriend" class="align-middle">
<b-icon-plus></b-icon-plus>
Add Friend
</a>
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-if="show_newfriend">
<td colspan="2">
<input type="text" class="form-control" placeholder="user@domain"
v-model="newfriend">
</td>
<td>
<button class="btn btn-primary" @click="tryRequestFriend">Send Request</button>
</td>
</tr>
<tr v-for="friend in friendslist" :key="friend.name"> <tr v-for="friend in friendslist" :key="friend.name">
<td>{{ friend.name }}</td> <td>{{ friend.name }}</td>
<td class="d-none d-md-table-cell">{{ friend.server.join(', ') }}</td> <td class="d-none d-md-table-cell">{{ friend.server.join(', ') }}</td>
<td class="table-action"> <td class="table-action">
<a href="#"> <a href="#" class="align-middle">
<b-icon-pencil-square></b-icon-pencil-square> <b-icon-pencil-square></b-icon-pencil-square>
Edit
</a> </a>
<a href="#"> <a href="#" class="align-middle">
<b-icon-trash></b-icon-trash> <b-icon-trash></b-icon-trash>
Delete
</a> </a>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="card">
<button class="btn" @click="fetchFriends">Refresh</button>
<router-link to="/inventory/new" class="btn btn-primary">Add</router-link>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -59,6 +75,8 @@ export default {
data() { data() {
return { return {
friends: {}, friends: {},
show_newfriend: false,
newfriend: ""
} }
}, },
computed: { computed: {
@ -75,7 +93,7 @@ export default {
} }
}, },
methods: { methods: {
...mapActions(['getFriends', "getFriendServer"]), ...mapActions(['getFriends', "getFriendServer", "requestFriend"]),
fetchFriends() { fetchFriends() {
this.getFriends().then((friends) => { this.getFriends().then((friends) => {
friends.map((friend) => { friends.map((friend) => {
@ -84,6 +102,18 @@ export default {
}) })
}) })
}) })
},
showNewFriend() {
this.show_newfriend = true
},
tryRequestFriend() {
this.requestFriend({username: this.newfriend}).then((ok) => {
if (ok) {
this.show_newfriend = false
this.newfriend = ""
this.fetchFriends()
}
}).catch(() => {})
} }
}, },
mounted() { mounted() {

View file

@ -73,7 +73,7 @@ export default {
try { try {
const servers = await this.getFriends().then(friends => friends.map(friend => this.getFriendServer({username: friend}))) const servers = await this.getFriends().then(friends => friends.map(friend => this.getFriendServer({username: friend})))
const urls = servers.map(server => server.then(s => { const urls = servers.map(server => server.then(s => {
return {host: `http://${s}`, target: "/api/inventory_items/"} return {host: s, target: "/api/inventory_items/"}
})) }))
urls.map(url => url.then(u => this.apiFederatedGet(u).then(items => { urls.map(url => url.then(u => this.apiFederatedGet(u).then(items => {
this.setInventoryItems({url: u.domain, items}) this.setInventoryItems({url: u.domain, items})

View file

@ -4,37 +4,36 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="card"> <div class="card">
<div class="card-header">Inventory</div> <div class="card-header">Create New Item</div>
<div class="card-body"> <div class="card-body">
<table class="table table-striped"> <div class="mb-3">
<thead> <label for="name" class="form-label">Name</label>
<tr> <input type="text" class="form-control" id="name" name="name"
<th>Item</th> placeholder="Enter item name" v-model="item.name">
<th>Quantity</th> </div>
<th>Owner</th> <div class="mb-3">
<th>Actions</th> <label for="description" class="form-label">Description</label>
</tr> <textarea class="form-control" id="description" name="description"
</thead> placeholder="Enter description" v-model="item.description"></textarea>
<tbody> </div>
<tr v-for="item in inventory_items" :key="item.id"> <div class="mb-3">
<td>{{ item.name }}</td> <label for="quantity" class="form-label">Quantity</label>
<td>{{ item.quantity }}</td> <input type="number" class="form-control" id="quantity" name="quantity"
<td>{{ item.owner }}</td> placeholder="Enter quantity" v-model="item.quantity">
<td> </div>
<a href="#"> <div class="mb-3">
<b-icon-pencil-square></b-icon-pencil-square> <label for="price" class="form-label">Price</label>
</a> <input type="number" class="form-control" id="price" name="price"
<a href="#"> placeholder="Enter price" v-model="item.price">
<b-icon-trash></b-icon-trash> </div>
</a> <div class="mb-3">
</td> <label for="image" class="form-label">Image</label>
</tr> <input type="text" class="form-control" id="image" name="image"
</tbody> placeholder="Enter image" v-model="item.image">
</table> </div>
</div> </div>
<div class="card"> <div class="card">
<button class="btn" @click="getInventoryItems">Refresh</button> <button type="submit" class="btn btn-primary" @click="createInventoryItem(item)">Add</button>
<router-link to="/inventory/new" class="btn btn-primary">Add</router-link>
</div> </div>
</div> </div>
</div> </div>
@ -46,13 +45,29 @@
<script> <script>
import * as BIcons from "bootstrap-icons-vue"; import * as BIcons from "bootstrap-icons-vue";
import BaseLayout from "@/components/BaseLayout.vue"; import BaseLayout from "@/components/BaseLayout.vue";
import {createApp} from "vue";
import {mapActions} from "vuex";
export default { export default {
name: "InventoryNew", name: "InventoryNew",
data() {
return {
item: {
name: "",
description: "",
quantity: 0,
price: 0,
image: ""
}
}
},
components: { components: {
BaseLayout, BaseLayout,
...BIcons ...BIcons
}, },
methods: {
...mapActions(['createInventoryItem']),
},
} }
</script> </script>