toolshed/frontend/src/store.js

388 lines
16 KiB
JavaScript
Raw Normal View History

import {createStore} from 'vuex';
import router from '@/router';
import FallBackResolver from "@/dns";
import NeighborsCache from "@/neigbors";
import {createNullAuth, createSignAuth, createTokenAuth, ServerSet, ServerSetUnion} from "@/federation";
export default createStore({
state: {
local_loaded: false,
last_load: {},
user: null,
token: null,
keypair: null,
remember: false,
2024-04-11 19:21:14 +00:00
friends: [],
item_map: {},
home_servers: null,
2024-04-09 15:19:38 +00:00
all_friends_servers: null,
2023-05-11 02:42:31 +00:00
messages: [],
resolver: new FallBackResolver(),
unreachable_neighbors: new NeighborsCache(),
2024-08-30 21:05:01 +00:00
tags: [],
properties: [],
files: [],
categories: [],
2024-04-11 19:21:14 +00:00
availability_policies: [],
},
mutations: {
setUser(state, user) {
state.user = user;
if (state.remember)
localStorage.setItem('user', user);
},
setToken(state, token) {
state.token = token;
if (state.remember)
localStorage.setItem('token', token);
},
setKey(state, keypair) {
state.keypair = nacl.crypto_sign_keypair_from_seed(nacl.from_hex(keypair))
if (state.remember)
localStorage.setItem('keypair', nacl.to_hex(state.keypair.signSk).slice(0, 64))
},
setRemember(state, remember) {
state.remember = remember;
if (!remember) {
localStorage.removeItem('user');
localStorage.removeItem('token');
localStorage.removeItem('keypair');
}
localStorage.setItem('remember', remember);
},
2024-04-11 19:21:14 +00:00
setInventoryItems(state, {url, items}) {
state.item_map[url] = items;
},
2024-04-09 15:19:38 +00:00
setFriends(state, friends) {
state.friends = friends;
},
setHomeServers(state, home_servers) {
state.home_servers = home_servers;
},
2024-04-09 15:19:38 +00:00
setAllFriendsServers(state, servers) {
state.all_friends_servers = servers;
},
2024-08-30 21:05:01 +00:00
setTags(state, tags) {
state.tags = tags;
},
setProperties(state, properties) {
state.properties = properties;
},
setCategories(state, categories) {
state.categories = categories;
},
2024-04-11 19:21:14 +00:00
setAvailabilityPolicies(state, availability_policies) {
state.availability_policies = availability_policies;
},
2024-08-30 21:05:01 +00:00
setDomains(state, domains) {
state.domains = domains;
},
logout(state) {
state.user = null;
state.token = null;
state.keypair = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
localStorage.removeItem('keypair');
router.push('/login');
},
load_local(state) {
if (state.local_loaded)
return;
const remember = localStorage.getItem('remember');
const user = localStorage.getItem('user');
const token = localStorage.getItem('token');
const keypair = localStorage.getItem('keypair');
if (user && token) {
this.commit('setUser', user);
this.commit('setToken', token);
if (keypair) {
this.commit('setKey', keypair)
}
2023-05-10 21:28:55 +00:00
router.push('/');
}
state.cache_loaded = true;
}
},
actions: {
async login({commit, dispatch, state, getters}, {username, password, remember}) {
commit('setRemember', remember);
2023-05-15 22:43:33 +00:00
const s = await dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors))
const data = await s.post(getters.nullAuth, '/auth/token/', {username, password})
if (data.token && data.key) {
commit('setToken', data.token);
commit('setUser', username);
commit('setKey', data.key);
commit('setHomeServers', s)
return true;
} else {
return false;
}
},
async lookupServer({state}, {username}) {
const domain = username.split('@')[1]
2023-05-10 21:28:55 +00:00
if (domain === 'example.eleon')
return ['10.23.42.186:8000'];
if (domain === 'localhost')
return ['127.0.0.1:8000'];
if (domain === 'example.com')
2023-05-31 18:43:14 +00:00
return ['10.23.42.128:8000','10.23.42.128:8000'];
2023-05-13 22:14:06 +00:00
if (domain === 'example.jedi')
return ['10.23.42.128:8000'];
if (domain === 'example2.com')
return ['10.23.42.128:9000'];
const request = '_toolshed-server._tcp.' + domain + '.'
return await state.resolver.query(request, 'SRV').then(
(result) => result.map(
(answer) => answer.target + ':' + answer.port))
},
async getHomeServers({state, dispatch, commit}) {
if (state.home_servers)
return state.home_servers
const promise = dispatch('lookupServer', {username: state.user}).then(servers => new ServerSet(servers, state.unreachable_neighbors))
commit('setHomeServers', promise)
return promise
},
2024-04-09 15:19:38 +00:00
async getAllKnownServers({state, dispatch, commit}) {
const friends = await dispatch('fetchFriends')
if (state.all_friends_servers)
return state.all_friends_servers
const promise = (async () => {
const servers = new ServerSetUnion([])
const home = await dispatch('getHomeServers')
servers.add(home)
for (const friend of friends) {
const s = await dispatch('lookupServer', {username: friend.username})
servers.add(new ServerSet(s, state.unreachable_neighbors))
}
return servers
})()
commit('setAllFriendsServers', promise)
return promise
},
async getFriendServers({state, dispatch, commit}, {username}) {
return dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors))
},
2024-04-11 19:21:14 +00:00
async fetchInventoryItems({commit, dispatch, getters}) {
const servers = await dispatch('getHomeServers')
const items = await servers.get(getters.signAuth, '/api/inventory_items/')
items.map(item => item.files.map(file => file.owner = item.owner))
commit('setInventoryItems', {url: '/', items})
return items
},
2023-05-28 19:05:22 +00:00
async searchInventories({state, dispatch, getters}, {query}) {
const servers = await dispatch('getAllFriendsServers')
return await servers.get(getters.signAuth, '/api/inventory/search/?q=' + query)
},
2024-08-30 22:21:01 +00:00
async createInventoryItem({state, dispatch, getters}, item) {
const servers = await dispatch('getHomeServers')
const data = {availability_policy: 'private', ...item}
const reply = await servers.post(getters.signAuth, '/api/inventory_items/', data)
state.last_load.files = 0
return reply
},
2024-08-30 21:05:01 +00:00
async updateInventoryItem({state, dispatch, getters}, item) {
const servers = await dispatch('getHomeServers')
const data = {availability_policy: 'friends', ...item}
data.files = data.files.map(file => file.id)
return await servers.patch(getters.signAuth, '/api/inventory_items/' + item.id + '/', data)
},
async deleteInventoryItem({state, dispatch, getters}, item) {
const servers = await dispatch('getHomeServers')
const ret = await servers.delete(getters.signAuth, '/api/inventory_items/' + item.id + '/')
dispatch('fetchInventoryItems')
return ret
},
2023-05-31 18:43:14 +00:00
async fetchSearchResults({state, dispatch, getters}, {query}) {
const servers = await dispatch('getAllFriendsServers')
return await servers.get(getters.signAuth, '/api/search/?query=' + query)
},
2024-04-09 15:19:38 +00:00
async fetchFriends({commit, dispatch, getters, state}) {
const servers = await dispatch('getHomeServers')
const data = await servers.get(getters.signAuth, '/api/friends/')
commit('setFriends', data)
return data
},
async fetchFriendRequests({state, dispatch, getters}) {
const servers = await dispatch('getHomeServers')
return await servers.get(getters.signAuth, '/api/friendrequests/')
},
async requestFriend({state, dispatch, getters}, {username}) {
if (username in state.friends) {
return true;
}
const home_servers = await dispatch('getHomeServers')
const home_reply = await home_servers.post(getters.signAuth, '/api/friendrequests/', {
befriender: state.user,
befriendee: username
})
if (home_reply.status !== 'pending' || !home_reply.secret)
return false;
const befriendee_servers = await dispatch('getFriendServers', {username})
const ext_reply = await befriendee_servers.post(getters.signAuth, '/api/friendrequests/', {
befriender: state.user,
befriendee: username,
befriender_key: nacl.to_hex(state.keypair.signPk),
secret: home_reply.secret
})
return true;
},
async acceptFriend({state, dispatch, getters}, {id, secret, befriender}) {
const home_servers = await dispatch('getHomeServers')
const home_reply = await home_servers.post(getters.signAuth, '/api/friends/', {
friend_request_id: id, secret: secret
})
const ext_servers = await dispatch('getFriendServers', {username: befriender})
const ext_reply = await ext_servers.post(getters.signAuth, '/api/friendrequests/', {
befriender: state.user,
befriendee: befriender,
befriender_key: nacl.to_hex(state.keypair.signPk),
secret: secret
})
return true
},
async declineFriend({state, dispatch, getters}, {id}) {
const servers = await dispatch('getHomeServers')
return await servers.delete(getters.signAuth, '/api/friendrequests/' + id + '/')
},
async dropFriend({state, dispatch, getters}, {id}) {
const servers = await dispatch('getHomeServers')
return await servers.delete(getters.signAuth, '/api/friends/' + id + '/')
},
2024-08-30 21:05:01 +00:00
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
},
async fetchCategories({state, commit, dispatch, getters}) {
if (state.last_load.categories > Date.now() - 1000 * 60 * 60 * 24) {
return state.categories
}
const servers = await dispatch('getHomeServers')
const data = await servers.get(getters.signAuth, '/api/categories/')
commit('setCategories', data)
state.last_load.categories = Date.now()
return data
},
async fetchAvailabilityPolicies({state, commit, dispatch, getters}) {
if (state.last_load.availability_policies > Date.now() - 1000 * 60 * 60 * 24) {
return state.availability_policies
}
const servers = await dispatch('getHomeServers')
const data = await servers.get(getters.signAuth, '/api/availability_policies/')
commit('setAvailabilityPolicies', data.map(policy => ({slug: policy[0], text: policy[1]})))
state.last_load.availability_policies = Date.now()
return data
},
async fetchInfo({state, commit, dispatch, getters}) {
const last_load_info = Math.min(
state.last_load.tags,
state.last_load.properties,
state.last_load.categories,
state.last_load.availability_policies)
if (last_load_info > Date.now() - 1000 * 60 * 60 * 24) {
return state.info
}
const servers = await dispatch('getHomeServers')
const data = await servers.get(getters.signAuth, '/api/info/')
commit('setTags', data.tags)
commit('setProperties', data.properties)
commit('setCategories', data.categories)
commit('setAvailabilityPolicies', data.availability_policies.map(policy => ({
slug: policy[0],
text: policy[1]
})))
commit('setDomains', data.domains)
state.last_load.tags = Date.now()
state.last_load.properties = Date.now()
state.last_load.categories = Date.now()
state.last_load.availability_policies = Date.now()
return data
}
},
getters: {
isLoggedIn(state) {
if (!state.local_loaded) {
state.remember = localStorage.getItem('remember') === 'true'
state.user = localStorage.getItem('user')
state.token = localStorage.getItem('token')
const keypair = localStorage.getItem('keypair')
if (keypair)
state.keypair = nacl.crypto_sign_keypair_from_seed(nacl.from_hex(keypair))
state.local_loaded = true
}
return state.user !== null && state.token !== null;
},
signAuth(state) {
return createSignAuth(state.user, state.keypair.signSk)
},
tokenAuth(state) {
return createTokenAuth(state.token)
},
nullAuth(state) {
return createNullAuth({})
},
2024-04-11 19:21:14 +00:00
inventory_items(state) {
return state.item_map['/'] || []
},
loaded_items(state) {
return Object.entries(state.item_map).reduce((acc, [url, items]) => {
return acc.concat(items)
}, [])
},
2023-05-11 02:09:26 +00:00
notifications(state) {
// supported types: error, warning, info, login, success, friend
const u = state.unreachable_neighbors.list().map(elem => {
return {
type: 'error',
title: elem.domain + ' unreachable',
msg: 'The neighbor ' + elem.domain + ' is currently unreachable. Please try again later.',
time: elem.time
}
})
return [...u, {
type: 'info',
title: 'Welcome to the Toolshed',
msg: 'This is a federated social network. You can add friends from other servers and share items with them.',
time: Date.now()
}, {
type: 'warning',
title: 'Lorem ipsum',
msg: 'Aliquam ex eros, imperdiet vulputate hendrerit et.',
time: Date.now() - 1000 * 60 * 60 * 2
}, {
type: 'login',
title: 'Login from 192.186.1.8',
time: Date.now() - 1000 * 60 * 60 * 5
}, {
type: 'friend',
title: 'New connection',
msg: 'Christina accepted your request.',
time: Date.now() - 1000 * 60 * 60 * 14
}, {
type: 'success',
title: 'Lorem ipsum',
msg: 'Aliquam ex eros, imperdiet vulputate hendrerit et.',
time: Date.now() - 1000 * 60 * 60 * 24
}]
},
}
})