diff --git a/net/server/internal/api_getGroups.go b/net/server/internal/api_getGroups.go
index 86845a35..1fa62415 100644
--- a/net/server/internal/api_getGroups.go
+++ b/net/server/internal/api_getGroups.go
@@ -38,7 +38,7 @@ func GetGroups(w http.ResponseWriter, r *http.Request) {
}
}
- var response []*Group
+ response := []*Group{}
for _, slot := range slots {
response = append(response, getGroupModel(&slot))
}
diff --git a/net/web/src/AppContext/AppContext.js b/net/web/src/AppContext/AppContext.js
index 3ce23469..665eec18 100644
--- a/net/web/src/AppContext/AppContext.js
+++ b/net/web/src/AppContext/AppContext.js
@@ -1,5 +1,5 @@
import { createContext } from 'react';
-import useAppContext from './useAppContext.hook';
+import { useAppContext } from './useAppContext.hook';
export const AppContext = createContext({});
diff --git a/net/web/src/AppContext/fetchUtil.js b/net/web/src/AppContext/fetchUtil.js
index 49875fe1..dec3e30b 100644
--- a/net/web/src/AppContext/fetchUtil.js
+++ b/net/web/src/AppContext/fetchUtil.js
@@ -43,3 +43,13 @@ export async function createAccount(username, password) {
checkResponse(profile);
return await profile.json()
}
+
+export async function getGroups(token, revision) {
+ let headers = new Headers()
+ headers.append('Authorization', 'Bearer ' + token);
+ let param = revision == null ? '' : '?revision=' + revision
+ let groups = await fetchWithTimeout('/alias/groups' + param, { method: 'GET', timeout: FETCH_TIMEOUT, headers: headers });
+ checkResponse(groups)
+ return await groups.json()
+}
+
diff --git a/net/web/src/AppContext/useAppContext.hook.js b/net/web/src/AppContext/useAppContext.hook.js
index 1219aac4..eca1ffd2 100644
--- a/net/web/src/AppContext/useAppContext.hook.js
+++ b/net/web/src/AppContext/useAppContext.hook.js
@@ -1,53 +1,103 @@
import { useEffect, useState, useRef } from 'react';
-import { getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
+import { getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
-export default function useAppContext() {
- const [state, setAppState] = useState(null);
+async function updateGroups(token, revision, groupMap) {
+ let groups = await getGroups(token, revision);
+ for (let group of groups) {
+ groupMap.set(group.id, group);
+ }
+}
+
+async function appCreate(username, password, updateState, setWebsocket) {
+ await createAccount(username, password);
+ let access = await setLogin(username, password)
+ updateState({ token: access, access: 'user' });
+ setWebsocket(access)
+ localStorage.setItem("session", JSON.stringify({ token: access, access: 'user' }));
+}
+
+async function appLogin(username, password, updateState, setWebsocket) {
+ let access = await setLogin(username, password)
+ updateState({ token: access, access: 'user' });
+ setWebsocket(access)
+ localStorage.setItem("session", JSON.stringify({ token: access, access: 'user' }));
+}
+
+function appLogout(updateState, clearWebsocket) {
+ updateState({ token: null, access: null });
+ clearWebsocket()
+ localStorage.removeItem("session");
+}
+
+
+
+export function useAppContext() {
+ const [state, setState] = useState(null);
+ const groupRevision = useRef(null);
+ const groups = useRef(new Map());
const ws = useRef(null);
-
- const login = async (username, password) => {
- let access = await setLogin(username, password)
- setAppState({ appToken: access, access: 'user' });
- localStorage.setItem("session", JSON.stringify({ token: access, access: 'user' }));
- connectStatus(access);
+ const revision = useRef(null);
+ const updateState = (value) => {
+ setState((s) => ({ ...s, ...value }))
}
-
- const create = async (username, password) => {
- await createAccount(username, password);
- try {
- await login(username, password)
- } catch(err) {
- throw new Error("login failed after account createion")
- }
- }
-
- const logout = () => {
- ws.current.onclose = () => {}
- ws.current.close()
- ws.current = null
- setAppState({ actions: accessActions })
- localStorage.removeItem("session");
+ const updateData = (value) => {
+ setState((s) => {
+ let data = { ...s.Data, ...value }
+ return { ...s, Data: data }
+ })
}
const userActions = {
- logout: logout,
+ logout: () => {
+ appLogout(updateState, clearWebsocket);
+ }
}
const adminActions = {
- logout: logout,
+ logout: () => {
+ appLogout(updateState, clearWebsocket);
+ }
}
const accessActions = {
- login: login,
- create: create,
+ login: async (username, password) => {
+ await appLogin(username, password, updateState, setWebsocket)
+ },
+ create: async (username, password) => {
+ await appCreate(username, password, updateState, setWebsocket)
+ },
username: getUsername,
available: getAvailable,
}
- const connectStatus = (token) => {
+ const processRevision = async (token) => {
+ while(revision.current != null) {
+ let rev = revision.current;
+
+ // update group if revision changed
+ if (rev.group != groupRevision.current) {
+ await updateGroups(token, groupRevision.current, groups.current);
+ updateData({ groups: Array.from(groups.current.values()) });
+ groupRevision.current = rev.group
+ }
+
+ // check if new revision was received during processing
+ if (rev == revision.current) {
+ revision.current = null
+ }
+ }
+ }
+
+ const setWebsocket = (token) => {
ws.current = new WebSocket("wss://" + window.location.host + "/status");
ws.current.onmessage = (ev) => {
- console.log(ev)
+ if (revision.current != null) {
+ revision.current = JSON.parse(ev.data);
+ }
+ else {
+ revision.current = JSON.parse(ev.data);
+ processRevision(token)
+ }
}
ws.current.onclose = () => {
console.log('ws close')
@@ -57,7 +107,7 @@ export default function useAppContext() {
ws.current.onclose = () => {}
ws.current.onopen = () => {}
ws.current.onerror = () => {}
- connectStatus(token)
+ setWebsocket(token);
}
}, 2000)
}
@@ -69,27 +119,33 @@ export default function useAppContext() {
}
}
+ const clearWebsocket = () => {
+ ws.current.onclose = () => {}
+ ws.current.close()
+ ws.current = null
+ }
+
useEffect(() => {
const storage = localStorage.getItem('session');
if (storage != null) {
try {
const session = JSON.parse(storage)
if (session?.access === 'admin') {
- setAppState({ token: session.token, access: session.access })
- connectStatus(session.token);
+ setState({ token: session.token, access: session.access })
+ setWebsocket(session.token);
} else if (session?.access === 'user') {
- setAppState({ token: session.token, access: session.access })
- connectStatus(session.token);
+ setState({ token: session.token, access: session.access })
+ setWebsocket(session.token);
} else {
- setAppState({})
+ setState({})
}
}
catch(err) {
console.log(err)
- setAppState({})
+ setState({})
}
} else {
- setAppState({})
+ setState({})
}
}, []);
diff --git a/net/web/src/Create/Create.jsx b/net/web/src/Create/Create.jsx
index d7170705..4a0e6927 100644
--- a/net/web/src/Create/Create.jsx
+++ b/net/web/src/Create/Create.jsx
@@ -18,7 +18,7 @@ export function Create() {
addonAfter={state.conflict} />
}
onChange={(e) => actions.setPassword(e.target.value)} value={state.password} />
- }
+ }
onChange={(e) => actions.setConfirmed(e.target.value)} value={state.confirmed} />
actions.onCreate()} disabled={actions.isDisabled()}>
Create Account