diff --git a/backend/internal/api/handler/certificates.go b/backend/internal/api/handler/certificates.go
index 44f5f3d0..cc4580b2 100644
--- a/backend/internal/api/handler/certificates.go
+++ b/backend/internal/api/handler/certificates.go
@@ -26,7 +26,7 @@ func GetCertificates() func(http.ResponseWriter, *http.Request) {
return
}
- certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r))
+ certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r), getExpandFromContext(r))
if err != nil {
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
} else {
@@ -46,11 +46,16 @@ func GetCertificate() func(http.ResponseWriter, *http.Request) {
return
}
- cert, err := certificate.GetByID(certificateID)
- if err != nil {
+ item, err := certificate.GetByID(certificateID)
+ switch err {
+ case sql.ErrNoRows:
+ h.ResultErrorJSON(w, r, http.StatusNotFound, "Not found", nil)
+ case nil:
+ // nolint: errcheck,gosec
+ item.Expand(getExpandFromContext(r))
+ h.ResultResponseJSON(w, r, http.StatusOK, item)
+ default:
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
- } else {
- h.ResultResponseJSON(w, r, http.StatusOK, cert)
}
}
}
diff --git a/backend/internal/entity/certificate/methods.go b/backend/internal/entity/certificate/methods.go
index b0204699..23d15f02 100644
--- a/backend/internal/entity/certificate/methods.go
+++ b/backend/internal/entity/certificate/methods.go
@@ -102,7 +102,7 @@ func Update(certificate *Model) error {
}
// List will return a list of certificates
-func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
+func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ListResponse, error) {
var result ListResponse
var exampleModel Model
@@ -135,6 +135,15 @@ func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error)
return result, err
}
+ if expand != nil {
+ for idx := range items {
+ expandErr := items[idx].Expand(expand)
+ if expandErr != nil {
+ logger.Error("CertificatesExpansionError", expandErr)
+ }
+ }
+ }
+
result = ListResponse{
Items: items,
Total: totalRows,
diff --git a/backend/internal/entity/certificate/model.go b/backend/internal/entity/certificate/model.go
index 7dec8f2e..0e39191d 100644
--- a/backend/internal/entity/certificate/model.go
+++ b/backend/internal/entity/certificate/model.go
@@ -13,8 +13,10 @@ import (
"npm/internal/database"
"npm/internal/entity/certificateauthority"
"npm/internal/entity/dnsprovider"
+ "npm/internal/entity/user"
"npm/internal/logger"
"npm/internal/types"
+ "npm/internal/util"
)
const (
@@ -59,6 +61,7 @@ type Model struct {
// Expansions:
CertificateAuthority *certificateauthority.Model `json:"certificate_authority,omitempty"`
DNSProvider *dnsprovider.Model `json:"dns_provider,omitempty"`
+ User *user.Model `json:"user,omitempty"`
}
func (m *Model) getByQuery(query string, params []interface{}) error {
@@ -161,7 +164,8 @@ func (m *Model) ValidateWildcardSupport() bool {
}
if hasWildcard {
- m.Expand()
+ // nolint: errcheck, gosec
+ m.Expand([]string{"certificate-authority", "dns-provider"})
if !m.CertificateAuthority.IsWildcardSupported {
return false
}
@@ -182,15 +186,28 @@ func (m *Model) setDefaultStatus() {
}
// Expand will populate attached objects for the model
-func (m *Model) Expand() {
- if m.CertificateAuthorityID > 0 {
- certificateAuthority, _ := certificateauthority.GetByID(m.CertificateAuthorityID)
+func (m *Model) Expand(items []string) error {
+ var err error
+
+ if util.SliceContainsItem(items, "certificate-authority") && m.CertificateAuthorityID > 0 {
+ var certificateAuthority certificateauthority.Model
+ certificateAuthority, err = certificateauthority.GetByID(m.CertificateAuthorityID)
m.CertificateAuthority = &certificateAuthority
}
- if m.DNSProviderID > 0 {
- dnsProvider, _ := dnsprovider.GetByID(m.DNSProviderID)
+
+ if util.SliceContainsItem(items, "dns-provider") && m.DNSProviderID > 0 {
+ var dnsProvider dnsprovider.Model
+ dnsProvider, err = dnsprovider.GetByID(m.DNSProviderID)
m.DNSProvider = &dnsProvider
}
+
+ if util.SliceContainsItem(items, "user") && m.ID > 0 {
+ var usr user.Model
+ usr, err = user.GetByID(m.UserID)
+ m.User = &usr
+ }
+
+ return err
}
// GetCertificateLocations will return the paths on disk where the SSL
@@ -222,7 +239,8 @@ func (m *Model) GetCertificateLocations() (string, string, string) {
func (m *Model) Request() error {
logger.Info("Requesting certificate for: #%d %v", m.ID, m.Name)
- m.Expand()
+ // nolint: errcheck, gosec
+ m.Expand([]string{"certificate-authority", "dns-provider"})
m.Status = StatusRequesting
if err := m.Save(); err != nil {
logger.Error("CertificateSaveError", err)
diff --git a/frontend/src/api/npm/getCertificates.ts b/frontend/src/api/npm/getCertificates.ts
index ace316d8..c8df51f4 100644
--- a/frontend/src/api/npm/getCertificates.ts
+++ b/frontend/src/api/npm/getCertificates.ts
@@ -11,7 +11,7 @@ export async function getCertificates(
const { result } = await api.get(
{
url: "certificates",
- params: { limit, offset, sort, ...filters },
+ params: { limit, offset, sort, expand: "user", ...filters },
},
abortController,
);
diff --git a/frontend/src/components/Monospace.tsx b/frontend/src/components/Monospace.tsx
new file mode 100644
index 00000000..0f9dffcd
--- /dev/null
+++ b/frontend/src/components/Monospace.tsx
@@ -0,0 +1,11 @@
+import { Text, TextProps } from "@chakra-ui/react";
+
+function Monospace(props: TextProps) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+export { Monospace };
diff --git a/frontend/src/components/Table/Formatters.tsx b/frontend/src/components/Table/Formatters.tsx
index 554950c5..4fe988dd 100644
--- a/frontend/src/components/Table/Formatters.tsx
+++ b/frontend/src/components/Table/Formatters.tsx
@@ -1,5 +1,5 @@
import { Avatar, Badge, Text, Tooltip } from "@chakra-ui/react";
-import { RowAction, RowActionsMenu } from "components";
+import { Monospace, RowAction, RowActionsMenu } from "components";
import { intl } from "locale";
import getNiceDNSProvider from "modules/Acmesh";
@@ -73,13 +73,19 @@ function CapabilitiesFormatter() {
function CertificateStatusFormatter() {
const formatCell = ({ value }: any) => {
- return (
-
- {value
- ? intl.formatMessage({ id: "ready" })
- : intl.formatMessage({ id: "setup-required" })}
-
- );
+ let color = "cyan.500";
+ switch (value) {
+ case "failed":
+ color = "red.400";
+ break;
+ case "provided":
+ color = "green.400";
+ break;
+ case "requesting":
+ color = "yellow.400";
+ break;
+ }
+ return {intl.formatMessage({ id: value })};
};
return formatCell;
@@ -185,6 +191,14 @@ function HostStatusFormatter() {
return formatCell;
}
+function MonospaceFormatter() {
+ const formatCell = ({ value }: any) => {
+ return {value};
+ };
+
+ return formatCell;
+}
+
function UpstreamStatusFormatter() {
const formatCell = ({ value, row }: any) => {
if (value === "ready") {
@@ -245,6 +259,7 @@ export {
HostStatusFormatter,
HostTypeFormatter,
IDFormatter,
+ MonospaceFormatter,
SecondsFormatter,
UpstreamStatusFormatter,
};
diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts
index a4b99784..47caaa09 100644
--- a/frontend/src/components/index.ts
+++ b/frontend/src/components/index.ts
@@ -5,6 +5,7 @@ export * from "./HelpDrawer";
export * from "./Loader";
export * from "./Loading";
export * from "./LocalePicker";
+export * from "./Monospace";
export * from "./Navigation";
export * from "./Permissions";
export * from "./PrettyButton";
diff --git a/frontend/src/index.scss b/frontend/src/index.scss
index 69ed5227..9d011adf 100644
--- a/frontend/src/index.scss
+++ b/frontend/src/index.scss
@@ -25,6 +25,10 @@ table td.w-80 {
width: 80px;
}
+span.monospace {
+ font-family: monospace;
+}
+
/* helpdoc */
.helpdoc-body {
p {
diff --git a/frontend/src/pages/Certificates/CertificatesTable.tsx b/frontend/src/pages/Certificates/CertificatesTable.tsx
index 0af0aa05..c983d4b0 100644
--- a/frontend/src/pages/Certificates/CertificatesTable.tsx
+++ b/frontend/src/pages/Certificates/CertificatesTable.tsx
@@ -3,7 +3,10 @@ import { useEffect, useMemo } from "react";
import {
tableEvents,
ActionsFormatter,
+ CertificateStatusFormatter,
+ GravatarFormatter,
IDFormatter,
+ MonospaceFormatter,
TableFilter,
TableLayout,
TablePagination,
@@ -41,6 +44,11 @@ function CertificatesTable({
}: CertificatesTableProps) {
const [columns, tableData] = useMemo(() => {
const columns = [
+ {
+ accessor: "user.gravatarUrl",
+ Cell: GravatarFormatter(),
+ className: "w-80",
+ },
{
Header: intl.formatMessage({ id: "column.id" }),
accessor: "id",
@@ -53,6 +61,7 @@ function CertificatesTable({
accessor: "name",
sortable: true,
Filter: TextFilter,
+ Cell: MonospaceFormatter(),
},
{
Header: intl.formatMessage({ id: "column.validation-type" }),
@@ -65,6 +74,7 @@ function CertificatesTable({
accessor: "status",
sortable: true,
Filter: TextFilter,
+ Cell: CertificateStatusFormatter(),
},
{
id: "actions",