diff --git a/core/src/plugins/auth/github.ts b/core/src/plugins/auth/github.ts
index 05e4acb70..26659056e 100644
--- a/core/src/plugins/auth/github.ts
+++ b/core/src/plugins/auth/github.ts
@@ -1,3 +1,4 @@
+import { USER_UPDATE } from "@monitor/util";
import axios from "axios";
import { FastifyInstance } from "fastify";
import fastifyOauth2, { OAuth2Namespace } from "fastify-oauth2";
@@ -43,6 +44,7 @@ const github = fp((app: FastifyInstance, _: {}, done: () => void) => {
);
} else {
const createdUser = await app.users.create(profile);
+ app.broadcast(USER_UPDATE, {});
const jwt = app.jwt.sign(
{ id: createdUser._id!.toString() },
{ expiresIn: TOKEN_EXPIRES_IN }
diff --git a/core/src/plugins/auth/local.ts b/core/src/plugins/auth/local.ts
index b623a9789..365fe3145 100644
--- a/core/src/plugins/auth/local.ts
+++ b/core/src/plugins/auth/local.ts
@@ -1,4 +1,5 @@
import { User } from "@monitor/types";
+import { USER_UPDATE } from "@monitor/util";
import { compare, hash } from "bcrypt";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import fp from "fastify-plugin";
@@ -18,6 +19,7 @@ const local = fp((app: FastifyInstance, _: {}, done: () => void) => {
username,
password: hashedPass,
});
+ app.broadcast(USER_UPDATE, {});
const jwt = app.jwt.sign(
{ id: user._id!.toString() },
{ expiresIn: TOKEN_EXPIRES_IN }
diff --git a/frontend/src/components/deployment/tabs/config/DockerAccount.tsx b/frontend/src/components/deployment/tabs/config/DockerAccount.tsx
index 45b60c2ec..126531acd 100644
--- a/frontend/src/components/deployment/tabs/config/DockerAccount.tsx
+++ b/frontend/src/components/deployment/tabs/config/DockerAccount.tsx
@@ -6,12 +6,13 @@ import { useConfig } from "./Provider";
const DockerAccount: Component<{}> = (p) => {
const { dockerAccounts } = useAppState();
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
return (
0}>
docker account
= (p) => {
)
}
position="bottom right"
+ disabled={!userCanUpdate()}
/>
diff --git a/frontend/src/components/deployment/tabs/config/Env.tsx b/frontend/src/components/deployment/tabs/config/Env.tsx
index 88639c036..d0e8d552b 100644
--- a/frontend/src/components/deployment/tabs/config/Env.tsx
+++ b/frontend/src/components/deployment/tabs/config/Env.tsx
@@ -6,7 +6,7 @@ import Grid from "../../../util/layout/Grid";
import { useConfig } from "./Provider";
const Env: Component<{}> = (p) => {
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
const onAdd = () => {
setDeployment("environment", (environment: any) => [
...environment,
@@ -30,14 +30,19 @@ const Env: Component<{}> = (p) => {
>
none
-
+
+
+
{(_, index) => (
-
+
= (p) => {
value.toUpperCase().replaceAll(" ", "_")
)
}
+ disabled={!userCanUpdate()}
/>
{" : "}
= (p) => {
onEdit={(value) =>
setDeployment("environment", index(), "value", value)
}
+ disabled={!userCanUpdate()}
/>
-
+
+
+
)}
diff --git a/frontend/src/components/deployment/tabs/config/Image.tsx b/frontend/src/components/deployment/tabs/config/Image.tsx
index 45e212108..a73b2b2c2 100644
--- a/frontend/src/components/deployment/tabs/config/Image.tsx
+++ b/frontend/src/components/deployment/tabs/config/Image.tsx
@@ -1,34 +1,31 @@
import { Component, Show } from "solid-js";
import { useAppState } from "../../../../state/StateProvider";
-import { useToggle } from "../../../../util/hooks";
+import { useUser } from "../../../../state/UserProvider";
+// import { useToggle } from "../../../../util/hooks";
import Input from "../../../util/Input";
import Flex from "../../../util/layout/Flex";
import Selector from "../../../util/menu/Selector";
import { useConfig } from "./Provider";
const Image: Component<{}> = (p) => {
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
const { builds } = useAppState();
- const [show, toggle] = useToggle();
+ // const [show, toggle] = useToggle();
return (
-
+
{deployment.buildID ? "build" : "image"}
-
+
-
- setDeployment("image", value)}
- />
-
+ setDeployment("image", value)}
+ disabled={!userCanUpdate()}
+ />
-
+
= (p) => {
);
}}
position="bottom right"
+ disabled={!userCanUpdate()}
/>
diff --git a/frontend/src/components/deployment/tabs/config/Network.tsx b/frontend/src/components/deployment/tabs/config/Network.tsx
index b99bcd1ee..637660229 100644
--- a/frontend/src/components/deployment/tabs/config/Network.tsx
+++ b/frontend/src/components/deployment/tabs/config/Network.tsx
@@ -4,12 +4,9 @@ import Selector from "../../../util/menu/Selector";
import { useConfig } from "./Provider";
const Network: Component<{}> = (p) => {
- const { deployment, setDeployment, networks } = useConfig();
+ const { deployment, setDeployment, networks, userCanUpdate } = useConfig();
return (
-
+
network
= (p) => {
selected={deployment.network || "bridge"}
onSelect={(network) => setDeployment("network", network)}
position="bottom right"
+ disabled={!userCanUpdate()}
/>
);
diff --git a/frontend/src/components/deployment/tabs/config/Ports.tsx b/frontend/src/components/deployment/tabs/config/Ports.tsx
index 37526ce62..2ec5fd032 100644
--- a/frontend/src/components/deployment/tabs/config/Ports.tsx
+++ b/frontend/src/components/deployment/tabs/config/Ports.tsx
@@ -6,7 +6,7 @@ import Grid from "../../../util/layout/Grid";
import { useConfig } from "./Provider";
const Ports: Component<{}> = (p) => {
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
const onAdd = () => {
setDeployment("ports", (ports: any) => [
...ports,
@@ -24,14 +24,19 @@ const Ports: Component<{}> = (p) => {
none
-
+
+
+
{({ local, container }, index) => (
-
+
= (p) => {
onEdit={(value) =>
setDeployment("ports", index(), "local", value)
}
+ disabled={!userCanUpdate()}
/>
{" : "}
= (p) => {
onEdit={(value) =>
setDeployment("ports", index(), "container", value)
}
+ disabled={!userCanUpdate()}
/>
-
+
+
+
)}
diff --git a/frontend/src/components/deployment/tabs/config/Provider.tsx b/frontend/src/components/deployment/tabs/config/Provider.tsx
index eecb30115..a421c09e5 100644
--- a/frontend/src/components/deployment/tabs/config/Provider.tsx
+++ b/frontend/src/components/deployment/tabs/config/Provider.tsx
@@ -11,6 +11,7 @@ import {
import { createStore, DeepReadonly, SetStoreFunction } from "solid-js/store";
import { ADD_UPDATE, UPDATE_DEPLOYMENT } from "../../../../state/actions";
import { useAppState } from "../../../../state/StateProvider";
+import { useUser } from "../../../../state/UserProvider";
import { getDeployment, getNetworks } from "../../../../util/query";
type ConfigDeployment = Deployment & {
@@ -26,12 +27,14 @@ type State = {
reset: () => void;
save: () => void;
networks: Accessor;
+ userCanUpdate: () => boolean;
};
const context = createContext();
export const ConfigProvider: Component<{}> = (p) => {
const { ws, deployments, selected } = useAppState();
+ const { username, permissions } = useUser();
const [editing] = createSignal(false);
const [deployment, set] = createStore({
...deployments.get(selected.id())!,
@@ -89,6 +92,16 @@ export const ConfigProvider: Component<{}> = (p) => {
onCleanup(unsub);
+ const userCanUpdate = () => {
+ if (permissions() > 1) {
+ return true;
+ } else if (permissions() > 0 && deployment.owners.includes(username()!)) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+
const state = {
editing,
deployment,
@@ -96,6 +109,7 @@ export const ConfigProvider: Component<{}> = (p) => {
reset: load,
save,
networks,
+ userCanUpdate,
};
return {p.children};
diff --git a/frontend/src/components/deployment/tabs/config/Restart.tsx b/frontend/src/components/deployment/tabs/config/Restart.tsx
index 39e2bc18b..ce4af8813 100644
--- a/frontend/src/components/deployment/tabs/config/Restart.tsx
+++ b/frontend/src/components/deployment/tabs/config/Restart.tsx
@@ -11,11 +11,12 @@ const RESTART_MODES = [
];
const Restart: Component<{}> = (p) => {
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
return (
restart
= (p) => {
)
}
position="bottom right"
+ disabled={!userCanUpdate()}
/>
);
diff --git a/frontend/src/components/deployment/tabs/config/Volumes.tsx b/frontend/src/components/deployment/tabs/config/Volumes.tsx
index 2514c0638..9adcf9a6e 100644
--- a/frontend/src/components/deployment/tabs/config/Volumes.tsx
+++ b/frontend/src/components/deployment/tabs/config/Volumes.tsx
@@ -6,7 +6,7 @@ import Grid from "../../../util/layout/Grid";
import { useConfig } from "./Provider";
const Volumes: Component<{}> = (p) => {
- const { deployment, setDeployment } = useConfig();
+ const { deployment, setDeployment, userCanUpdate } = useConfig();
const onAdd = () => {
setDeployment("volumes", (volumes: any) => [
...volumes,
@@ -14,7 +14,9 @@ const Volumes: Component<{}> = (p) => {
]);
};
const onRemove = (index: number) => {
- setDeployment("volumes", (volumes) => volumes!.filter((_, i) => i !== index));
+ setDeployment("volumes", (volumes) =>
+ volumes!.filter((_, i) => i !== index)
+ );
};
return (
@@ -24,14 +26,19 @@ const Volumes: Component<{}> = (p) => {
none
-
+
+
+
{({ local, container }, index) => (
-
+
= (p) => {
onEdit={(value) =>
setDeployment("volumes", index(), "local", value)
}
+ disabled={!userCanUpdate()}
/>
{" : "}
= (p) => {
onEdit={(value) =>
setDeployment("volumes", index(), "container", value)
}
+ disabled={!userCanUpdate()}
/>
-
+
+
+
)}
diff --git a/frontend/src/components/login/NotActivated.tsx b/frontend/src/components/login/NotActivated.tsx
index 8dfaf6f7a..d2bcb5ad0 100644
--- a/frontend/src/components/login/NotActivated.tsx
+++ b/frontend/src/components/login/NotActivated.tsx
@@ -3,15 +3,19 @@ import s from "./login.module.scss";
import Grid from "../util/layout/Grid";
import Loading from "../util/loading/Loading";
import { useUser } from "../../state/UserProvider";
+import Flex from "../util/layout/Flex";
const NotActivated: Component<{}> = (p) => {
const { logout } = useUser();
return (
-
- account not activated
-
diff --git a/frontend/src/components/users/Users.tsx b/frontend/src/components/users/Users.tsx
index 892faf3cb..42fd3f4e6 100644
--- a/frontend/src/components/users/Users.tsx
+++ b/frontend/src/components/users/Users.tsx
@@ -10,7 +10,9 @@ import {
import { USER_UPDATE } from "../../state/actions";
import { useAppState } from "../../state/StateProvider";
import { combineClasses, readablePermissions } from "../../util/helpers";
-import { getUsers, updateUser } from "../../util/query";
+import { deleteUser, getUsers, updateUser } from "../../util/query";
+import ConfirmButton from "../util/ConfirmButton";
+import Icon from "../util/Icon";
import Input from "../util/Input";
import Flex from "../util/layout/Flex";
import Grid from "../util/layout/Grid";
@@ -66,6 +68,12 @@ const Users: Component<{}> = (p) => {
>
{user.enabled ? "enabled" : "disabled"}
+
deleteUser(user._id!)}
+ >
+
+
)}
diff --git a/frontend/src/components/util/Input.tsx b/frontend/src/components/util/Input.tsx
index 46999af04..2e9988b9c 100644
--- a/frontend/src/components/util/Input.tsx
+++ b/frontend/src/components/util/Input.tsx
@@ -1,23 +1,29 @@
-import { Component, JSX } from "solid-js";
+import { Component, JSX, Show } from "solid-js";
const Input: Component<
{
onEdit?: (value: string) => void;
onConfirm?: (value: string) => void;
onEnter?: (value: string) => void;
- } & JSX.InputHTMLAttributes
+ disabled?: boolean;
+ } & JSX.InputHTMLAttributes &
+ JSX.HTMLAttributes
> = (p) => {
return (
- p.onEdit && p.onEdit(e.currentTarget.value)}
- onBlur={(e) => p.onConfirm && p.onConfirm(e.currentTarget.value)}
- onKeyDown={(e) => {
- if (e.key === "Enter") {
- p.onEnter ? p.onEnter(e.currentTarget.value) : e.currentTarget.blur();
- }
- }}
- />
+ {p.value} }>
+ p.onEdit && p.onEdit(e.currentTarget.value)}
+ onBlur={(e) => p.onConfirm && p.onConfirm(e.currentTarget.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ p.onEnter
+ ? p.onEnter(e.currentTarget.value)
+ : e.currentTarget.blur();
+ }
+ }}
+ />
+
);
};
diff --git a/frontend/src/components/util/menu/Selector.tsx b/frontend/src/components/util/menu/Selector.tsx
index ec34e5725..8a99e15a7 100644
--- a/frontend/src/components/util/menu/Selector.tsx
+++ b/frontend/src/components/util/menu/Selector.tsx
@@ -1,4 +1,4 @@
-import { Component, For } from "solid-js";
+import { Component, For, JSX, Show } from "solid-js";
import { useToggle } from "../../../util/hooks";
import Icon from "../Icon";
import { Position } from "./helpers";
@@ -11,37 +11,49 @@ const Selector: Component<{
onSelect?: (item: string, index: number) => void;
position?: Position;
targetClass?: string;
+ disabled?: boolean;
+ disabledClass?: string;
+ disabledStyle?: JSX.CSSProperties;
}> = (p) => {
const [show, toggle] = useToggle();
return (
-