added some optimizations

This commit is contained in:
yoan 2024-08-23 11:33:43 +08:00
parent a5beb26d9d
commit f394e34861
13 changed files with 192 additions and 69 deletions

View File

@ -31,18 +31,23 @@ type Deployer interface {
Deploy(ctx context.Context) error Deploy(ctx context.Context) error
} }
func Get(record *models.Record) (Deployer, error) { func Get(record *models.Record, cert *applicant.Certificate) (Deployer, error) {
access := record.ExpandedOne("targetAccess") access := record.ExpandedOne("targetAccess")
option := &DeployerOption{ option := &DeployerOption{
DomainId: record.Id, DomainId: record.Id,
Domain: record.GetString("domain"), Domain: record.GetString("domain"),
Product: getProduct(record), Product: getProduct(record),
Access: access.GetString("config"), Access: access.GetString("config"),
Certificate: applicant.Certificate{ }
if cert != nil {
option.Certificate = *cert
} else {
option.Certificate = applicant.Certificate{
Certificate: record.GetString("certificate"), Certificate: record.GetString("certificate"),
PrivateKey: record.GetString("privateKey"), PrivateKey: record.GetString("privateKey"),
}, }
} }
switch record.GetString("targetType") { switch record.GetString("targetType") {
case targetAliyunOss: case targetAliyunOss:
return NewAliyun(option) return NewAliyun(option)

View File

@ -26,9 +26,11 @@ func deploy(ctx context.Context, record *models.Record) error {
app.GetApp().Logger().Error("部署失败", "err", r) app.GetApp().Logger().Error("部署失败", "err", r)
} }
}() }()
var certificate *applicant.Certificate
currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id) currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id)
history := NewHistory(record) history := NewHistory(record)
defer history.commit() defer history.commit()
// ############1.检查域名配置 // ############1.检查域名配置
history.record(checkPhase, "开始检查", nil) history.record(checkPhase, "开始检查", nil)
@ -73,25 +75,21 @@ func deploy(ctx context.Context, record *models.Record) error {
app.GetApp().Logger().Error("获取applicant失败", "err", err) app.GetApp().Logger().Error("获取applicant失败", "err", err)
return err return err
} }
certificate, err := applicant.Apply() certificate, err = applicant.Apply()
if err != nil { if err != nil {
history.record(applyPhase, "申请证书失败", err) history.record(applyPhase, "申请证书失败", err)
app.GetApp().Logger().Error("申请证书失败", "err", err) app.GetApp().Logger().Error("申请证书失败", "err", err)
return err return err
} }
history.record(applyPhase, "申请证书成功", nil) history.record(applyPhase, "申请证书成功", nil)
if err = saveCert(ctx, record, certificate); err != nil { history.setCert(certificate)
history.record(applyPhase, "保存证书失败", err)
app.GetApp().Logger().Error("保存证书失败", "err", err)
return err
}
} }
history.record(applyPhase, "保存证书成功", nil, true) history.record(applyPhase, "保存证书成功", nil, true)
// ############3.部署证书 // ############3.部署证书
history.record(deployPhase, "开始部署", nil, false) history.record(deployPhase, "开始部署", nil, false)
deployer, err := deployer.Get(currRecord) deployer, err := deployer.Get(currRecord, certificate)
if err != nil { if err != nil {
history.record(deployPhase, "获取deployer失败", err) history.record(deployPhase, "获取deployer失败", err)
app.GetApp().Logger().Error("获取deployer失败", "err", err) app.GetApp().Logger().Error("获取deployer失败", "err", err)
@ -99,38 +97,14 @@ func deploy(ctx context.Context, record *models.Record) error {
} }
if err = deployer.Deploy(ctx); err != nil { if err = deployer.Deploy(ctx); err != nil {
setDeployed(ctx, record, false)
app.GetApp().Logger().Error("部署失败", "err", err) app.GetApp().Logger().Error("部署失败", "err", err)
history.record(deployPhase, "部署失败", err) history.record(deployPhase, "部署失败", err)
return err return err
} }
setDeployed(ctx, record, true)
app.GetApp().Logger().Info("部署成功") app.GetApp().Logger().Info("部署成功")
history.record(deployPhase, "部署成功", nil, true) history.record(deployPhase, "部署成功", nil, true)
return nil return nil
} }
func setDeployed(ctx context.Context, record *models.Record, deployed bool) error {
record.Set("deployed", deployed)
if err := app.GetApp().Dao().SaveRecord(record); err != nil {
return err
}
return nil
}
func saveCert(ctx context.Context, record *models.Record, cert *applicant.Certificate) error {
record.Set("certUrl", cert.CertUrl)
record.Set("certStableUrl", cert.CertStableUrl)
record.Set("privateKey", cert.PrivateKey)
record.Set("certificate", cert.Certificate)
record.Set("issuerCertificate", cert.IssuerCertificate)
record.Set("csr", cert.Csr)
record.Set("expiredAt", time.Now().Add(time.Hour*24*90))
if err := app.GetApp().Dao().SaveRecord(record); err != nil {
return err
}
return nil
}

