From f2db575c7080e5a0b94903ce856d15e7b65cd3c7 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 5 Jan 2023 06:33:14 +0000 Subject: [PATCH] implement owners management --- frontend/src/components/build/Build.tsx | 2 +- frontend/src/components/build/Updates.tsx | 2 + frontend/src/components/build/tabs/Owners.tsx | 257 +++++++++++------- .../src/components/build/tabs/Provider.tsx | 5 + frontend/src/components/build/tabs/Tabs.tsx | 2 +- .../src/components/deployment/Updates.tsx | 3 +- .../deployment/tabs/config/Config.tsx | 8 +- .../deployment/tabs/config/Owners.tsx | 247 +++++++++++------ .../deployment/tabs/config/Provider.tsx | 5 + frontend/src/components/server/Updates.tsx | 2 + .../src/components/server/tabs/Owners.tsx | 159 +++++++---- frontend/src/components/server/tabs/Tabs.tsx | 7 +- .../server/tabs/config/Provider.tsx | 11 +- 13 files changed, 455 insertions(+), 255 deletions(-) diff --git a/frontend/src/components/build/Build.tsx b/frontend/src/components/build/Build.tsx index 3d056aa01..6a5484546 100644 --- a/frontend/src/components/build/Build.tsx +++ b/frontend/src/components/build/Build.tsx @@ -31,8 +31,8 @@ const Build: Component<{}> = (p) => { navigate("/"); } }); - onCleanup(unsub); }); + onCleanup(() => unsub); return ( }> diff --git a/frontend/src/components/build/Updates.tsx b/frontend/src/components/build/Updates.tsx index b2e4922f3..9cf772028 100644 --- a/frontend/src/components/build/Updates.tsx +++ b/frontend/src/components/build/Updates.tsx @@ -2,6 +2,7 @@ import { Component, createEffect, For, + onCleanup, Show, } from "solid-js"; import { useUpdates } from "../../state/hooks"; @@ -25,6 +26,7 @@ const Updates: Component<{}> = (p) => { } }); }); + onCleanup(() => unsub()) return ( = (p) => { - // const { build } = useConfig(); - // const { user } = useUser(); - // const [userSearch, setUserSearch] = createSignal(""); - // const [users, setUsers] = createSignal([]); - // createEffect(() => { - // if (userSearch().length > 0) { - // getUsers(userSearch(), true).then((users) => { - // setUsers(users.filter((user) => !build.owners.includes(user.username))); - // }); - // } else { - // setUsers([]); - // } - // }); - // return ( - // - // - // - // - // setUserSearch("")} - // target={ - // - // } - // content={ - // <> - // - // {(user) => ( - // { - // await addOwnerToBuild(build._id!, user.username); - // pushNotification("good", "owner added to build"); - // setUserSearch(""); - // }} - // confirm="add user" - // > - // {user.username} - // - // )} - // - // no matching users - // - // } - // menuStyle={{ width: "12rem" }} - // /> - // - // {(owner) => ( - // - //
- // {owner} - // {owner === username() && " ( you )"} - //
- // 1}> - // { - // await removeOwnerFromBuild(build._id!, owner); - // pushNotification( - // "good", - // "user removed from collaborators" - // ); - // }} - // > - // remove - // - // - //
- // )} - //
- // - // - // - // - // ); - return
+ const { ws } = useAppState(); + const { build } = useConfig(); + const { user } = useUser(); + const params = useParams(); + const [userSearch, setUserSearch] = createSignal(""); + const [users, setUsers] = createSignal([]); + createEffect(() => { + client.list_users().then(setUsers); + }); + const getUser = (user_id: string) => + users().find((u) => getId(u) === user_id)!; + const searchUsers = createMemo(() => + users().filter( + (u) => + !u.admin && + u.enabled && + u.username.includes(userSearch()) && + (build.permissions[getId(u)] === undefined || + build.permissions[getId(u)] === PermissionLevel.None) + ) + ); + let unsub = () => {}; + createEffect(() => { + unsub(); + unsub = ws.subscribe( + [Operation.ModifyUserPermissions, Operation.ModifyUserEnabled], + () => { + client.list_users().then(setUsers); + } + ); + }); + onCleanup(() => unsub()); + return ( + + + + + setUserSearch("")} + target={ + + } + content={ + <> + + {(user) => ( + { + client.update_user_permissions_on_target({ + user_id: getId(user), + permission: PermissionLevel.Read, + target_type: PermissionsTarget.Build, + target_id: params.id, + }); + setUserSearch(""); + }} + confirm="add user" + > + {user.username} + + )} + + no matching users + + } + menuStyle={{ width: "12rem" }} + /> + + {(user_id) => { + const u = () => getUser(user_id); + const permissions = () => build.permissions[user_id]; + return ( + +
+ {u().username} + {user_id === getId(user()) && " ( you )"} +
+ + { + client.update_user_permissions_on_target({ + user_id, + permission: permission as PermissionLevel, + target_type: PermissionsTarget.Build, + target_id: params.id, + }); + }} + position="bottom right" + /> + { + client.update_user_permissions_on_target({ + user_id, + permission: PermissionLevel.None, + target_type: PermissionsTarget.Build, + target_id: params.id, + }); + }} + > + remove + + +
+ ); + }} +
+ + + + + ); }; export default Owners; diff --git a/frontend/src/components/build/tabs/Provider.tsx b/frontend/src/components/build/tabs/Provider.tsx index fde17ea79..3ce15a958 100644 --- a/frontend/src/components/build/tabs/Provider.tsx +++ b/frontend/src/components/build/tabs/Provider.tsx @@ -2,6 +2,7 @@ import { useParams } from "@solidjs/router"; import { createContext, createEffect, + onCleanup, ParentComponent, useContext, } from "solid-js"; @@ -80,6 +81,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => { }); }); + onCleanup(() => update_unsub()); + let modify_unsub = () => {}; createEffect(() => { @@ -95,6 +98,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => { ); }); + onCleanup(() => modify_unsub()); + const userCanUpdate = () => user().admin || build.permissions[getId(user())] === PermissionLevel.Update; const state = { diff --git a/frontend/src/components/build/tabs/Tabs.tsx b/frontend/src/components/build/tabs/Tabs.tsx index cd41ca478..b9e245f4d 100644 --- a/frontend/src/components/build/tabs/Tabs.tsx +++ b/frontend/src/components/build/tabs/Tabs.tsx @@ -33,7 +33,7 @@ const BuildTabs: Component<{}> = (p) => { title: "build", element: () => , }, - userCanUpdate() && { + user().admin && { title: "collaborators", element: () => , }, diff --git a/frontend/src/components/deployment/Updates.tsx b/frontend/src/components/deployment/Updates.tsx index 40fc4caa1..716d0b226 100644 --- a/frontend/src/components/deployment/Updates.tsx +++ b/frontend/src/components/deployment/Updates.tsx @@ -1,4 +1,4 @@ -import { Component, createEffect, For, Show } from "solid-js"; +import { Component, createEffect, For, onCleanup, Show } from "solid-js"; import { useUpdates } from "../../state/hooks"; import Grid from "../shared/layout/Grid"; import Update from "../update/Update"; @@ -26,6 +26,7 @@ const Updates: Component<{}> = (p) => { } }); }); + onCleanup(() => unsub()); return ( = () => { const { deployment, reset, save, userCanUpdate } = useConfig(); + const { user } = useUser(); const { isMobile } = useAppDimensions(); - const listenerUrl = () => `${URL}/api/listener/deployment/${deployment._id}`; + const listenerUrl = () => `${URL}/api/listener/deployment/${getId(deployment)}`; return ( @@ -97,7 +99,7 @@ const Config: Component<{}> = () => { ), }, - userCanUpdate() && { + user().admin && { title: "collaborators", element: () => ( = (p) => { - // const { deployment } = useConfig(); - // const { user } = useUser(); - // const [userSearch, setUserSearch] = createSignal(""); - // const [users, setUsers] = createSignal([]); - // createEffect(() => { - // if (userSearch().length > 0) { - // getUsers(userSearch(), true).then((users) => { - // setUsers( - // users.filter((user) => !deployment.owners.includes(user.username)) - // ); - // }); - // } else { - // setUsers([]); - // } - // }); - // return ( - // - // setUserSearch("")} - // target={ - // - // } - // content={ - // <> - // - // {(user) => ( - // { - // await addOwnerToDeployment(deployment._id!, user.username); - // pushNotification("good", "owner added to deployment"); - // setUserSearch(""); - // }} - // confirm="add user" - // > - // {user.username} - // - // )} - // - // no matching users - // - // } - // menuStyle={{ width: "12rem" }} - // /> - // - // {(owner) => ( - // - //
- // {owner} - // {owner === username() && " ( you )"} - //
- // 1}> - // { - // await removeOwnerFromDeployment(deployment._id!, owner); - // pushNotification("good", "user removed from collaborators"); - // }} - // > - // remove - // - // - //
- // )} - //
- // - // ); - return
+ const { ws } = useAppState(); + const { deployment } = useConfig(); + const { user } = useUser(); + const params = useParams(); + const [userSearch, setUserSearch] = createSignal(""); + const [users, setUsers] = createSignal([]); + createEffect(() => { + client.list_users().then(setUsers); + }); + const getUser = (user_id: string) => + users().find((u) => getId(u) === user_id)!; + const searchUsers = createMemo(() => + users().filter( + (u) => + !u.admin && + u.enabled && + u.username.includes(userSearch()) && + (deployment.permissions![getId(u)] === undefined || + deployment.permissions![getId(u)] === PermissionLevel.None) + ) + ); + let unsub = () => {}; + createEffect(() => { + unsub(); + unsub = ws.subscribe( + [Operation.ModifyUserPermissions, Operation.ModifyUserEnabled], + () => { + client.list_users().then(setUsers); + } + ); + }); + onCleanup(() => unsub()); + return ( + + + + + setUserSearch("")} + target={ + + } + content={ + <> + + {(user) => ( + { + client.update_user_permissions_on_target({ + user_id: getId(user), + permission: PermissionLevel.Read, + target_type: PermissionsTarget.Deployment, + target_id: params.id, + }); + setUserSearch(""); + }} + confirm="add user" + > + {user.username} + + )} + + no matching users + + } + menuStyle={{ width: "12rem" }} + /> + + {(user_id) => { + const u = () => getUser(user_id); + const permissions = () => deployment.permissions![user_id]; + return ( + +
+ {u().username} + {user_id === getId(user()) && " ( you )"} +
+ + { + client.update_user_permissions_on_target({ + user_id, + permission: permission as PermissionLevel, + target_type: PermissionsTarget.Deployment, + target_id: params.id, + }); + }} + position="bottom right" + /> + { + client.update_user_permissions_on_target({ + user_id, + permission: PermissionLevel.None, + target_type: PermissionsTarget.Deployment, + target_id: params.id, + }); + }} + > + remove + + +
+ ); + }} +
+ + + + + ); }; export default Owners; diff --git a/frontend/src/components/deployment/tabs/config/Provider.tsx b/frontend/src/components/deployment/tabs/config/Provider.tsx index bfb5aa8b3..c9392fd6c 100644 --- a/frontend/src/components/deployment/tabs/config/Provider.tsx +++ b/frontend/src/components/deployment/tabs/config/Provider.tsx @@ -4,6 +4,7 @@ import { createContext, createEffect, createSignal, + onCleanup, ParentComponent, useContext, } from "solid-js"; @@ -108,6 +109,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => { }); }); + onCleanup(() => update_unsub()); + let modify_unsub = () => {}; createEffect(() => { @@ -123,6 +126,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => { ); }); + onCleanup(() => modify_unsub()); + const userCanUpdate = () => user().admin || deployment.permissions![getId(user())] === PermissionLevel.Update; diff --git a/frontend/src/components/server/Updates.tsx b/frontend/src/components/server/Updates.tsx index 1b8e17a80..ec5067f59 100644 --- a/frontend/src/components/server/Updates.tsx +++ b/frontend/src/components/server/Updates.tsx @@ -2,6 +2,7 @@ import { Component, createEffect, For, + onCleanup, Show, } from "solid-js"; import { useUpdates } from "../../state/hooks"; @@ -24,6 +25,7 @@ const Updates: Component<{}> = (p) => { } }); }); + onCleanup(() => unsub()); return ( 0}> diff --git a/frontend/src/components/server/tabs/Owners.tsx b/frontend/src/components/server/tabs/Owners.tsx index 15b010865..b43eef01b 100644 --- a/frontend/src/components/server/tabs/Owners.tsx +++ b/frontend/src/components/server/tabs/Owners.tsx @@ -1,40 +1,75 @@ import { useParams } from "@solidjs/router"; -import { Component, createEffect, createSignal, For, Show } from "solid-js"; -import { client, pushNotification } from "../../.."; +import { + Component, + createEffect, + createMemo, + createSignal, + For, + onCleanup, + Show, +} from "solid-js"; +import { client } from "../../.."; +import { useAppState } from "../../../state/StateProvider"; import { useUser } from "../../../state/UserProvider"; -import { PermissionLevel, PermissionsTarget, User } from "../../../types"; +import { + Operation, + PermissionLevel, + PermissionsTarget, + User, +} from "../../../types"; import { combineClasses, getId } from "../../../util/helpers"; import ConfirmButton from "../../shared/ConfirmButton"; import Input from "../../shared/Input"; import Flex from "../../shared/layout/Flex"; import Grid from "../../shared/layout/Grid"; import Menu from "../../shared/menu/Menu"; +import Selector from "../../shared/menu/Selector"; import { useConfig } from "./config/Provider"; +const PERMISSIONS_OPTIONS = [ + PermissionLevel.Read, + PermissionLevel.Execute, + PermissionLevel.Update, +]; + const Owners: Component<{}> = (p) => { + const { ws } = useAppState(); const { server } = useConfig(); + const { user } = useUser(); const params = useParams(); const [userSearch, setUserSearch] = createSignal(""); const [users, setUsers] = createSignal([]); createEffect(() => { - if (userSearch().length > 0) { - // getUsers(userSearch(), true).then((users) => { - // setUsers( - // users.filter((user) => !server.owners.includes(user.username)) - // ); - // }); - } else { - setUsers([]); - } + client.list_users().then(setUsers); }); + const getUser = (user_id: string) => + users().find((u) => getId(u) === user_id)!; + const searchUsers = createMemo(() => + users().filter( + (u) => + !u.admin && + u.enabled && + u.username.includes(userSearch()) && + (server.permissions![getId(u)] === undefined || + server.permissions![getId(u)] === PermissionLevel.None) + ) + ); + let unsub = () => {}; + createEffect(() => { + unsub(); + unsub = ws.subscribe( + [Operation.ModifyUserPermissions, Operation.ModifyUserEnabled], + () => { + client.list_users().then(setUsers); + } + ); + }); + onCleanup(() => unsub()); return ( - + setUserSearch("")} @@ -48,7 +83,7 @@ const Owners: Component<{}> = (p) => { } content={ <> - + {(user) => ( = (p) => { width: "100%", "justify-content": "flex-start", }} - onConfirm={async () => { - await client.update_user_permissions_on_target({ user_id: getId(user), permission: PermissionLevel.Read, target_type: PermissionsTarget.Server, target_id: params.id }); - pushNotification("good", "owner added to server"); + onConfirm={() => { + client.update_user_permissions_on_target({ + user_id: getId(user), + permission: PermissionLevel.Read, + target_type: PermissionsTarget.Server, + target_id: params.id, + }); setUserSearch(""); }} confirm="add user" @@ -72,37 +111,55 @@ const Owners: Component<{}> = (p) => { } menuStyle={{ width: "12rem" }} /> - {/* - {(user_id) => ( - -
- {owner} - {owner === username() && " ( you )"} -
- 1}> - { - await removeOwnerFromServer(server._id!, owner); - pushNotification( - "good", - "user removed from collaborators" - ); - }} - > - remove - - -
- )} -
*/} + + {(user_id) => { + const u = () => getUser(user_id); + const permissions = () => server.permissions![user_id]; + return ( + +
+ {u().username} + {user_id === getId(user()) && " ( you )"} +
+ + { + client.update_user_permissions_on_target({ + user_id, + permission: permission as PermissionLevel, + target_type: PermissionsTarget.Server, + target_id: params.id, + }); + }} + position="bottom right" + /> + { + client.update_user_permissions_on_target({ + user_id, + permission: PermissionLevel.None, + target_type: PermissionsTarget.Server, + target_id: params.id, + }); + }} + > + remove + + +
+ ); + }} +
diff --git a/frontend/src/components/server/tabs/Tabs.tsx b/frontend/src/components/server/tabs/Tabs.tsx index 053c25507..c78396bef 100644 --- a/frontend/src/components/server/tabs/Tabs.tsx +++ b/frontend/src/components/server/tabs/Tabs.tsx @@ -2,8 +2,6 @@ import { useParams } from "@solidjs/router"; import { Component, Show } from "solid-js"; import { useAppState } from "../../../state/StateProvider"; import { useUser } from "../../../state/UserProvider"; -import { PermissionLevel } from "../../../types"; -import { getId } from "../../../util/helpers"; import Tabs, { Tab } from "../../shared/tabs/Tabs"; import Config from "./config/Config"; import { ConfigProvider } from "./config/Provider"; @@ -15,9 +13,6 @@ const ServerTabs: Component<{}> = (p) => { const params = useParams(); const { user } = useUser(); const server = () => servers.get(params.id)!; - const userCanUpdate = () => - user().admin || - server()!.server.permissions![getId(user())] === PermissionLevel.Update; return ( @@ -33,7 +28,7 @@ const ServerTabs: Component<{}> = (p) => { title: "stats", element: () => , }, - userCanUpdate() && { + user().admin && { title: "collaborators", element: () => , }, diff --git a/frontend/src/components/server/tabs/config/Provider.tsx b/frontend/src/components/server/tabs/config/Provider.tsx index 4883f6cf1..8649b231f 100644 --- a/frontend/src/components/server/tabs/config/Provider.tsx +++ b/frontend/src/components/server/tabs/config/Provider.tsx @@ -67,13 +67,16 @@ export const ConfigProvider: ParentComponent<{}> = (p) => { client.update_server(server); }; - onCleanup( - ws.subscribe([Operation.UpdateServer], (update) => { + let unsub = () => {} + createEffect(() => { + unsub(); + unsub = ws.subscribe([Operation.UpdateServer], (update) => { if (update.target.id === params.id) { load(); } - }) - ); + }); + }); + onCleanup(() => unsub()); // onCleanup( // ws.subscribe(