diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index 8643cbd..136556f 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -9,6 +9,12 @@ + @@ -68,6 +74,19 @@ export default { flex-grow: 1 } +.sidebar-link, a.sidebar-link { + display: block; + padding: .625rem 1.625rem; + font-weight: 400; + transition: background .1s ease-in-out; + position: relative; + text-decoration: none; + cursor: pointer; + color: #e9ecef80; + background: #222e3c; + border-left: 3px solid transparent; +} + .sidebar-link i, .sidebar-link svg, a.sidebar-link i, a.sidebar-link svg { margin-right: .75rem; color: rgba(233, 236, 239, .5) diff --git a/frontend/src/router.js b/frontend/src/router.js index 9d8f707..275216f 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -1,29 +1,27 @@ import {createRouter, createWebHistory} from 'vue-router' -import Index from '@/views/Index.vue'; +import Dashboard from '@/views/Dashboard.vue'; import Login from '@/views/Login.vue'; import Register from '@/views/Register.vue'; import store from '@/store'; +import Friends from "@/views/Friends.vue"; const routes = [ - {path: '/', component: Index, meta: {requiresAuth: true}}, + {path: '/', component: Dashboard, meta: {requiresAuth: true}}, + {path: '/friends', component: Friends, meta: {requiresAuth: true}}, {path: '/login', component: Login, meta: {requiresAuth: false}}, {path: '/register', component: Register, meta: {requiresAuth: false}}, ] const router = createRouter({ - // 4. Provide the history implementation to use. We are using the hash history for simplicity here. history: createWebHistory(), linkActiveClass: "active", - routes, // short for `routes: routes` + routes, }) router.beforeEach((to/*, from*/) => { - // instead of having to check every route record with - // to.matched.some(record => record.meta.requiresAuth) + // if this route requires auth, check if logged in, if not, redirect to login page. if (to.meta.requiresAuth && !store.getters.isLoggedIn) { - // this route requires auth, check if logged in - // if not, redirect to login page. console.log("Not logged in, redirecting to login page") return { path: '/login', diff --git a/frontend/src/store.js b/frontend/src/store.js index 6d0dd73..071004d 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -14,6 +14,7 @@ export default createStore({ keypair: null, remember: false, home_servers: null, + all_friends_servers: null, resolver: new FallBackResolver(), unreachable_neighbors: new NeighborsCache(), }, @@ -42,9 +43,15 @@ export default createStore({ } localStorage.setItem('remember', remember); }, + setFriends(state, friends) { + state.friends = friends; + }, setHomeServers(state, home_servers) { state.home_servers = home_servers; }, + setAllFriendsServers(state, servers) { + state.all_friends_servers = servers; + }, logout(state) { state.user = null; state.token = null; @@ -101,9 +108,79 @@ export default createStore({ commit('setHomeServers', promise) return promise }, + 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)) }, + 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 + '/') + }, }, getters: { isLoggedIn(state) { diff --git a/frontend/src/views/Index.vue b/frontend/src/views/Dashboard.vue similarity index 94% rename from frontend/src/views/Index.vue rename to frontend/src/views/Dashboard.vue index 90fbeb1..6c19b16 100644 --- a/frontend/src/views/Index.vue +++ b/frontend/src/views/Dashboard.vue @@ -18,7 +18,7 @@ import * as BIcons from "bootstrap-icons-vue"; import BaseLayout from "@/components/BaseLayout.vue"; export default { - name: 'Index', + name: 'Dashboard', components: { ...BIcons, BaseLayout diff --git a/frontend/src/views/Friends.vue b/frontend/src/views/Friends.vue new file mode 100644 index 0000000..7d49453 --- /dev/null +++ b/frontend/src/views/Friends.vue @@ -0,0 +1,190 @@ + + + + + \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js index f6a3db5..c199937 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -2,7 +2,6 @@ import {fileURLToPath, URL} from 'node:url' import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' -import * as fs from "fs"; export default defineConfig({ plugins: [vue()],