You've already forked nginx-proxy-manager
mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-11-01 05:31:05 +03:00
Various tweaks and backend improvements
This commit is contained in:
@@ -24,7 +24,7 @@ const certbotLogsDir = "/data/logs";
|
|||||||
const certbotWorkDir = "/tmp/letsencrypt-lib";
|
const certbotWorkDir = "/tmp/letsencrypt-lib";
|
||||||
|
|
||||||
const omissions = () => {
|
const omissions = () => {
|
||||||
return ["is_deleted", "owner.is_deleted"];
|
return ["is_deleted", "owner.is_deleted", "meta.dns_provider_credentials"];
|
||||||
};
|
};
|
||||||
|
|
||||||
const internalCertificate = {
|
const internalCertificate = {
|
||||||
@@ -122,7 +122,7 @@ const internalCertificate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this command really should clean up and delete the cert if it can't fully succeed
|
// 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 {
|
try {
|
||||||
if (certificate.provider === "letsencrypt") {
|
if (certificate.provider === "letsencrypt") {
|
||||||
@@ -202,6 +202,9 @@ const internalCertificate = {
|
|||||||
savedRow.meta = _.assign({}, savedRow.meta, {
|
savedRow.meta = _.assign({}, savedRow.meta, {
|
||||||
letsencrypt_certificate: certInfo,
|
letsencrypt_certificate: certInfo,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await internalCertificate.addCreatedAuditLog(access, certificate.id, savedRow);
|
||||||
|
|
||||||
return savedRow;
|
return savedRow;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Delete the certificate from the database if it was not created successfully
|
// 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);
|
data.meta = _.assign({}, data.meta || {}, certificate.meta);
|
||||||
|
|
||||||
// Add to audit log
|
// 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, {
|
await internalAuditLog.add(access, {
|
||||||
action: "created",
|
action: "created",
|
||||||
object_type: "certificate",
|
object_type: "certificate",
|
||||||
object_id: certificate.id,
|
object_id: certificate_id,
|
||||||
meta: data,
|
meta: meta,
|
||||||
});
|
});
|
||||||
|
|
||||||
return certificate;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,10 +292,7 @@ const internalCertificate = {
|
|||||||
.query()
|
.query()
|
||||||
.where("is_deleted", 0)
|
.where("is_deleted", 0)
|
||||||
.andWhere("id", data.id)
|
.andWhere("id", data.id)
|
||||||
.allowGraph("[owner]")
|
.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
|
||||||
.allowGraph("[proxy_hosts]")
|
|
||||||
.allowGraph("[redirection_hosts]")
|
|
||||||
.allowGraph("[dead_hosts]")
|
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (accessData.permission_visibility !== "all") {
|
if (accessData.permission_visibility !== "all") {
|
||||||
@@ -305,7 +309,24 @@ const internalCertificate = {
|
|||||||
}
|
}
|
||||||
// Custom omissions
|
// Custom omissions
|
||||||
if (typeof data.omit !== "undefined" && data.omit !== null) {
|
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;
|
return row;
|
||||||
},
|
},
|
||||||
@@ -415,7 +436,7 @@ const internalCertificate = {
|
|||||||
.query()
|
.query()
|
||||||
.where("is_deleted", 0)
|
.where("is_deleted", 0)
|
||||||
.groupBy("id")
|
.groupBy("id")
|
||||||
.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts]")
|
.allowGraph("[owner,proxy_hosts,redirection_hosts,dead_hosts,streams]")
|
||||||
.orderBy("nice_name", "ASC");
|
.orderBy("nice_name", "ASC");
|
||||||
|
|
||||||
if (accessData.permission_visibility !== "all") {
|
if (accessData.permission_visibility !== "all") {
|
||||||
@@ -433,7 +454,11 @@ const internalCertificate = {
|
|||||||
query.withGraphFetched(`[${expand.join(", ")}]`);
|
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;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import deadHostModel from "./dead_host.js";
|
|||||||
import now from "./now_helper.js";
|
import now from "./now_helper.js";
|
||||||
import proxyHostModel from "./proxy_host.js";
|
import proxyHostModel from "./proxy_host.js";
|
||||||
import redirectionHostModel from "./redirection_host.js";
|
import redirectionHostModel from "./redirection_host.js";
|
||||||
|
import streamModel from "./stream.js";
|
||||||
import userModel from "./user.js";
|
import userModel from "./user.js";
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
@@ -114,6 +115,17 @@ class Certificate extends Model {
|
|||||||
qb.where("redirection_host.is_deleted", 0);
|
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,6 +1,6 @@
|
|||||||
export type AccessListExpansion = "owner" | "items" | "clients";
|
export type AccessListExpansion = "owner" | "items" | "clients";
|
||||||
export type AuditLogExpansion = "user";
|
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 HostExpansion = "owner" | "certificate";
|
||||||
export type ProxyHostExpansion = "owner" | "access_list" | "certificate";
|
export type ProxyHostExpansion = "owner" | "access_list" | "certificate";
|
||||||
export type UserExpansion = "permissions";
|
export type UserExpansion = "permissions";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import OverlayTrigger from "react-bootstrap/OverlayTrigger";
|
import OverlayTrigger from "react-bootstrap/OverlayTrigger";
|
||||||
import Popover from "react-bootstrap/Popover";
|
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";
|
import { T } from "src/locale";
|
||||||
|
|
||||||
const getSection = (title: string, items: ProxyHost[] | RedirectionHost[] | DeadHost[]) => {
|
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 {
|
interface Props {
|
||||||
proxyHosts: ProxyHost[];
|
proxyHosts: ProxyHost[];
|
||||||
redirectionHosts: RedirectionHost[];
|
redirectionHosts: RedirectionHost[];
|
||||||
deadHosts: DeadHost[];
|
deadHosts: DeadHost[];
|
||||||
|
streams: Stream[];
|
||||||
}
|
}
|
||||||
export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHosts }: Props) {
|
export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHosts, streams }: Props) {
|
||||||
const totalCount = proxyHosts?.length + redirectionHosts?.length + deadHosts?.length;
|
const totalCount = proxyHosts?.length + redirectionHosts?.length + deadHosts?.length + streams?.length;
|
||||||
if (totalCount === 0) {
|
if (totalCount === 0) {
|
||||||
return (
|
return (
|
||||||
<span className="badge bg-red-lt">
|
<span className="badge bg-red-lt">
|
||||||
@@ -41,6 +62,7 @@ export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHo
|
|||||||
proxyHosts.sort();
|
proxyHosts.sort();
|
||||||
redirectionHosts.sort();
|
redirectionHosts.sort();
|
||||||
deadHosts.sort();
|
deadHosts.sort();
|
||||||
|
streams.sort();
|
||||||
|
|
||||||
const popover = (
|
const popover = (
|
||||||
<Popover id="popover-basic">
|
<Popover id="popover-basic">
|
||||||
@@ -48,6 +70,7 @@ export function CertificateInUseFormatter({ proxyHosts, redirectionHosts, deadHo
|
|||||||
{getSection("proxy-hosts", proxyHosts)}
|
{getSection("proxy-hosts", proxyHosts)}
|
||||||
{getSection("redirection-hosts", redirectionHosts)}
|
{getSection("redirection-hosts", redirectionHosts)}
|
||||||
{getSection("dead-hosts", deadHosts)}
|
{getSection("dead-hosts", deadHosts)}
|
||||||
|
{getSectionStream(streams)}
|
||||||
</Popover.Body>
|
</Popover.Body>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IconArrowsCross, IconBolt, IconBoltOff, IconDisc, IconLock, IconShield, IconUser } from "@tabler/icons-react";
|
import { IconArrowsCross, IconBolt, IconBoltOff, IconDisc, IconLock, IconShield, IconUser } from "@tabler/icons-react";
|
||||||
|
import cn from "classnames";
|
||||||
import type { AuditLog } from "src/api/backend";
|
import type { AuditLog } from "src/api/backend";
|
||||||
import { DateTimeFormat, T } from "src/locale";
|
import { DateTimeFormat, T } from "src/locale";
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ const getColorForAction = (action: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = (row: AuditLog) => {
|
const getIcon = (row: AuditLog) => {
|
||||||
const c = getColorForAction(row.action);
|
const c = cn(getColorForAction(row.action), "me-1");
|
||||||
let ico = null;
|
let ico = null;
|
||||||
switch (row.objectType) {
|
switch (row.objectType) {
|
||||||
case "user":
|
case "user":
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ const useSetDeadHost = () => {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["dead-hosts"] });
|
queryClient.invalidateQueries({ queryKey: ["dead-hosts"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["certificates"] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const useSetProxyHost = () => {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["proxy-hosts"] });
|
queryClient.invalidateQueries({ queryKey: ["proxy-hosts"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["certificates"] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const useSetRedirectionHost = () => {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["redirection-hosts"] });
|
queryClient.invalidateQueries({ queryKey: ["redirection-hosts"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["certificates"] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const useSetStream = () => {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["streams"] });
|
queryClient.invalidateQueries({ queryKey: ["streams"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
queryClient.invalidateQueries({ queryKey: ["audit-logs"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
queryClient.invalidateQueries({ queryKey: ["host-report"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["certificates"] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
|
|||||||
proxyHosts={r.proxyHosts}
|
proxyHosts={r.proxyHosts}
|
||||||
redirectionHosts={r.redirectionHosts}
|
redirectionHosts={r.redirectionHosts}
|
||||||
deadHosts={r.deadHosts}
|
deadHosts={r.deadHosts}
|
||||||
|
streams={r.streams}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default function TableWrapper() {
|
|||||||
"dead_hosts",
|
"dead_hosts",
|
||||||
"proxy_hosts",
|
"proxy_hosts",
|
||||||
"redirection_hosts",
|
"redirection_hosts",
|
||||||
|
"streams",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ const Dashboard = () => {
|
|||||||
- check permissions in all places
|
- check permissions in all places
|
||||||
|
|
||||||
More for api, then implement here:
|
More for api, then implement here:
|
||||||
- Properly implement refresh tokens
|
|
||||||
- Add error message_18n for all backend errors
|
- Add error message_18n for all backend errors
|
||||||
- minor: certificates expand with hosts needs to omit 'is_deleted'
|
- minor: certificates expand with hosts needs to omit 'is_deleted'
|
||||||
- properly wrap all logger.debug called in isDebug check
|
- properly wrap all logger.debug called in isDebug check
|
||||||
|
|||||||
Reference in New Issue
Block a user