implement owners management

This commit is contained in:
mbecker20
2023-01-05 06:33:14 +00:00
parent 5628d92b3c
commit f2db575c70
13 changed files with 455 additions and 255 deletions
+1 -1
View File
@@ -31,8 +31,8 @@ const Build: Component<{}> = (p) => {
navigate("/");
}
});
onCleanup(unsub);
});
onCleanup(() => unsub);
return (
<Show when={build()} fallback={<NotFound type="build" />}>
<ActionStateProvider>
@@ -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 (
<Show
when={
+158 -99
View File
@@ -1,111 +1,170 @@
import { Component, createEffect, createSignal, For, Show } from "solid-js";
import { pushNotification } from "../../..";
import { useParams } from "@solidjs/router";
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 { User } from "../../../types";
import { combineClasses } from "../../../util/helpers";
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 "./Provider";
const PERMISSIONS_OPTIONS = [
PermissionLevel.Read,
PermissionLevel.Execute,
PermissionLevel.Update,
];
const Owners: Component<{}> = (p) => {
// const { build } = useConfig();
// const { user } = useUser();
// const [userSearch, setUserSearch] = createSignal("");
// const [users, setUsers] = createSignal<User[]>([]);
// createEffect(() => {
// if (userSearch().length > 0) {
// getUsers(userSearch(), true).then((users) => {
// setUsers(users.filter((user) => !build.owners.includes(user.username)));
// });
// } else {
// setUsers([]);
// }
// });
// return (
// <Show when={build.loaded}>
// <Grid class="config">
// <Grid class="config-items scroller" style={{ height: "100%" }}>
// <Grid
// class={combineClasses("config-item shadow", themeClass())}
// gap="0.5rem"
// >
// <Menu
// show={userSearch() ? true : false}
// close={() => setUserSearch("")}
// target={
// <Input
// placeholder="add user"
// value={userSearch()}
// onEdit={setUserSearch}
// style={{ width: "12rem" }}
// />
// }
// content={
// <>
// <For each={users()}>
// {(user) => (
// <ConfirmButton
// color="grey"
// style={{
// width: "100%",
// "justify-content": "flex-start",
// }}
// onConfirm={async () => {
// await addOwnerToBuild(build._id!, user.username);
// pushNotification("good", "owner added to build");
// setUserSearch("");
// }}
// confirm="add user"
// >
// {user.username}
// </ConfirmButton>
// )}
// </For>
// <Show when={users().length === 0}>no matching users</Show>
// </>
// }
// menuStyle={{ width: "12rem" }}
// />
// <For each={build.owners}>
// {(owner) => (
// <Flex
// alignItems="center"
// justifyContent="space-between"
// class={combineClasses("grey-no-hover", themeClass())}
// style={{
// padding: "0.5rem",
// }}
// >
// <div class="big-text">
// {owner}
// {owner === username() && " ( you )"}
// </div>
// <Show when={permissions() > 1}>
// <ConfirmButton
// color="red"
// onConfirm={async () => {
// await removeOwnerFromBuild(build._id!, owner);
// pushNotification(
// "good",
// "user removed from collaborators"
// );
// }}
// >
// remove
// </ConfirmButton>
// </Show>
// </Flex>
// )}
// </For>
// </Grid>
// </Grid>
// </Grid>
// </Show>
// );
return <div></div>
const { ws } = useAppState();
const { build } = useConfig();
const { user } = useUser();
const params = useParams();
const [userSearch, setUserSearch] = createSignal("");
const [users, setUsers] = createSignal<User[]>([]);
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 (
<Show when={build.loaded}>
<Grid class="config">
<Grid class="config-items scroller" style={{ height: "100%" }}>
<Grid class={combineClasses("config-item shadow")} gap="0.5rem">
<Menu
show={userSearch() ? true : false}
close={() => setUserSearch("")}
target={
<Input
placeholder="add user"
value={userSearch()}
onEdit={setUserSearch}
style={{ width: "12rem" }}
/>
}
content={
<>
<For each={searchUsers()}>
{(user) => (
<ConfirmButton
color="grey"
style={{
width: "100%",
"justify-content": "flex-start",
}}
onConfirm={() => {
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}
</ConfirmButton>
)}
</For>
<Show when={users().length === 0}>no matching users</Show>
</>
}
menuStyle={{ width: "12rem" }}
/>
<For each={Object.keys(build.permissions)}>
{(user_id) => {
const u = () => getUser(user_id);
const permissions = () => build.permissions[user_id];
return (
<Flex
alignItems="center"
justifyContent="space-between"
class={combineClasses("grey-no-hover")}
style={{
padding: "0.5rem",
}}
>
<div class="big-text">
{u().username}
{user_id === getId(user()) && " ( you )"}
</div>
<Flex alignItems="center">
<Selector
selected={permissions()}
items={PERMISSIONS_OPTIONS}
onSelect={(permission) => {
client.update_user_permissions_on_target({
user_id,
permission: permission as PermissionLevel,
target_type: PermissionsTarget.Build,
target_id: params.id,
});
}}
position="bottom right"
/>
<ConfirmButton
color="red"
onConfirm={() => {
client.update_user_permissions_on_target({
user_id,
permission: PermissionLevel.None,
target_type: PermissionsTarget.Build,
target_id: params.id,
});
}}
>
remove
</ConfirmButton>
</Flex>
</Flex>
);
}}
</For>
</Grid>
</Grid>
</Grid>
</Show>
);
};
export default Owners;
@@ -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 = {
+1 -1
View File
@@ -33,7 +33,7 @@ const BuildTabs: Component<{}> = (p) => {
title: "build",
element: () => <BuildConfig />,
},
userCanUpdate() && {
user().admin && {
title: "collaborators",
element: () => <Owners />,
},
@@ -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 (
<Show
when={
@@ -19,13 +19,15 @@ import { OnClone, OnPull } from "./mount-repo/OnGit";
import Loading from "../../../shared/loading/Loading";
import Owners from "./Owners";
import { pushNotification, URL } from "../../../..";
import { combineClasses, copyToClipboard } from "../../../../util/helpers";
import { combineClasses, copyToClipboard, getId } from "../../../../util/helpers";
import { useAppDimensions } from "../../../../state/DimensionProvider";
import { useUser } from "../../../../state/UserProvider";
const Config: Component<{}> = () => {
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 (
<Show when={deployment.loaded}>
<Grid class="config">
@@ -97,7 +99,7 @@ const Config: Component<{}> = () => {
</Grid>
),
},
userCanUpdate() && {
user().admin && {
title: "collaborators",
element: () => (
<Grid
@@ -1,101 +1,170 @@
import { Component, createEffect, createSignal, For, Show } from "solid-js";
import { pushNotification } from "../../../..";
import { useParams } from "@solidjs/router";
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 { User } from "../../../../types";
import { combineClasses } from "../../../../util/helpers";
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 "./Provider";
const PERMISSIONS_OPTIONS = [
PermissionLevel.Read,
PermissionLevel.Execute,
PermissionLevel.Update,
];
const Owners: Component<{}> = (p) => {
// const { deployment } = useConfig();
// const { user } = useUser();
// const [userSearch, setUserSearch] = createSignal("");
// const [users, setUsers] = createSignal<User[]>([]);
// createEffect(() => {
// if (userSearch().length > 0) {
// getUsers(userSearch(), true).then((users) => {
// setUsers(
// users.filter((user) => !deployment.owners.includes(user.username))
// );
// });
// } else {
// setUsers([]);
// }
// });
// return (
// <Grid
// class={combineClasses("config-item shadow", themeClass())}
// gap="0.5rem"
// >
// <Menu
// show={userSearch() ? true : false}
// close={() => setUserSearch("")}
// target={
// <Input
// placeholder="add user"
// value={userSearch()}
// onEdit={setUserSearch}
// style={{ width: "12rem" }}
// />
// }
// content={
// <>
// <For each={users()}>
// {(user) => (
// <ConfirmButton
// color="grey"
// style={{ width: "100%", "justify-content": "flex-start" }}
// onConfirm={async () => {
// await addOwnerToDeployment(deployment._id!, user.username);
// pushNotification("good", "owner added to deployment");
// setUserSearch("");
// }}
// confirm="add user"
// >
// {user.username}
// </ConfirmButton>
// )}
// </For>
// <Show when={users().length === 0}>no matching users</Show>
// </>
// }
// menuStyle={{ width: "12rem" }}
// />
// <For each={deployment.owners}>
// {(owner) => (
// <Flex
// alignItems="center"
// justifyContent="space-between"
// class={combineClasses("grey-no-hover", themeClass())}
// style={{
// padding: "0.5rem",
// }}
// >
// <div class="big-text">
// {owner}
// {owner === username() && " ( you )"}
// </div>
// <Show when={permissions() > 1}>
// <ConfirmButton
// color="red"
// onConfirm={async () => {
// await removeOwnerFromDeployment(deployment._id!, owner);
// pushNotification("good", "user removed from collaborators");
// }}
// >
// remove
// </ConfirmButton>
// </Show>
// </Flex>
// )}
// </For>
// </Grid>
// );
return <div></div>
const { ws } = useAppState();
const { deployment } = useConfig();
const { user } = useUser();
const params = useParams();
const [userSearch, setUserSearch] = createSignal("");
const [users, setUsers] = createSignal<User[]>([]);
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 (
<Show when={deployment.loaded}>
<Grid class="config">
<Grid class="config-items scroller" style={{ height: "100%" }}>
<Grid class={combineClasses("config-item shadow")} gap="0.5rem">
<Menu
show={userSearch() ? true : false}
close={() => setUserSearch("")}
target={
<Input
placeholder="add user"
value={userSearch()}
onEdit={setUserSearch}
style={{ width: "12rem" }}
/>
}
content={
<>
<For each={searchUsers()}>
{(user) => (
<ConfirmButton
color="grey"
style={{
width: "100%",
"justify-content": "flex-start",
}}
onConfirm={() => {
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}
</ConfirmButton>
)}
</For>
<Show when={users().length === 0}>no matching users</Show>
</>
}
menuStyle={{ width: "12rem" }}
/>
<For each={Object.keys(deployment.permissions!)}>
{(user_id) => {
const u = () => getUser(user_id);
const permissions = () => deployment.permissions![user_id];
return (
<Flex
alignItems="center"
justifyContent="space-between"
class={combineClasses("grey-no-hover")}
style={{
padding: "0.5rem",
}}
>
<div class="big-text">
{u().username}
{user_id === getId(user()) && " ( you )"}
</div>
<Flex alignItems="center">
<Selector
selected={permissions()}
items={PERMISSIONS_OPTIONS}
onSelect={(permission) => {
client.update_user_permissions_on_target({
user_id,
permission: permission as PermissionLevel,
target_type: PermissionsTarget.Deployment,
target_id: params.id,
});
}}
position="bottom right"
/>
<ConfirmButton
color="red"
onConfirm={() => {
client.update_user_permissions_on_target({
user_id,
permission: PermissionLevel.None,
target_type: PermissionsTarget.Deployment,
target_id: params.id,
});
}}
>
remove
</ConfirmButton>
</Flex>
</Flex>
);
}}
</For>
</Grid>
</Grid>
</Grid>
</Show>
);
};
export default Owners;
@@ -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;
@@ -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 (
<Show when={updates.loaded() && (updates.collection()?.length || 0) > 0}>
<Grid class={combineClasses("card shadow")}>
+108 -51
View File
@@ -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<User[]>([]);
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 (
<Show when={server.loaded}>
<Grid class="config">
<Grid class="config-items scroller" style={{ height: "100%" }}>
<Grid
class={combineClasses("config-item shadow")}
gap="0.5rem"
>
<Grid class={combineClasses("config-item shadow")} gap="0.5rem">
<Menu
show={userSearch() ? true : false}
close={() => setUserSearch("")}
@@ -48,7 +83,7 @@ const Owners: Component<{}> = (p) => {
}
content={
<>
<For each={users()}>
<For each={searchUsers()}>
{(user) => (
<ConfirmButton
color="grey"
@@ -56,9 +91,13 @@ const Owners: Component<{}> = (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" }}
/>
{/* <For each={Object.keys(server.permissions!)}>
{(user_id) => (
<Flex
alignItems="center"
justifyContent="space-between"
class={combineClasses("grey-no-hover")}
style={{
padding: "0.5rem",
}}
>
<div class="big-text">
{owner}
{owner === username() && " ( you )"}
</div>
<Show when={permissions() > 1}>
<ConfirmButton
color="red"
onConfirm={async () => {
await removeOwnerFromServer(server._id!, owner);
pushNotification(
"good",
"user removed from collaborators"
);
}}
>
remove
</ConfirmButton>
</Show>
</Flex>
)}
</For> */}
<For each={Object.keys(server.permissions!)}>
{(user_id) => {
const u = () => getUser(user_id);
const permissions = () => server.permissions![user_id];
return (
<Flex
alignItems="center"
justifyContent="space-between"
class={combineClasses("grey-no-hover")}
style={{
padding: "0.5rem",
}}
>
<div class="big-text">
{u().username}
{user_id === getId(user()) && " ( you )"}
</div>
<Flex alignItems="center">
<Selector
selected={permissions()}
items={PERMISSIONS_OPTIONS}
onSelect={(permission) => {
client.update_user_permissions_on_target({
user_id,
permission: permission as PermissionLevel,
target_type: PermissionsTarget.Server,
target_id: params.id,
});
}}
position="bottom right"
/>
<ConfirmButton
color="red"
onConfirm={() => {
client.update_user_permissions_on_target({
user_id,
permission: PermissionLevel.None,
target_type: PermissionsTarget.Server,
target_id: params.id,
});
}}
>
remove
</ConfirmButton>
</Flex>
</Flex>
);
}}
</For>
</Grid>
</Grid>
</Grid>
+1 -6
View File
@@ -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 (
<Show when={server()}>
<ConfigProvider>
@@ -33,7 +28,7 @@ const ServerTabs: Component<{}> = (p) => {
title: "stats",
element: () => <Stats />,
},
userCanUpdate() && {
user().admin && {
title: "collaborators",
element: () => <Owners />,
},
@@ -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(