mirror of
https://github.com/h44z/wg-portal.git
synced 2025-09-15 15:21:14 +00:00
V2 alpha - initial version (#172)
Initial alpha codebase for version 2 of WireGuard Portal. This version is considered unstable and incomplete (for example, no public REST API)! Use with care! Fixes/Implements the following issues: - OAuth support #154, #1 - New Web UI with internationalisation support #98, #107, #89, #62 - Postgres Support #49 - Improved Email handling #47, #119 - DNS Search Domain support #46 - Bugfixes #94, #48 --------- Co-authored-by: Fabian Wechselberger <wechselbergerf@hotmail.com>
This commit is contained in:
125
frontend/src/stores/auth.js
Normal file
125
frontend/src/stores/auth.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { notify } from "@kyvg/vue3-notification";
|
||||
import { apiWrapper } from '@/helpers/fetch-wrapper'
|
||||
import router from '../router'
|
||||
|
||||
export const authStore = defineStore({
|
||||
id: 'auth',
|
||||
state: () => ({
|
||||
// initialize state from local storage to enable user to stay logged in
|
||||
user: JSON.parse(localStorage.getItem('user')),
|
||||
providers: [],
|
||||
returnUrl: localStorage.getItem('returnUrl')
|
||||
}),
|
||||
getters: {
|
||||
UserIdentifier: (state) => state.user?.Identifier || 'unknown',
|
||||
User: (state) => state.user,
|
||||
LoginProviders: (state) => state.providers,
|
||||
IsAuthenticated: (state) => state.user != null,
|
||||
IsAdmin: (state) => state.user?.IsAdmin || false,
|
||||
ReturnUrl: (state) => state.returnUrl || '/',
|
||||
},
|
||||
actions: {
|
||||
SetReturnUrl(link) {
|
||||
this.returnUrl = link
|
||||
localStorage.setItem('returnUrl', link)
|
||||
},
|
||||
ResetReturnUrl() {
|
||||
this.returnUrl = null
|
||||
localStorage.removeItem('returnUrl')
|
||||
},
|
||||
// LoadProviders always returns a fulfilled promise, even if the request failed.
|
||||
async LoadProviders() {
|
||||
apiWrapper.get(`/auth/providers`)
|
||||
.then(providers => this.providers = providers)
|
||||
.catch(error => {
|
||||
this.providers = []
|
||||
console.log("Failed to load auth providers: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load external authentication providers!",
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// LoadSession returns promise that might have been rejected if the session was not authenticated.
|
||||
async LoadSession() {
|
||||
return apiWrapper.get(`/auth/session`)
|
||||
.then(session => {
|
||||
if (session.LoggedIn === true) {
|
||||
this.ResetReturnUrl()
|
||||
this.setUserInfo(session)
|
||||
return session.UserIdentifier
|
||||
} else {
|
||||
this.setUserInfo(null)
|
||||
return Promise.reject(new Error('session not authenticated'))
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.setUserInfo(null)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
},
|
||||
// Login returns promise that might have been rejected if the login attempt was not successful.
|
||||
async Login(username, password) {
|
||||
return apiWrapper.post(`/auth/login`, { username, password })
|
||||
.then(user => {
|
||||
this.ResetReturnUrl()
|
||||
this.setUserInfo(user)
|
||||
return user.Identifier
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("Login failed:", err)
|
||||
this.setUserInfo(null)
|
||||
return Promise.reject(new Error("login failed"))
|
||||
})
|
||||
},
|
||||
async Logout() {
|
||||
this.setUserInfo(null)
|
||||
this.ResetReturnUrl() // just to be sure^^
|
||||
|
||||
try {
|
||||
await apiWrapper.post(`/auth/logout`)
|
||||
} catch (e) {
|
||||
console.log("Logout request failed:", e)
|
||||
}
|
||||
|
||||
notify({
|
||||
title: "Logged Out",
|
||||
text: "Logout successful!",
|
||||
type: "warn",
|
||||
})
|
||||
|
||||
|
||||
await router.push('/login')
|
||||
},
|
||||
// -- internal setters
|
||||
setUserInfo(userInfo) {
|
||||
// store user details and jwt in local storage to keep user logged in between page refreshes
|
||||
if (userInfo) {
|
||||
if ('UserIdentifier' in userInfo) { // session object
|
||||
this.user = {
|
||||
Identifier: userInfo['UserIdentifier'],
|
||||
Firstname: userInfo['UserFirstname'],
|
||||
Lastname: userInfo['UserLastname'],
|
||||
Email: userInfo['UserEmail'],
|
||||
IsAdmin: userInfo['IsAdmin']
|
||||
}
|
||||
} else { // user object
|
||||
this.user = {
|
||||
Identifier: userInfo['Identifier'],
|
||||
Firstname: userInfo['Firstname'],
|
||||
Lastname: userInfo['Lastname'],
|
||||
Email: userInfo['Email'],
|
||||
IsAdmin: userInfo['IsAdmin']
|
||||
}
|
||||
}
|
||||
localStorage.setItem('user', JSON.stringify(this.user))
|
||||
} else {
|
||||
this.user = null
|
||||
localStorage.removeItem('user')
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
152
frontend/src/stores/interfaces.js
Normal file
152
frontend/src/stores/interfaces.js
Normal file
@@ -0,0 +1,152 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import {apiWrapper} from '@/helpers/fetch-wrapper'
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import { freshInterface } from '@/helpers/models';
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/interface`
|
||||
|
||||
export const interfaceStore = defineStore({
|
||||
id: 'interfaces',
|
||||
state: () => ({
|
||||
interfaces: [],
|
||||
prepared: freshInterface(),
|
||||
configuration: "",
|
||||
selected: "",
|
||||
fetching: false,
|
||||
}),
|
||||
getters: {
|
||||
Count: (state) => state.interfaces.length,
|
||||
Prepared: (state) => {console.log("STATE:", state.prepared); return state.prepared},
|
||||
All: (state) => state.interfaces,
|
||||
Find: (state) => {
|
||||
return (id) => state.interfaces.find((p) => p.Identifier === id)
|
||||
},
|
||||
GetSelected: (state) => state.interfaces.find((i) => i.Identifier === state.selected) || state.interfaces[0],
|
||||
isFetching: (state) => state.fetching,
|
||||
},
|
||||
actions: {
|
||||
setInterfaces(interfaces) {
|
||||
this.interfaces = interfaces
|
||||
if (this.interfaces.length > 0) {
|
||||
this.selected = this.interfaces[0].Identifier
|
||||
} else {
|
||||
this.selected = ""
|
||||
}
|
||||
this.fetching = false
|
||||
},
|
||||
async LoadInterfaces() {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/all`)
|
||||
.then(this.setInterfaces)
|
||||
.catch(error => {
|
||||
this.setInterfaces([])
|
||||
console.log("Failed to load interfaces: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load interfaces!",
|
||||
})
|
||||
})
|
||||
},
|
||||
setPreparedInterface(iface) {
|
||||
this.prepared = iface;
|
||||
},
|
||||
setInterfaceConfig(ifaceConfig) {
|
||||
this.configuration = ifaceConfig;
|
||||
},
|
||||
async PrepareInterface() {
|
||||
return apiWrapper.get(`${baseUrl}/prepare`)
|
||||
.then(this.setPreparedInterface)
|
||||
.catch(error => {
|
||||
this.prepared = freshInterface()
|
||||
console.log("Failed to load prepared interface: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load prepared interface!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadInterfaceConfig(id) {
|
||||
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}`)
|
||||
.then(this.setInterfaceConfig)
|
||||
.catch(error => {
|
||||
this.configuration = ""
|
||||
console.log("Failed to load interface configuration: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load interface configuration!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async DeleteInterface(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.interfaces = this.interfaces.filter(i => i.Identifier !== id)
|
||||
if (this.interfaces.length > 0) {
|
||||
this.selected = this.interfaces[0].Identifier
|
||||
} else {
|
||||
this.selected = ""
|
||||
}
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async UpdateInterface(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(iface => {
|
||||
let idx = this.interfaces.findIndex((i) => i.Identifier === id)
|
||||
this.interfaces[idx] = iface
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async CreateInterface(formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/new`, formData)
|
||||
.then(iface => {
|
||||
this.interfaces.push(iface)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async ApplyPeerDefaults(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/${base64_url_encode(id)}/apply-peer-defaults`, formData)
|
||||
.then(() => {
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async SaveConfiguration(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/${base64_url_encode(id)}/save-config`)
|
||||
.then(() => {
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
258
frontend/src/stores/peers.js
Normal file
258
frontend/src/stores/peers.js
Normal file
@@ -0,0 +1,258 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from "@/helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import {interfaceStore} from "./interfaces";
|
||||
import {freshPeer, freshStats} from '@/helpers/models';
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/peer`
|
||||
|
||||
export const peerStore = defineStore({
|
||||
id: 'peers',
|
||||
state: () => ({
|
||||
peers: [],
|
||||
stats: {},
|
||||
statsEnabled: false,
|
||||
peer: freshPeer(),
|
||||
prepared: freshPeer(),
|
||||
configuration: "",
|
||||
filter: "",
|
||||
pageSize: 10,
|
||||
pageOffset: 0,
|
||||
pages: [],
|
||||
fetching: false,
|
||||
}),
|
||||
getters: {
|
||||
Find: (state) => {
|
||||
return (id) => state.peers.find((p) => p.Identifier === id)
|
||||
},
|
||||
|
||||
Count: (state) => state.peers.length,
|
||||
Prepared: (state) => {console.log("STATE:", state.prepared); return state.prepared},
|
||||
FilteredCount: (state) => state.Filtered.length,
|
||||
All: (state) => state.peers,
|
||||
Filtered: (state) => {
|
||||
if (!state.filter) {
|
||||
return state.peers
|
||||
}
|
||||
return state.peers.filter((p) => {
|
||||
return p.DisplayName.includes(state.filter) || p.Identifier.includes(state.filter)
|
||||
})
|
||||
},
|
||||
FilteredAndPaged: (state) => {
|
||||
return state.Filtered.slice(state.pageOffset, state.pageOffset + state.pageSize)
|
||||
},
|
||||
ConfigQrUrl: (state) => {
|
||||
return (id) => state.peers.find((p) => p.Identifier === id) ? apiWrapper.url(`${baseUrl}/config-qr/${base64_url_encode(id)}`) : ''
|
||||
},
|
||||
isFetching: (state) => state.fetching,
|
||||
hasNextPage: (state) => state.pageOffset < (state.FilteredCount - state.pageSize),
|
||||
hasPrevPage: (state) => state.pageOffset > 0,
|
||||
currentPage: (state) => (state.pageOffset / state.pageSize)+1,
|
||||
Statistics: (state) => {
|
||||
return (id) => state.statsEnabled && (id in state.stats) ? state.stats[id] : freshStats()
|
||||
},
|
||||
hasStatistics: (state) => state.statsEnabled,
|
||||
|
||||
},
|
||||
actions: {
|
||||
afterPageSizeChange() {
|
||||
// reset pageOffset to avoid problems with new page sizes
|
||||
this.pageOffset = 0
|
||||
this.calculatePages()
|
||||
},
|
||||
calculatePages() {
|
||||
let pageCounter = 1;
|
||||
this.pages = []
|
||||
for (let i = 0; i < this.FilteredCount; i+=this.pageSize) {
|
||||
this.pages.push(pageCounter++)
|
||||
}
|
||||
},
|
||||
gotoPage(page) {
|
||||
this.pageOffset = (page-1) * this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
nextPage() {
|
||||
this.pageOffset += this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
previousPage() {
|
||||
this.pageOffset -= this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
setPeers(peers) {
|
||||
this.peers = peers
|
||||
this.calculatePages()
|
||||
this.fetching = false
|
||||
},
|
||||
setPeer(peer) {
|
||||
this.peer = peer
|
||||
this.fetching = false
|
||||
},
|
||||
setPreparedPeer(peer) {
|
||||
this.prepared = peer;
|
||||
},
|
||||
setPeerConfig(config) {
|
||||
this.configuration = config;
|
||||
},
|
||||
setStats(statsResponse) {
|
||||
if (!statsResponse) {
|
||||
this.stats = {}
|
||||
this.statsEnabled = false
|
||||
}
|
||||
this.stats = statsResponse.Stats
|
||||
this.statsEnabled = statsResponse.Enabled
|
||||
},
|
||||
async PreparePeer(interfaceId) {
|
||||
return apiWrapper.get(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/prepare`)
|
||||
.then(this.setPreparedPeer)
|
||||
.catch(error => {
|
||||
this.prepared = freshPeer()
|
||||
console.log("Failed to load prepared peer: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load prepared peer!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async MailPeerConfig(linkOnly, ids) {
|
||||
return apiWrapper.post(`${baseUrl}/config-mail`, {
|
||||
Identifiers: ids,
|
||||
LinkOnly: linkOnly
|
||||
})
|
||||
.then(() => {
|
||||
notify({
|
||||
title: "Peer Configuration sent",
|
||||
text: "Email sent to linked user!",
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("Failed to send peer configuration: ", error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async LoadPeerConfig(id) {
|
||||
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}`)
|
||||
.then(this.setPeerConfig)
|
||||
.catch(error => {
|
||||
this.configuration = ""
|
||||
console.log("Failed to load peer configuration: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peer configuration!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadPeer(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(this.setPeer)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
console.log("Failed to load peer: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peer!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadStats(interfaceId) {
|
||||
// if no interfaceId is given, use the currently selected interface
|
||||
if (!interfaceId) {
|
||||
interfaceId = interfaceStore().GetSelected.Identifier
|
||||
if (!interfaceId) {
|
||||
return // no interface, nothing to load
|
||||
}
|
||||
}
|
||||
this.fetching = true
|
||||
|
||||
return apiWrapper.get(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/stats`)
|
||||
.then(this.setStats)
|
||||
.catch(error => {
|
||||
this.setStats(undefined)
|
||||
console.log("Failed to load peer stats: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peer stats!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async DeletePeer(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.peers = this.peers.filter(p => p.Identifier !== id)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async UpdatePeer(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(peer => {
|
||||
let idx = this.peers.findIndex((p) => p.Identifier === id)
|
||||
this.peers[idx] = peer
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async CreatePeer(interfaceId, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/new`, formData)
|
||||
.then(peer => {
|
||||
this.peers.push(peer)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async CreateMultiplePeers(interfaceId, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/multiplenew`, formData)
|
||||
.then(peers => {
|
||||
this.peers.push(...peers)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async LoadPeers(interfaceId) {
|
||||
// if no interfaceId is given, use the currently selected interface
|
||||
if (!interfaceId) {
|
||||
interfaceId = interfaceStore().GetSelected.Identifier
|
||||
if (!interfaceId) {
|
||||
return // no interface, nothing to load
|
||||
}
|
||||
}
|
||||
this.fetching = true
|
||||
|
||||
return apiWrapper.get(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/all`)
|
||||
.then(this.setPeers)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
console.log("Failed to load peers: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peers!",
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
137
frontend/src/stores/profile.js
Normal file
137
frontend/src/stores/profile.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from "@/helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import {authStore} from "@/stores/auth";
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
import {freshStats} from "@/helpers/models";
|
||||
|
||||
const baseUrl = `/user`
|
||||
|
||||
export const profileStore = defineStore({
|
||||
id: 'profile',
|
||||
state: () => ({
|
||||
peers: [],
|
||||
stats: {},
|
||||
statsEnabled: false,
|
||||
user: {},
|
||||
filter: "",
|
||||
pageSize: 10,
|
||||
pageOffset: 0,
|
||||
pages: [],
|
||||
fetching: false,
|
||||
}),
|
||||
getters: {
|
||||
FindPeers: (state) => {
|
||||
return (id) => state.peers.find((p) => p.Identifier === id)
|
||||
},
|
||||
CountPeers: (state) => state.peers.length,
|
||||
FilteredPeerCount: (state) => state.FilteredPeers.length,
|
||||
Peers: (state) => state.peers,
|
||||
FilteredPeers: (state) => {
|
||||
if (!state.filter) {
|
||||
return state.peers
|
||||
}
|
||||
return state.peers.filter((p) => {
|
||||
return p.DisplayName.includes(state.filter) || p.Identifier.includes(state.filter)
|
||||
})
|
||||
},
|
||||
FilteredAndPagedPeers: (state) => {
|
||||
return state.FilteredPeers.slice(state.pageOffset, state.pageOffset + state.pageSize)
|
||||
},
|
||||
isFetching: (state) => state.fetching,
|
||||
hasNextPage: (state) => state.pageOffset < (state.FilteredPeerCount - state.pageSize),
|
||||
hasPrevPage: (state) => state.pageOffset > 0,
|
||||
currentPage: (state) => (state.pageOffset / state.pageSize)+1,
|
||||
Statistics: (state) => {
|
||||
return (id) => state.statsEnabled && (id in state.stats) ? state.stats[id] : freshStats()
|
||||
},
|
||||
hasStatistics: (state) => state.statsEnabled,
|
||||
},
|
||||
actions: {
|
||||
afterPageSizeChange() {
|
||||
// reset pageOffset to avoid problems with new page sizes
|
||||
this.pageOffset = 0
|
||||
this.calculatePages()
|
||||
},
|
||||
calculatePages() {
|
||||
let pageCounter = 1;
|
||||
this.pages = []
|
||||
for (let i = 0; i < this.FilteredPeerCount; i+=this.pageSize) {
|
||||
this.pages.push(pageCounter++)
|
||||
}
|
||||
},
|
||||
gotoPage(page) {
|
||||
this.pageOffset = (page-1) * this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
nextPage() {
|
||||
this.pageOffset += this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
previousPage() {
|
||||
this.pageOffset -= this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
setPeers(peers) {
|
||||
this.peers = peers
|
||||
this.fetching = false
|
||||
},
|
||||
setUser(user) {
|
||||
this.user = user
|
||||
this.fetching = false
|
||||
},
|
||||
setStats(statsResponse) {
|
||||
if (!statsResponse) {
|
||||
this.stats = {}
|
||||
this.statsEnabled = false
|
||||
}
|
||||
this.stats = statsResponse.Stats
|
||||
this.statsEnabled = statsResponse.Enabled
|
||||
},
|
||||
async LoadPeers() {
|
||||
this.fetching = true
|
||||
let currentUser = authStore().user.Identifier
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(currentUser)}/peers`)
|
||||
.then(this.setPeers)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
console.log("Failed to load user peers for ", currentUser, ": ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load user peers!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadStats() {
|
||||
this.fetching = true
|
||||
let currentUser = authStore().user.Identifier
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(currentUser)}/stats`)
|
||||
.then(this.setStats)
|
||||
.catch(error => {
|
||||
this.setStats(undefined)
|
||||
console.log("Failed to load peer stats: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peer stats!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadUser() {
|
||||
this.fetching = true
|
||||
let currentUser = authStore().user.Identifier
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(currentUser)}`)
|
||||
.then(this.setUser)
|
||||
.catch(error => {
|
||||
this.setUser({})
|
||||
console.log("Failed to load user for ", currentUser, ": ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load user!",
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
32
frontend/src/stores/security.js
Normal file
32
frontend/src/stores/security.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { notify } from "@kyvg/vue3-notification";
|
||||
import { apiWrapper } from '@/helpers/fetch-wrapper'
|
||||
|
||||
export const securityStore = defineStore({
|
||||
id: 'security',
|
||||
state: () => ({
|
||||
csrfToken: "",
|
||||
}),
|
||||
getters: {
|
||||
CsrfToken: (state) => state.csrfToken,
|
||||
},
|
||||
actions: {
|
||||
SetCsrfToken(token) {
|
||||
this.csrfToken = token
|
||||
},
|
||||
// LoadSecurityProperties always returns a fulfilled promise, even if the request failed.
|
||||
async LoadSecurityProperties() {
|
||||
await apiWrapper.get(`/csrf`)
|
||||
.then(token => this.SetCsrfToken(token))
|
||||
.catch(error => {
|
||||
this.SetCsrfToken("");
|
||||
console.log("Failed to load csrf token: ", error);
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load csrf token!",
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
36
frontend/src/stores/settings.js
Normal file
36
frontend/src/stores/settings.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { notify } from "@kyvg/vue3-notification";
|
||||
import { apiWrapper } from '@/helpers/fetch-wrapper'
|
||||
|
||||
const baseUrl = `/config`
|
||||
|
||||
export const settingsStore = defineStore({
|
||||
id: 'settings',
|
||||
state: () => ({
|
||||
settings: {},
|
||||
}),
|
||||
getters: {
|
||||
Setting: (state) => {
|
||||
return (key) => (key in state.settings) ? state.settings[key] : undefined
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setSettings(settings) {
|
||||
this.settings = settings
|
||||
},
|
||||
// LoadSecurityProperties always returns a fulfilled promise, even if the request failed.
|
||||
async LoadSettings() {
|
||||
await apiWrapper.get(`${baseUrl}/settings`)
|
||||
.then(data => this.setSettings(data))
|
||||
.catch(error => {
|
||||
this.setSettings({});
|
||||
console.log("Failed to load settings: ", error);
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load settings!",
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
147
frontend/src/stores/users.js
Normal file
147
frontend/src/stores/users.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from "@/helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/user`
|
||||
|
||||
export const userStore = defineStore({
|
||||
id: 'users',
|
||||
state: () => ({
|
||||
userPeers: [],
|
||||
users: [],
|
||||
filter: "",
|
||||
pageSize: 10,
|
||||
pageOffset: 0,
|
||||
pages: [],
|
||||
fetching: false,
|
||||
}),
|
||||
getters: {
|
||||
Find: (state) => {
|
||||
return (id) => state.users.find((p) => p.Identifier === id)
|
||||
},
|
||||
Count: (state) => state.users.length,
|
||||
FilteredCount: (state) => state.Filtered.length,
|
||||
All: (state) => state.users,
|
||||
Peers: (state) => state.userPeers,
|
||||
Filtered: (state) => {
|
||||
if (!state.filter) {
|
||||
return state.users
|
||||
}
|
||||
return state.users.filter((u) => {
|
||||
return u.Firstname.includes(state.filter) || u.Lastname.includes(state.filter) || u.Email.includes(state.filter) || u.Identifier.includes(state.filter)
|
||||
})
|
||||
},
|
||||
FilteredAndPaged: (state) => {
|
||||
return state.Filtered.slice(state.pageOffset, state.pageOffset + state.pageSize)
|
||||
},
|
||||
isFetching: (state) => state.fetching,
|
||||
hasNextPage: (state) => state.pageOffset < (state.FilteredCount - state.pageSize),
|
||||
hasPrevPage: (state) => state.pageOffset > 0,
|
||||
currentPage: (state) => (state.pageOffset / state.pageSize)+1,
|
||||
},
|
||||
actions: {
|
||||
afterPageSizeChange() {
|
||||
// reset pageOffset to avoid problems with new page sizes
|
||||
this.pageOffset = 0
|
||||
this.calculatePages()
|
||||
},
|
||||
calculatePages() {
|
||||
let pageCounter = 1;
|
||||
this.pages = []
|
||||
for (let i = 0; i < this.FilteredCount; i+=this.pageSize) {
|
||||
this.pages.push(pageCounter++)
|
||||
}
|
||||
},
|
||||
gotoPage(page) {
|
||||
this.pageOffset = (page-1) * this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
nextPage() {
|
||||
this.pageOffset += this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
previousPage() {
|
||||
this.pageOffset -= this.pageSize
|
||||
|
||||
this.calculatePages()
|
||||
},
|
||||
setUsers(users) {
|
||||
this.users = users
|
||||
this.calculatePages()
|
||||
this.fetching = false
|
||||
},
|
||||
setUserPeers(peers) {
|
||||
this.userPeers = peers
|
||||
this.fetching = false
|
||||
},
|
||||
async LoadUsers() {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/all`)
|
||||
.then(this.setUsers)
|
||||
.catch(error => {
|
||||
this.setUsers([])
|
||||
console.log("Failed to load users: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load users!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async DeleteUser(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.users = this.users.filter(u => u.Identifier !== id)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async UpdateUser(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(user => {
|
||||
let idx = this.users.findIndex((u) => u.Identifier === id)
|
||||
this.users[idx] = user
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async CreateUser(formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/new`, formData)
|
||||
.then(user => {
|
||||
this.users.push(user)
|
||||
this.fetching = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.fetching = false
|
||||
console.log(error)
|
||||
throw new Error(error)
|
||||
})
|
||||
},
|
||||
async LoadUserPeers(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(id)}/peers`)
|
||||
.then(this.setUserPeers)
|
||||
.catch(error => {
|
||||
this.setUserPeers([])
|
||||
console.log("Failed to load user peers for ",id ,": ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load user peers!",
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user