View File

@ -14,10 +14,12 @@ func create(ctx context.Context, record *models.Record) error {
} }
if record.GetBool("rightnow") { if record.GetBool("rightnow") {
go func() {
if err := deploy(ctx, record); err != nil {
app.GetApp().Logger().Error("deploy failed", "err", err)
}
}()
if err := deploy(ctx, record); err != nil {
return err
}
} }
scheduler := app.GetScheduler() scheduler := app.GetScheduler()
@ -45,9 +47,11 @@ func update(ctx context.Context, record *models.Record) error {
if record.GetBool("rightnow") { if record.GetBool("rightnow") {
if err := deploy(ctx, record); err != nil { go func() {
return err if err := deploy(ctx, record); err != nil {
} app.GetApp().Logger().Error("deploy failed", "err", err)
}
}()
} }
err := scheduler.Add(record.Id, record.GetString("crontab"), func() { err := scheduler.Add(record.Id, record.GetString("crontab"), func() {

View File

@ -1,6 +1,7 @@
package domains package domains
import ( import (
"certimate/internal/applicant"
"certimate/internal/utils/app" "certimate/internal/utils/app"
"certimate/internal/utils/xtime" "certimate/internal/utils/xtime"
"time" "time"
@ -20,6 +21,7 @@ type history struct {
Phase Phase `json:"phase"` Phase Phase `json:"phase"`
PhaseSuccess bool `json:"phaseSuccess"` PhaseSuccess bool `json:"phaseSuccess"`
DeployedAt string `json:"deployedAt"` DeployedAt string `json:"deployedAt"`
Cert *applicant.Certificate `json:"cert"`
} }
func NewHistory(record *models.Record) *history { func NewHistory(record *models.Record) *history {
@ -52,6 +54,10 @@ func (a *history) record(phase Phase, msg string, err error, pass ...bool) {
} }
func (a *history) setCert(cert *applicant.Certificate) {
a.Cert = cert
}
func (a *history) commit() error { func (a *history) commit() error {
collection, err := app.GetApp().Dao().FindCollectionByNameOrId("deployments") collection, err := app.GetApp().Dao().FindCollectionByNameOrId("deployments")
if err != nil { if err != nil {
@ -78,6 +84,19 @@ func (a *history) commit() error {
domainRecord.Set("lastDeployedAt", a.DeployedAt) domainRecord.Set("lastDeployedAt", a.DeployedAt)
domainRecord.Set("lastDeployment", record.Id) domainRecord.Set("lastDeployment", record.Id)
domainRecord.Set("rightnow", false) domainRecord.Set("rightnow", false)
if a.Phase == deployPhase && a.PhaseSuccess {
domainRecord.Set("deployed", true)
}
cert := a.Cert
if cert != nil {
domainRecord.Set("certUrl", cert.CertUrl)
domainRecord.Set("certStableUrl", cert.CertStableUrl)
domainRecord.Set("privateKey", cert.PrivateKey)
domainRecord.Set("certificate", cert.Certificate)
domainRecord.Set("issuerCertificate", cert.IssuerCertificate)
domainRecord.Set("csr", cert.Csr)
domainRecord.Set("expiredAt", time.Now().Add(time.Hour*24*90))
}
if err := app.GetApp().Dao().SaveRecord(domainRecord); err != nil { if err := app.GetApp().Dao().SaveRecord(domainRecord); err != nil {
return err return err

File diff suppressed because one or more lines are too long

2
ui/dist/index.html vendored
View File

@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title> <title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-3QHTbODC.js"></script> <script type="module" crossorigin src="/assets/index-BI5-7Lpm.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-127rBbJX.css"> <link rel="stylesheet" crossorigin href="/assets/index-127rBbJX.css">
</head> </head>
<body class="bg-background"> <body class="bg-background">

31
ui/package-lock.json generated
View File

@ -15,6 +15,7 @@
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-radio-group": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1", "@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
@ -1621,6 +1622,36 @@
} }
} }
}, },
"node_modules/@radix-ui/react-scroll-area": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.1.0.tgz",
"integrity": "sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==",
"dependencies": {
"@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select": { "node_modules/@radix-ui/react-select": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-select/-/react-select-2.1.1.tgz", "resolved": "https://registry.npmmirror.com/@radix-ui/react-select/-/react-select-2.1.1.tgz",

View File

@ -17,6 +17,7 @@
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-radio-group": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1", "@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",

View File

@ -0,0 +1,46 @@
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }

View File

@ -169,8 +169,20 @@ export default function Dashboard() {
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</header> </header>
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6"> <main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6 relative">
<Outlet /> <Outlet />
<div className="fixed right-0 bottom-0 w-full flex justify-between p-5">
<div className=""></div>
<div className="text-muted-foreground text-sm hover:text-stone-900">
<a
href="https://github.com/usual2970/certimate/releases"
target="_blank"
>
Certimate v0.0.2
</a>
</div>
</div>
</main> </main>
</div> </div>
</div> </div>

View File

@ -20,7 +20,13 @@ import { Tooltip, TooltipTrigger } from "@/components/ui/tooltip";
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import { Domain } from "@/domain/domain"; import { Domain } from "@/domain/domain";
import { convertZulu2Beijing, getDate } from "@/lib/time"; import { convertZulu2Beijing, getDate } from "@/lib/time";
import { list, remove, save } from "@/repository/domains"; import {
list,
remove,
save,
subscribeId,
unsubscribeId,
} from "@/repository/domains";
import { TooltipContent, TooltipProvider } from "@radix-ui/react-tooltip"; import { TooltipContent, TooltipProvider } from "@radix-ui/react-tooltip";
import { CircleCheck, CircleX, Earth } from "lucide-react"; import { CircleCheck, CircleX, Earth } from "lucide-react";
@ -82,22 +88,25 @@ const Home = () => {
const handleRightNowClick = async (domain: Domain) => { const handleRightNowClick = async (domain: Domain) => {
try { try {
unsubscribeId(domain.id);
subscribeId(domain.id, (resp) => {
console.log(resp);
const updatedDomains = domains.map((domain) => {
if (domain.id === resp.id) {
return { ...resp };
}
return domain;
});
setDomains(updatedDomains);
});
domain.rightnow = true; domain.rightnow = true;
const resp = await save(domain);
const updatedDomains = domains.map((domain) => { await save(domain);
if (domain.id === resp.id) {
return { ...resp };
}
return domain;
});
setDomains(updatedDomains);
toast.toast({ toast.toast({
title: "执行成功", title: "操作成功",
description: "执行成功", description: "已发起部署,请稍后查看部署日志。",
}); });
setTimeout(() => {
navigate(0);
}, 1000);
} catch (e) { } catch (e) {
toast.toast({ toast.toast({
title: "执行失败", title: "执行失败",

View File

@ -1,6 +1,7 @@
import DeployProgress from "@/components/certimate/DeployProgress"; import DeployProgress from "@/components/certimate/DeployProgress";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { import {
Sheet, Sheet,
@ -35,7 +36,7 @@ const History = () => {
}, [domain]); }, [domain]);
return ( return (
<div> <ScrollArea className="h-[80vh] overflow-hidden">
<div className="text-muted-foreground"></div> <div className="text-muted-foreground"></div>
{!deployments?.length ? ( {!deployments?.length ? (
<> <>
@ -184,7 +185,7 @@ const History = () => {
))} ))}
</> </>
)} )}
</div> </ScrollArea>
); );
}; };

View File

@ -25,3 +25,24 @@ export const save = async (data: Domain) => {
export const remove = async (id: string) => { export const remove = async (id: string) => {
return await getPb().collection("domains").delete(id); return await getPb().collection("domains").delete(id);
}; };
type Callback = (data: Domain) => void;
export const subscribeId = (id: string, callback: Callback) => {
return getPb()
.collection("domains")
.subscribe<Domain>(
id,
(e) => {
if (e.action === "update") {
callback(e.record);
}
},
{
expand: "lastDeployment",
}
);
};
export const unsubscribeId = (id: string) => {
getPb().collection("domains").unsubscribe(id);
};