diff --git a/bin/core/src/helpers/mod.rs b/bin/core/src/helpers/mod.rs index 98a621698..c6b40fb18 100644 --- a/bin/core/src/helpers/mod.rs +++ b/bin/core/src/helpers/mod.rs @@ -142,14 +142,17 @@ pub async fn get_tag_check_owner( async fn update_list_item( update: Update, ) -> anyhow::Result { - let username = + let username = if User::is_service_user(&update.operator) { + update.operator.clone() + } else { find_one_by_id(&db_client().await.users, &update.operator) .await .context("failed to query mongo for user")? .with_context(|| { format!("no user found with id {}", update.operator) })? - .username; + .username + }; let update = UpdateListItem { id: update.id, operation: update.operation, diff --git a/bin/core/src/helpers/resource.rs b/bin/core/src/helpers/resource.rs index a8041ee01..0c7daea4d 100644 --- a/bin/core/src/helpers/resource.rs +++ b/bin/core/src/helpers/resource.rs @@ -110,10 +110,13 @@ pub trait StateResource { permission_level: PermissionLevel, ) -> anyhow::Result> { let resource = Self::get_resource(id_or_name).await?; + if user.admin { + return Ok(resource); + } let permissions = Self::get_user_permission_on_resource(&user.id, &resource.id) .await?; - if user.admin || permissions >= permission_level { + if permissions >= permission_level { Ok(resource) } else { Err(anyhow!( diff --git a/client/core/rs/src/entities/user.rs b/client/core/rs/src/entities/user.rs index 5d67d4ecb..b2c68edec 100644 --- a/client/core/rs/src/entities/user.rs +++ b/client/core/rs/src/entities/user.rs @@ -62,6 +62,10 @@ impl User { self.config = UserConfig::default(); } } + + pub fn is_service_user(user_id: &str) -> bool { + matches!(user_id, "Procedure" | "Github") + } } pub fn admin_service_user(user_id: &str) -> Option { diff --git a/frontend/src/components/dashboard/api-keys.tsx b/frontend/src/components/dashboard/api-keys.tsx index c045d3462..5eba4bfe6 100644 --- a/frontend/src/components/dashboard/api-keys.tsx +++ b/frontend/src/components/dashboard/api-keys.tsx @@ -9,12 +9,14 @@ export const ApiKeysSummary = () => { return ( - -
- Api Keys - {keys_count} Total + +
+
+ Api Keys + {keys_count} Total +
+
-
diff --git a/frontend/src/components/dashboard/tags.tsx b/frontend/src/components/dashboard/tags.tsx index 92950baa9..8d9442f3e 100644 --- a/frontend/src/components/dashboard/tags.tsx +++ b/frontend/src/components/dashboard/tags.tsx @@ -9,12 +9,14 @@ export const TagsSummary = () => { return ( - -
- Tags - {tags_count} Total + +
+
+ Tags + {tags_count} Total +
+
-
diff --git a/frontend/src/components/resources/alerter/index.tsx b/frontend/src/components/resources/alerter/index.tsx index f41ab2a3c..1749c1a48 100644 --- a/frontend/src/components/resources/alerter/index.tsx +++ b/frontend/src/components/resources/alerter/index.tsx @@ -18,7 +18,7 @@ import { Link } from "react-router-dom"; import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card"; import { AlerterConfig, DeleteAlerter } from "./config"; import { CopyResource, ResourceLink } from "@components/util"; -import { TagsWithBadge } from "@components/tags"; +import { TagsWithBadge, useTagsFilter } from "@components/tags"; const useAlerter = (id?: string) => useRead("ListAlerters", {}).data?.find((d) => d.id === id); @@ -43,11 +43,20 @@ export const AlerterComponents: RequiredResourceComponents = { ), }, Actions: [], - Table: () => { + Table: ({ search }) => { + const tags = useTagsFilter(); const alerters = useRead("ListAlerters", {}).data; + const searchSplit = search?.split(" ") || []; return ( + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true + ) ?? [] + } columns={[ { header: "Name", @@ -76,12 +85,14 @@ export const AlerterComponents: RequiredResourceComponents = { return ( - -
- Alerters - {alerters_count} Total + +
+
+ Alerters + {alerters_count} Total +
+
-
diff --git a/frontend/src/components/resources/build/dashboard.tsx b/frontend/src/components/resources/build/dashboard.tsx index 882991d19..f1b61da66 100644 --- a/frontend/src/components/resources/build/dashboard.tsx +++ b/frontend/src/components/resources/build/dashboard.tsx @@ -21,7 +21,8 @@ export const BuildChart = () => { const container_ref = useRef(null); const line_ref = useRef(); const series_ref = useRef>(); - const { data } = useRead("GetBuildMonthlyStats", {}); + const build_stats = useRead("GetBuildMonthlyStats", {}).data; + const summary = useRead("GetBuildsSummary", {}).data; const handleResize = () => line_ref.current?.applyOptions({ @@ -29,7 +30,7 @@ export const BuildChart = () => { }); useEffect(() => { - if (!data) return; + if (!build_stats) return; if (line_ref.current) line_ref.current.remove(); const init = () => { if (!container_ref.current) return; @@ -52,9 +53,9 @@ export const BuildChart = () => { series_ref.current = line_ref.current.addHistogramSeries({ priceLineVisible: false, }); - const max = data.days.reduce((m, c) => Math.max(m, c.time), 0); + const max = build_stats.days.reduce((m, c) => Math.max(m, c.time), 0); series_ref.current.setData( - data.days.map((d) => ({ + build_stats.days.map((d) => ({ time: (d.ts / 1000) as Time, value: d.count, color: @@ -71,19 +72,22 @@ export const BuildChart = () => { return () => { window.removeEventListener("resize", handleResize); }; - }, [data]); + }, [build_stats]); return ( - -
- Builds - - {data?.total_time.toFixed(2)} Hours - + +
+
+ Builds + +
{summary?.total} Total
|{" "} +
{build_stats?.total_time.toFixed(2)} Hours
+
+
+
-
diff --git a/frontend/src/components/resources/build/table.tsx b/frontend/src/components/resources/build/table.tsx index 653f6cf51..fa0d3d499 100644 --- a/frontend/src/components/resources/build/table.tsx +++ b/frontend/src/components/resources/build/table.tsx @@ -4,14 +4,18 @@ import { DataTable } from "@ui/data-table"; import { fmt_date_with_minutes, fmt_version } from "@lib/formatting"; import { ResourceComponents } from ".."; -export const BuildTable = () => { +export const BuildTable = ({ search }: { search?: string }) => { const builds = useRead("ListBuilds", {}).data; const tags = useTagsFilter(); + const searchSplit = search?.split(" ") || []; return ( - tags.every((tag) => build.tags.includes(tag)) + builds?.filter((resource) => + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true ) ?? [] } columns={[ diff --git a/frontend/src/components/resources/builder/index.tsx b/frontend/src/components/resources/builder/index.tsx index c80535d5d..68f57e399 100644 --- a/frontend/src/components/resources/builder/index.tsx +++ b/frontend/src/components/resources/builder/index.tsx @@ -56,14 +56,18 @@ export const BuilderComponents: RequiredResourceComponents = { ), }, - Table: () => { + Table: ({ search }) => { const tags = useTagsFilter(); const builders = useRead("ListBuilders", {}).data; + const searchSplit = search?.split(" ") || []; return ( - tags.every((tag) => builder.tags.includes(tag)) + builders?.filter((resource) => + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true ) ?? [] } columns={[ @@ -142,12 +146,14 @@ export const BuilderComponents: RequiredResourceComponents = { return ( - -
- Builders - {builders_count} Total + +
+
+ Builders + {builders_count} Total +
+
-
diff --git a/frontend/src/components/resources/deployment/dashboard.tsx b/frontend/src/components/resources/deployment/dashboard.tsx index 49e6b1d16..ea3f9b769 100644 --- a/frontend/src/components/resources/deployment/dashboard.tsx +++ b/frontend/src/components/resources/deployment/dashboard.tsx @@ -21,12 +21,14 @@ export const DeploymentsChart = () => { return ( - -
- Deployments - {summary?.total} Total + +
+
+ Deployments + {summary?.total} Total +
+
-
diff --git a/frontend/src/components/resources/deployment/index.tsx b/frontend/src/components/resources/deployment/index.tsx index 7f0c7a109..ab3df96bc 100644 --- a/frontend/src/components/resources/deployment/index.tsx +++ b/frontend/src/components/resources/deployment/index.tsx @@ -108,9 +108,9 @@ export const DeploymentComponents: RequiredResourceComponents = { ); }, - Table: () => { + Table: ({ search }) => { const deployments = useRead("ListDeployments", {}).data; - return ; + return ; }, Dashboard: DeploymentsChart, }; diff --git a/frontend/src/components/resources/deployment/table.tsx b/frontend/src/components/resources/deployment/table.tsx index 2f6a4fcad..3012e1c9f 100644 --- a/frontend/src/components/resources/deployment/table.tsx +++ b/frontend/src/components/resources/deployment/table.tsx @@ -11,15 +11,20 @@ import { snake_case_to_upper_space_case } from "@lib/formatting"; export const DeploymentTable = ({ deployments, + search, }: { deployments: Types.DeploymentListItem[] | undefined; + search: string | undefined; }) => { const tags = useTagsFilter(); + const searchSplit = search?.split(" ") || []; return ( - tags.every((tag) => deployment.tags.includes(tag)) + deployments?.filter((resource) => + tags.every((tag) => resource.tags.includes(tag)) && searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true ) ?? [] } columns={[ @@ -47,7 +52,7 @@ export const DeploymentTable = ({ return undefined; } } else { - const [img, _] = image.split(":"); + const [img] = image.split(":"); return img; } }, diff --git a/frontend/src/components/resources/index.tsx b/frontend/src/components/resources/index.tsx index 8388c5689..b7c481dcb 100644 --- a/frontend/src/components/resources/index.tsx +++ b/frontend/src/components/resources/index.tsx @@ -10,8 +10,8 @@ import { ProcedureComponents } from "./procedure/index"; export const ResourceComponents: { [key in UsableResource]: RequiredResourceComponents; } = { - Deployment: DeploymentComponents, Server: ServerComponents, + Deployment: DeploymentComponents, Build: BuildComponents, Repo: RepoComponents, Procedure: ProcedureComponents, diff --git a/frontend/src/components/resources/procedure/index.tsx b/frontend/src/components/resources/procedure/index.tsx index e7253e93a..2177df7ff 100644 --- a/frontend/src/components/resources/procedure/index.tsx +++ b/frontend/src/components/resources/procedure/index.tsx @@ -87,7 +87,10 @@ export const ProcedureComponents: RequiredResourceComponents = {
Procedure Type - setType(type as "Sequence" | "Parallel")} + > @@ -108,12 +111,14 @@ export const ProcedureComponents: RequiredResourceComponents = { return ( - -
- Procedures - {procedure_count} Total + +
+
+ Procedures + {procedure_count} Total +
+
-
diff --git a/frontend/src/components/resources/procedure/table.tsx b/frontend/src/components/resources/procedure/table.tsx index 79698590d..26abc327a 100644 --- a/frontend/src/components/resources/procedure/table.tsx +++ b/frontend/src/components/resources/procedure/table.tsx @@ -1,13 +1,22 @@ import { useRead } from "@lib/hooks"; import { DataTable } from "@ui/data-table"; import { ProcedureComponents } from "."; -import { TagsWithBadge } from "@components/tags"; +import { TagsWithBadge, useTagsFilter } from "@components/tags"; -export const ProcedureTable = () => { +export const ProcedureTable = ({ search }: { search: string | undefined }) => { + const tags = useTagsFilter(); const procedures = useRead("ListProcedures", {}).data; + const searchSplit = search?.split(" ") || []; return ( + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true + ) ?? [] + } columns={[ { accessorKey: "id", diff --git a/frontend/src/components/resources/repo/index.tsx b/frontend/src/components/resources/repo/index.tsx index d2ace457b..0ae501d5f 100644 --- a/frontend/src/components/resources/repo/index.tsx +++ b/frontend/src/components/resources/repo/index.tsx @@ -1,4 +1,4 @@ -import { TagsWithBadge } from "@components/tags"; +import { TagsWithBadge, useTagsFilter } from "@components/tags"; import { useRead, useWrite } from "@lib/hooks"; import { RequiredResourceComponents } from "@types"; import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card"; @@ -35,11 +35,20 @@ export const RepoComponents: RequiredResourceComponents = { ), }, - Table: () => { + Table: ({ search }) => { + const tags = useTagsFilter(); const repos = useRead("ListRepos", {}).data; + const searchSplit = search?.split(" ") || []; return ( + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true + ) ?? [] + } columns={[ { accessorKey: "id", @@ -65,12 +74,14 @@ export const RepoComponents: RequiredResourceComponents = { return ( - -
- Repos - {repo_count} Total + +
+
+ Repos + {repo_count} Total +
+
-
diff --git a/frontend/src/components/resources/server/dashboard.tsx b/frontend/src/components/resources/server/dashboard.tsx index 2d519a581..4019ca470 100644 --- a/frontend/src/components/resources/server/dashboard.tsx +++ b/frontend/src/components/resources/server/dashboard.tsx @@ -20,12 +20,14 @@ export const ServersChart = () => { return ( - -
- Servers - {data?.total} Total + +
+
+ Servers + {data?.total} Total +
+
-
diff --git a/frontend/src/components/resources/server/table.tsx b/frontend/src/components/resources/server/table.tsx index 94a487f9e..b0d8d0992 100644 --- a/frontend/src/components/resources/server/table.tsx +++ b/frontend/src/components/resources/server/table.tsx @@ -4,15 +4,19 @@ import { DataTable } from "@ui/data-table"; import { ServerComponents } from "."; import { ResourceComponents } from ".."; -export const ServerTable = () => { +export const ServerTable = ({ search }: { search: string | undefined }) => { const servers = useRead("ListServers", {}).data; const tags = useTagsFilter(); + const searchSplit = search?.split(" ") || []; return ( nav(`/servers/${id}`)} data={ - servers?.filter((server) => - tags.every((tag) => server.tags.includes(tag)) + servers?.filter((resource) => + tags.every((tag) => resource.tags.includes(tag)) && + searchSplit.length > 0 + ? searchSplit.every((search) => resource.name.includes(search)) + : true ) ?? [] } columns={[ diff --git a/frontend/src/components/tags/index.tsx b/frontend/src/components/tags/index.tsx index 2a882cc0b..e2d768bb1 100644 --- a/frontend/src/components/tags/index.tsx +++ b/frontend/src/components/tags/index.tsx @@ -22,7 +22,7 @@ type TargetExcludingSystem = Exclude; const tagsAtom = atomWithStorage("tags-v0", []); export const useTagsFilter = () => { - const [tags, _] = useAtom(tagsAtom); + const [tags] = useAtom(tagsAtom); return tags; }; diff --git a/frontend/src/pages/home/all_resources.tsx b/frontend/src/pages/home/all_resources.tsx index 494da8bc7..11e2fd467 100644 --- a/frontend/src/pages/home/all_resources.tsx +++ b/frontend/src/pages/home/all_resources.tsx @@ -2,13 +2,12 @@ import { Page, Section } from "@components/layouts"; import { ResourceComponents } from "@components/resources"; import { TagsFilter, useTagsFilter } from "@components/tags"; import { useRead } from "@lib/hooks"; -import { UsableResource } from "@types"; +import { RequiredResourceComponents, UsableResource } from "@types"; import { Input } from "@ui/input"; import { useState } from "react"; export const AllResources = () => { const [search, setSearch] = useState(""); - const tags = useTagsFilter(); return ( {
} > - {Object.entries(ResourceComponents).map(([type, Components]) => { - const count = useRead( - `List${type as UsableResource}s`, - {} - ).data?.filter((resource) => - tags.every((tag) => resource.tags.includes(tag)) - ).length; - - if (!count) return; - - return ( -
}> - -
- ); - })} + {Object.entries(ResourceComponents).map(([type, Components]) => ( + + ))} ); }; + +const TableSection = ({ + type, + Components, + search, +}: { + type: string; + Components: RequiredResourceComponents; + search?: string; +}) => { + const tags = useTagsFilter(); + const count = useRead(`List${type as UsableResource}s`, {}).data?.filter( + (resource) => tags.every((tag) => resource.tags.includes(tag)) + ).length; + + if (!count) return; + + return ( +
}> + +
+ ); +}; diff --git a/frontend/src/pages/home/dashboard.tsx b/frontend/src/pages/home/dashboard.tsx index 389f8fdc2..52c244ae1 100644 --- a/frontend/src/pages/home/dashboard.tsx +++ b/frontend/src/pages/home/dashboard.tsx @@ -40,14 +40,16 @@ const Resources = () => (
- -
- Tree - - Visualize your servers / deployments - + +
+
+ Tree + + Visualize your servers / deployments + +
+
-
diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts index c6b6f3c3b..be8a7d74b 100644 --- a/frontend/src/types.d.ts +++ b/frontend/src/types.d.ts @@ -1,5 +1,4 @@ import { Types } from "@monitor/client"; -import { ReactNode } from "react"; export type UsableResource = Exclude; @@ -18,11 +17,11 @@ export interface RequiredResourceComponents { Description: IdComponent; Status: IdComponent; Link: IdComponent; - + Info: IdComponent[]; Actions: IdComponent[]; - Table: React.FC; + Table: React.FC<{ search?: string }>; Page: { [section: string]: IdComponent }; }