Przeglądaj źródła

Various tweaks and backend improvements

Jamie Curnow 1 miesiąc temu
rodzic
commit
3b9beaeae5

+ 38 - 13
backend/internal/certificate.js

@@ -24,7 +24,7 @@ const certbotLogsDir = "/data/logs";
 const certbotWorkDir = "/tmp/letsencrypt-lib";
 
 const omissions = () => {
-	return ["is_deleted", "owner.is_deleted"];
+	return ["is_deleted", "owner.is_deleted", "meta.dns_provider_credentials"];
 };
 
 const internalCertificate = {
@@ -122,7 +122,7 @@ const internalCertificate = {
 		}
 
 		// this command really should clean up and delete the cert if it can't fully succeed
-		const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions()));
+		const certificate = await certificateModel.query().insertAndFetch(data);
 
 		try {
 			if (certificate.provider === "letsencrypt") {
@@ -202,6 +202,9 @@ const internalCertificate = {
 					savedRow.meta = _.assign({}, savedRow.meta, {
 						letsencrypt_certificate: certInfo,
 					});
+
+					await internalCertificate.addCreatedAuditLog(access, certificate.id, savedRow);
+
 					return savedRow;
 				} catch (err) {
 					// Delete the certificate from the database if it was not created successfully
@@ -218,14 +221,18 @@ const internalCertificate = {
 		data.meta = _.assign({}, data.meta || {}, certificate.meta);
 
 		// Add to audit log
+		await internalCertificate.addCreatedAuditLog(access, certificate.id, utils.omitRow(omissions())(data));
+
+		return utils.omitRow(omissions())(certificate);
+	},
+
+	addCreatedAuditLog: async (access, certificate_id, meta) => {
 		await internalAuditLog.add(access, {
 			action: "created",
 			object_type: "certificate",
-			object_id: certificate.id,
-			meta: data,
+			object_id: certificate_id,
+			meta: meta,
 		});
-
-		return certificate;
 	},
 
 	/**
@@ -285,10 +292,7 @@ const internalCertificate = {
 			.query()
 			.where("is_deleted", 0)
 			.andWhere("id", data.id)
-			.allowGraph("[owner]")
-			.allowGraph("[proxy_hosts]")
-			.allowGraph("[redirection_hosts]")
-			.allowGraph("[dead_hosts]")
+			.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
 			.first();
 
 		if (accessData.permission_visibility !== "all") {
@@ -305,7 +309,24 @@ const internalCertificate = {
 		}
 		// Custom omissions
 		if (typeof data.omit !== "undefined" && data.omit !== null) {
-			return _.omit(row, data.omit);
+			return _.omit(row, [...data.omit]);
+		}
+
+		return internalCertificate.cleanExpansions(row);
+	},
+
+	cleanExpansions: (row) => {
+		if (typeof row.proxy_hosts !== "undefined") {
+			row.proxy_hosts = utils.omitRows(["is_deleted"])(row.proxy_hosts);
+		}
+		if (typeof row.redirection_hosts !== "undefined") {
+			row.redirection_hosts = utils.omitRows(["is_deleted"])(row.redirection_hosts);
+		}
+		if (typeof row.dead_hosts !== "undefined") {
+			row.dead_hosts = utils.omitRows(["is_deleted"])(row.dead_hosts);
+		}
+		if (typeof row.streams !== "undefined") {
+			row.streams = utils.omitRows(["is_deleted"])(row.streams);
 		}
 		return row;
 	},
@@ -415,7 +436,7 @@ const internalCertificate = {
 			.query()
 			.where("is_deleted", 0)
 			.groupBy("id")
-			.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts]")
+			.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
 			.orderBy("nice_name", "ASC");
 
 		if (accessData.permission_visibility !== "all") {
@@ -433,7 +454,11 @@ const internalCertificate = {
 			query.withGraphFetched(`[${expand.join(", ")}]`);
 		}
 
-		return await query.then(utils.omitRows(omissions()));
+		const r = await query.then(utils.omitRows(omissions()));
+		for (let i = 0; i < r.length; i++) {
+			r[i] = internalCertificate.cleanExpansions(r[i]);
+		}
+		return r;
 	},
 
 	/**

+ 12 - 0
backend/models/certificate.js

@@ -8,6 +8,7 @@ import deadHostModel from "./dead_host.js";
 import now from "./now_helper.js";
 import proxyHostModel from "./proxy_host.js";
 import redirectionHostModel from "./redirection_host.js";
+import streamModel from "./stream.js";
 import userModel from "./user.js";
 
 Model.knex(db);
@@ -114,6 +115,17 @@ class Certificate extends Model {
 					qb.where("redirection_host.is_deleted", 0);
 				},
 			},
+			streams: {
+				relation: Model.HasManyRelation,
+				modelClass: streamModel,
+				join: {
+					from: "certificate.id",
+					to: "stream.certificate_id",
+				},
+				modify: (qb) => {
+					qb.where("stream.is_deleted", 0);
+				},
+			},
 		};
 	}
 }

+ 1 - 1
frontend/src/api/backend/expansions.ts

@@ -1,6 +1,6 @@
 export type AccessListExpansion = "owner" | "items" | "clients";
 export type AuditLogExpansion = "user";
-export type CertificateExpansion = "owner" | "proxy_hosts" | "redirection_hosts" | "dead_hosts";
+export type CertificateExpansion = "owner" | "proxy_hosts" | "redirection_hosts" | "dead_hosts" | "streams";
 export type HostExpansion = "owner" | "certificate";
 export type ProxyHostExpansion = "owner" | "access_list" | "certificate";
 export type UserExpansion = "permissions";

+ 26 - 3
frontend/src/components/Table/Formatter/CertificateInUseFormatter.tsx

@@ -1,6 +1,6 @@
 import OverlayTrigger from "react-bootstrap/OverlayTrigger";
 import Popover from "react-bootstrap/Popover";
-import type { DeadHost, ProxyHost, RedirectionHost } from "src/api/backend";
+import type { DeadHost, ProxyHost, RedirectionHost, Stream } from "src/api/backend";
 import { T } from "src/locale";
 
 const getSection = (title: string, items: ProxyHost[] | RedirectionHost[] | DeadHost[]) => {
@@ -23,13 +23,34 @@ const getSection = (title: string, items: ProxyHost[] | RedirectionHost[] | Dead
 	);
 };
 
+const getSectionStream = (items: Stream[]) => {
+	if (items.length === 0) {
+		return null;
+	}
+	return (
+		<>
+			<div>
+				<strong>
+					<T id="streams" />
+				</strong>
+			</div>
+			{items.map((stream) => (
+				<div key={stream.id} className="ms-1">
+					{stream.forwardingHost}:{stream.forwardingPort}
+				</div>
+			))}
+		</>
+	);
+};
+
 interface Props {
 	proxyHosts: ProxyHost[];
 	redirectionHosts: RedirectionHost[];
 	deadHosts: DeadHost[];
+	streams: Stream[];
 }
-export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHosts }: Props) {
-	const totalCount = proxyHosts?.length + redirectionHosts?.length + deadHosts?.length;
+export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHosts, streams }: Props) {
+	const totalCount = proxyHosts?.length + redirectionHosts?.length + deadHosts?.length + streams?.length;
 	if (totalCount === 0) {
 		return (
 			<span className="badge bg-red-lt">
@@ -41,6 +62,7 @@ export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHo
 	proxyHosts.sort();
 	redirectionHosts.sort();
 	deadHosts.sort();
+	streams.sort();
 
 	const popover = (
 		<Popover id="popover-basic">
@@ -48,6 +70,7 @@ export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHo
 				{getSection("proxy-hosts", proxyHosts)}
 				{getSection("redirection-hosts", redirectionHosts)}
 				{getSection("dead-hosts", deadHosts)}
+				{getSectionStream(streams)}
 			</Popover.Body>
 		</Popover>
 	);

+ 2 - 1
frontend/src/components/Table/Formatter/EventFormatter.tsx

@@ -1,4 +1,5 @@
 import { IconArrowsCross, IconBolt, IconBoltOff, IconDisc, IconLock, IconShield, IconUser } from "@tabler/icons-react";
+import cn from "classnames";
 import type { AuditLog } from "src/api/backend";
 import { DateTimeFormat, T } from "src/locale";
 
@@ -32,7 +33,7 @@ const getColorForAction = (action: string) => {
 };
 
 const getIcon = (row: AuditLog) => {
-	const c = getColorForAction(row.action);
+	const c = cn(getColorForAction(row.action), "me-1");
 	let ico = null;
 	switch (row.objectType) {
 		case "user":

+ 1 - 0
frontend/src/hooks/useDeadHost.ts

@@ -52,6 +52,7 @@ const useSetDeadHost = () => {
 			queryClient.invalidateQueries({ queryKey: ["dead-hosts"] });
 			queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
 			queryClient.invalidateQueries({ queryKey: ["host-report"] });
+			queryClient.invalidateQueries({ queryKey: ["certificates"] });
 		},
 	});
 };

+ 1 - 0
frontend/src/hooks/useProxyHost.ts

@@ -59,6 +59,7 @@ const useSetProxyHost = () => {
 			queryClient.invalidateQueries({ queryKey: ["proxy-hosts"] });
 			queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
 			queryClient.invalidateQueries({ queryKey: ["host-report"] });
+			queryClient.invalidateQueries({ queryKey: ["certificates"] });
 		},
 	});
 };

+ 1 - 0
frontend/src/hooks/useRedirectionHost.ts

@@ -63,6 +63,7 @@ const useSetRedirectionHost = () => {
 			queryClient.invalidateQueries({ queryKey: ["redirection-hosts"] });
 			queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
 			queryClient.invalidateQueries({ queryKey: ["host-report"] });
+			queryClient.invalidateQueries({ queryKey: ["certificates"] });
 		},
 	});
 };

+ 1 - 0
frontend/src/hooks/useStream.ts

@@ -48,6 +48,7 @@ const useSetStream = () => {
 			queryClient.invalidateQueries({ queryKey: ["streams"] });
 			queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
 			queryClient.invalidateQueries({ queryKey: ["host-report"] });
+			queryClient.invalidateQueries({ queryKey: ["certificates"] });
 		},
 	});
 };

+ 1 - 0
frontend/src/pages/Certificates/Table.tsx

@@ -79,6 +79,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
 							proxyHosts={r.proxyHosts}
 							redirectionHosts={r.redirectionHosts}
 							deadHosts={r.deadHosts}
+							streams={r.streams}
 						/>
 					);
 				},

+ 1 - 0
frontend/src/pages/Certificates/TableWrapper.tsx

@@ -22,6 +22,7 @@ export default function TableWrapper() {
 		"dead_hosts",
 		"proxy_hosts",
 		"redirection_hosts",
+		"streams",
 	]);
 
 	if (isLoading) {

+ 0 - 1
frontend/src/pages/Dashboard/index.tsx

@@ -124,7 +124,6 @@ const Dashboard = () => {
 - check permissions in all places
 
 More for api, then implement here:
-- Properly implement refresh tokens
 - Add error message_18n for all backend errors
 - minor: certificates expand with hosts needs to omit 'is_deleted'
 - properly wrap all logger.debug called in isDebug check