diff --git a/frontend/src/components/PropertyField.vue b/frontend/src/components/PropertyField.vue new file mode 100644 index 0000000..208a687 --- /dev/null +++ b/frontend/src/components/PropertyField.vue @@ -0,0 +1,98 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/TagField.vue b/frontend/src/components/TagField.vue new file mode 100644 index 0000000..08f1944 --- /dev/null +++ b/frontend/src/components/TagField.vue @@ -0,0 +1,79 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/federation.js b/frontend/src/federation.js index a471203..f391710 100644 --- a/frontend/src/federation.js +++ b/frontend/src/federation.js @@ -41,6 +41,33 @@ class ServerSet { throw new Error('all servers failed') } + async patch(auth, target, data) { + if (!auth || typeof auth.buildAuthHeader !== 'function') { + throw new Error('no auth') + } + for (const server of this.servers) { + try { + if (this.unreachable_neighbors.queryUnreachable(server)) { + continue + } + const url = "http://" + server + target // TODO https + return await fetch(url, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + ...auth.buildAuthHeader(url) + }, + credentials: 'omit', + body: JSON.stringify(data) + }).catch(err => this.unreachable_neighbors.unreachable(server) + ).then(response => response.json()) + } catch (e) { + console.error('patch to server failed', server, e) + } + } + throw new Error('all servers failed') + } + async get(auth, target) { if (!auth || typeof auth.buildAuthHeader !== 'function') { throw new Error('no auth') @@ -65,6 +92,58 @@ class ServerSet { } throw new Error('all servers failed') } + + async delete(auth, target) { + if (!auth || typeof auth.buildAuthHeader !== 'function') { + throw new Error('no auth') + } + for (const server of this.servers) { + try { + if (this.unreachable_neighbors.queryUnreachable(server)) { + continue + } + const url = "http://" + server + target // TODO https + return await fetch(url, { + method: 'DELETE', + headers: { + ...auth.buildAuthHeader(url) + }, + credentials: 'omit' + }).catch(err => this.unreachable_neighbors.unreachable(server) + ).then(response => response.json()) + } catch (e) { + console.error('delete from server failed', server, e) + } + } + throw new Error('all servers failed') + } + + async put(auth, target, data) { + if (!auth || typeof auth.buildAuthHeader !== 'function') { + throw new Error('no auth') + } + for (const server of this.servers) { + try { + if (this.unreachable_neighbors.queryUnreachable(server)) { + continue + } + const url = "http://" + server + target // TODO https + return await fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + ...auth.buildAuthHeader(url) + }, + credentials: 'omit', + body: JSON.stringify(data) + }).catch(err => this.unreachable_neighbors.unreachable(server) + ).then(response => response.json()) + } catch (e) { + console.error('put to server failed', server, e) + } + } + throw new Error('all servers failed') + } } class authMethod { @@ -102,6 +181,12 @@ function createTokenAuth(token) { }, context) } -export {ServerSet, createSignAuth, createTokenAuth} +function createNullAuth() { + return new authMethod(() => { + return {} + }, {}) +} + +export {ServerSet, createSignAuth, createTokenAuth, createNullAuth}; diff --git a/frontend/src/store.js b/frontend/src/store.js index 22badeb..77ced70 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -2,12 +2,13 @@ import {createStore} from 'vuex'; import router from '@/router'; import FallBackResolver from "@/dns"; import NeighborsCache from "@/neigbors"; -import {createSignAuth, createTokenAuth, ServerSet} from "@/federation"; +import {createSignAuth, createTokenAuth, createNullAuth, ServerSet} from "@/federation"; export default createStore({ state: { local_loaded: false, + last_load: {}, user: null, token: null, keypair: null, @@ -20,6 +21,8 @@ export default createStore({ all_friends_servers: null, resolver: new FallBackResolver(), unreachable_neighbors: new NeighborsCache(), + tags: [], + properties: [], }, mutations: { setUser(state, user) { @@ -61,6 +64,14 @@ export default createStore({ console.log('setAllFriendsServers', servers) state.all_friends_servers = servers; }, + setTags(state, tags) { + console.log('setTags', tags) + state.tags = tags; + }, + setProperties(state, properties) { + console.log('setProperties', properties) + state.properties = properties; + }, logout(state) { state.user = null; state.token = null; @@ -154,66 +165,6 @@ export default createStore({ async getFriendServers({state, dispatch, commit}, {username}) { return dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) }, - /*async apiFederatedGet({state}, {host, target}) { - 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 = "http://" + host + target // TODO https - const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url), state.keypair.signSk) - const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature) - return await fetch(url, { - method: 'GET', - headers: { - 'Authorization': auth - } - }).catch(err => state.unreachable_neighbors.unreachable(host) - ).then(response => response.json()) - }, - async apiFederatedPost({state}, {host, target, data}) { - console.log('apiFederatedPost', 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 = "http://" + host + target // TODO https - const json = JSON.stringify(data) - const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url + json), state.keypair.signSk) - const auth = 'Signature ' + state.user + ':' + nacl.to_hex(signature) - return await fetch(url, { - method: 'POST', - headers: { - 'Authorization': auth, - 'Content-Type': 'application/json' - }, - body: json - }).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(target, { - method: 'GET', - headers: auth, - credentials: 'omit' - }).then(response => response.json()) - }, - async apiLocalPost({state}, {target, data}) { - const auth = state.token ? {'Authorization': 'Token ' + state.token} : {} - return await fetch(target, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...auth - }, - credentials: 'omit', - body: JSON.stringify(data) - }).then(response => response.json()) - },*/ async fetchInventoryItems({commit, dispatch, getters}) { const servers = await dispatch('getHomeServers') const items = await servers.get(getters.signAuth, '/api/inventory_items/') @@ -224,6 +175,18 @@ export default createStore({ const servers = await dispatch('getAllFriendsServers') return await servers.get(getters.signAuth, '/api/inventory/search/?q=' + query) }, + async createInventoryItem({state, dispatch, getters}, {item}) { + const servers = await dispatch('getHomeServers') + return await servers.post(getters.signAuth, '/api/inventory_items/', item) + }, + async updateInventoryItem({state, dispatch, getters}, {item}) { + const servers = await dispatch('getHomeServers') + return await servers.patch(getters.signAuth, '/api/inventory_items/' + item.id + '/', item) + }, + async deleteInventoryItem({state, dispatch, getters}, {item}) { + const servers = await dispatch('getHomeServers') + return await servers.delete(getters.signAuth, '/api/inventory_items/' + item.id + '/') + }, /*async searchInventoryItems() { try { const servers = await this.fetchFriends().then(friends => friends.map(friend => this.lookupServer({username: friend.name}))) @@ -249,7 +212,6 @@ export default createStore({ return await servers.get(getters.signAuth, '/api/friendrequests/') }, async requestFriend({state, dispatch, getters}, {username}) { - console.log('requesting friend ' + username) if (username in state.friends) { return true; } @@ -261,7 +223,6 @@ export default createStore({ if (home_reply.status !== 'pending' || !home_reply.secret) return false; - console.log('home_reply', home_reply) const befriendee_servers = await dispatch('getFriendServers', {username}) const ext_reply = befriendee_servers.post(getters.signAuth, '/api/friendrequests/', { befriender: state.user, @@ -269,16 +230,13 @@ export default createStore({ befriender_key: nacl.to_hex(state.keypair.signPk), secret: home_reply.secret }) - console.log('ext_reply', ext_reply) return true; }, async acceptFriend({state, dispatch, getters}, {id, secret, befriender}) { - console.log('accepting friend ' + id) const home_servers = await dispatch('getHomeServers') const home_reply = await home_servers.post(getters.signAuth, '/api/friends/', { friend_request_id: id, secret: secret }) - console.log('home_reply', home_reply) const ext_servers = await dispatch('getFriendServers', {username: befriender}) const ext_reply = await ext_servers.post(getters.signAuth, '/api/friendrequests/', { befriender: state.user, @@ -286,13 +244,32 @@ export default createStore({ befriender_key: nacl.to_hex(state.keypair.signPk), secret: secret }) - console.log('ext_reply', ext_reply) return true }, async declineFriend({state, dispatch}, args) { // TODO implement console.log('declining friend ' + args) }, + async fetchTags({state, commit, dispatch, getters}) { + if(state.last_load.tags > Date.now() - 1000 * 60 * 60 * 24) { + return state.tags + } + const servers = await dispatch('getHomeServers') + const data = await servers.get(getters.signAuth, '/api/tags/') + commit('setTags', data) + state.last_load.tags = Date.now() + return data + }, + async fetchProperties({state, commit, dispatch, getters}) { + if(state.last_load.properties > Date.now() - 1000 * 60 * 60 * 24) { + return state.properties + } + const servers = await dispatch('getHomeServers') + const data = await servers.get(getters.signAuth, '/api/properties/') + commit('setProperties', data) + state.last_load.properties = Date.now() + return data + } }, getters: { isLoggedIn(state) { @@ -316,6 +293,9 @@ export default createStore({ console.log('tokenAuth', state.token) return createTokenAuth(state.token) }, + nullAuth(state) { + return createNullAuth({}) + }, inventory_items(state) { return Object.entries(state.item_map).reduce((acc, [url, items]) => { return acc.concat(items) diff --git a/frontend/src/views/InventoryNew.vue b/frontend/src/views/InventoryNew.vue index 2cfb5e7..de2b075 100644 --- a/frontend/src/views/InventoryNew.vue +++ b/frontend/src/views/InventoryNew.vue @@ -6,6 +6,24 @@
Create New Item
+
+
    +
  • + {{ tag }} +
  • +
+ + +
+
+
    +
  • + {{ property.name }}: {{ property.value }} +
  • +
+ + +
- +
@@ -44,9 +63,10 @@