mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
When adding a domain, you can also add a custom email address.
This commit is contained in:
parent
139a6980ac
commit
7550aec904
@ -23,6 +23,8 @@ const (
|
|||||||
configTypeGodaddy = "godaddy"
|
configTypeGodaddy = "godaddy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultEmail = "536464346@qq.com"
|
||||||
|
|
||||||
type Certificate struct {
|
type Certificate struct {
|
||||||
CertUrl string `json:"certUrl"`
|
CertUrl string `json:"certUrl"`
|
||||||
CertStableUrl string `json:"certStableUrl"`
|
CertStableUrl string `json:"certStableUrl"`
|
||||||
@ -60,8 +62,12 @@ type Applicant interface {
|
|||||||
|
|
||||||
func Get(record *models.Record) (Applicant, error) {
|
func Get(record *models.Record) (Applicant, error) {
|
||||||
access := record.ExpandedOne("access")
|
access := record.ExpandedOne("access")
|
||||||
|
email := record.GetString("email")
|
||||||
|
if email == "" {
|
||||||
|
email = defaultEmail
|
||||||
|
}
|
||||||
option := &ApplyOption{
|
option := &ApplyOption{
|
||||||
Email: "536464346@qq.com",
|
Email: email,
|
||||||
Domain: record.GetString("domain"),
|
Domain: record.GetString("domain"),
|
||||||
Access: access.GetString("config"),
|
Access: access.GetString("config"),
|
||||||
}
|
}
|
||||||
|
584
migrations/1726184067_collections_snapshot.go
Normal file
584
migrations/1726184067_collections_snapshot.go
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
jsonData := `[
|
||||||
|
{
|
||||||
|
"id": "z3p974ainxjqlvs",
|
||||||
|
"created": "2024-07-29 10:02:48.334Z",
|
||||||
|
"updated": "2024-09-12 23:13:12.119Z",
|
||||||
|
"name": "domains",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "iuaerpl2",
|
||||||
|
"name": "domain",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ukkhuw85",
|
||||||
|
"name": "email",
|
||||||
|
"type": "email",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"exceptDomains": null,
|
||||||
|
"onlyDomains": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "v98eebqq",
|
||||||
|
"name": "crontab",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "alc8e9ow",
|
||||||
|
"name": "access",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "4yzbv8urny5ja1e",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "topsc9bj",
|
||||||
|
"name": "certUrl",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "vixgq072",
|
||||||
|
"name": "certStableUrl",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "g3a3sza5",
|
||||||
|
"name": "privateKey",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "gr6iouny",
|
||||||
|
"name": "certificate",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "tk6vnrmn",
|
||||||
|
"name": "issuerCertificate",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "sjo6ibse",
|
||||||
|
"name": "csr",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "x03n1bkj",
|
||||||
|
"name": "expiredAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "srybpixz",
|
||||||
|
"name": "targetType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun-oss",
|
||||||
|
"aliyun-cdn",
|
||||||
|
"ssh",
|
||||||
|
"webhook",
|
||||||
|
"tencent-cdn",
|
||||||
|
"qiniu-cdn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "xy7yk0mb",
|
||||||
|
"name": "targetAccess",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "4yzbv8urny5ja1e",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "6jqeyggw",
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hdsjcchf",
|
||||||
|
"name": "deployed",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "aiya3rev",
|
||||||
|
"name": "rightnow",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ixznmhzc",
|
||||||
|
"name": "lastDeployedAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "ghtlkn5j",
|
||||||
|
"name": "lastDeployment",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "0a1o4e6sstp694f",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_4ABO6EQ` + "`" + ` ON ` + "`" + `domains` + "`" + ` (` + "`" + `domain` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4yzbv8urny5ja1e",
|
||||||
|
"created": "2024-07-29 10:04:39.685Z",
|
||||||
|
"updated": "2024-09-12 23:08:52.810Z",
|
||||||
|
"name": "access",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "geeur58v",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "iql7jpwx",
|
||||||
|
"name": "config",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hwy7m03o",
|
||||||
|
"name": "configType",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"aliyun",
|
||||||
|
"tencent",
|
||||||
|
"ssh",
|
||||||
|
"webhook",
|
||||||
|
"cloudflare",
|
||||||
|
"qiniu",
|
||||||
|
"namesilo",
|
||||||
|
"godaddy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "lr33hiwg",
|
||||||
|
"name": "deleted",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "hsxcnlvd",
|
||||||
|
"name": "usage",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"apply",
|
||||||
|
"deploy",
|
||||||
|
"all"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_wkoST0j` + "`" + ` ON ` + "`" + `access` + "`" + ` (` + "`" + `name` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0a1o4e6sstp694f",
|
||||||
|
"created": "2024-07-30 06:30:27.801Z",
|
||||||
|
"updated": "2024-09-12 23:08:52.810Z",
|
||||||
|
"name": "deployments",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "farvlzk7",
|
||||||
|
"name": "domain",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "z3p974ainxjqlvs",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "jx5f69i3",
|
||||||
|
"name": "log",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "qbxdtg9q",
|
||||||
|
"name": "phase",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"values": [
|
||||||
|
"check",
|
||||||
|
"apply",
|
||||||
|
"deploy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "rglrp1hz",
|
||||||
|
"name": "phaseSuccess",
|
||||||
|
"type": "bool",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "lt1g1blu",
|
||||||
|
"name": "deployedAt",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "_pb_users_auth_",
|
||||||
|
"created": "2024-09-12 13:09:54.234Z",
|
||||||
|
"updated": "2024-09-12 23:08:52.811Z",
|
||||||
|
"name": "users",
|
||||||
|
"type": "auth",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_avatar",
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "file",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"mimeTypes": [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/gif",
|
||||||
|
"image/webp"
|
||||||
|
],
|
||||||
|
"thumbs": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 5242880,
|
||||||
|
"protected": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "id = @request.auth.id",
|
||||||
|
"viewRule": "id = @request.auth.id",
|
||||||
|
"createRule": "",
|
||||||
|
"updateRule": "id = @request.auth.id",
|
||||||
|
"deleteRule": "id = @request.auth.id",
|
||||||
|
"options": {
|
||||||
|
"allowEmailAuth": true,
|
||||||
|
"allowOAuth2Auth": true,
|
||||||
|
"allowUsernameAuth": true,
|
||||||
|
"exceptEmailDomains": null,
|
||||||
|
"manageRule": null,
|
||||||
|
"minPasswordLength": 8,
|
||||||
|
"onlyEmailDomains": null,
|
||||||
|
"onlyVerified": false,
|
||||||
|
"requireEmail": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dy6ccjb60spfy6p",
|
||||||
|
"created": "2024-09-12 23:12:21.677Z",
|
||||||
|
"updated": "2024-09-12 23:19:09.110Z",
|
||||||
|
"name": "settings",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "1tcmdsdf",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "f9wyhypi",
|
||||||
|
"name": "content",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 2000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX ` + "`" + `idx_RO7X9Vw` + "`" + ` ON ` + "`" + `settings` + "`" + ` (` + "`" + `name` + "`" + `)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
|
||||||
|
collections := []*models.Collection{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &collections); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daos.New(db).ImportCollections(collections, true, nil)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
284
ui/dist/assets/index-CSdX68Da.js
vendored
Normal file
284
ui/dist/assets/index-CSdX68Da.js
vendored
Normal file
File diff suppressed because one or more lines are too long
284
ui/dist/assets/index-DTugtq1k.js
vendored
284
ui/dist/assets/index-DTugtq1k.js
vendored
File diff suppressed because one or more lines are too long
2
ui/dist/index.html
vendored
2
ui/dist/index.html
vendored
@ -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-DTugtq1k.js"></script>
|
<script type="module" crossorigin src="/assets/index-CSdX68Da.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Cg0yCJnh.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Cg0yCJnh.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-background">
|
<body class="bg-background">
|
||||||
|
141
ui/src/components/certimate/EmailsEdit.tsx
Normal file
141
ui/src/components/certimate/EmailsEdit.tsx
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../ui/dialog";
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "../ui/form";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { useConfig } from "@/providers/config";
|
||||||
|
import { update } from "@/repository/settings";
|
||||||
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
import { PbErrorData } from "@/domain/base";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
type EmailsEditProps = {
|
||||||
|
className?: string;
|
||||||
|
trigger: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||||
|
const {
|
||||||
|
config: { emails },
|
||||||
|
setEmails,
|
||||||
|
} = useConfig();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
email: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||||
|
if (emails.content.emails.includes(data.email)) {
|
||||||
|
form.setError("email", {
|
||||||
|
message: "邮箱已存在",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到 config
|
||||||
|
const newEmails = [...emails.content.emails, data.email];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await update({
|
||||||
|
...emails,
|
||||||
|
name: "emails",
|
||||||
|
content: {
|
||||||
|
emails: newEmails,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
setEmails(resp);
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
form.reset();
|
||||||
|
form.clearErrors();
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
} catch (e) {
|
||||||
|
const err = e as ClientResponseError;
|
||||||
|
|
||||||
|
Object.entries(err.response.data as PbErrorData).forEach(
|
||||||
|
([key, value]) => {
|
||||||
|
form.setError(key as keyof z.infer<typeof formSchema>, {
|
||||||
|
type: "manual",
|
||||||
|
message: value.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog onOpenChange={setOpen} open={open}>
|
||||||
|
<DialogTrigger asChild className={cn(className)}>
|
||||||
|
{trigger}
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[600px] w-full dark:text-stone-200">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>添加邮箱</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="container py-3">
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
console.log(e);
|
||||||
|
e.stopPropagation();
|
||||||
|
form.handleSubmit(onSubmit)(e);
|
||||||
|
}}
|
||||||
|
className="space-y-8"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>邮箱</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="请输入邮箱" {...field} type="email" />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit">保存</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmailsEdit;
|
@ -3,6 +3,7 @@ import { Deployment, Pahse } from "./deployment";
|
|||||||
export type Domain = {
|
export type Domain = {
|
||||||
id: string;
|
id: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
|
email?: string;
|
||||||
crontab: string;
|
crontab: string;
|
||||||
access: string;
|
access: string;
|
||||||
targetAccess: string;
|
targetAccess: string;
|
||||||
|
9
ui/src/domain/settings.ts
Normal file
9
ui/src/domain/settings.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export type Setting = {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
content: EmailsSetting;
|
||||||
|
};
|
||||||
|
|
||||||
|
type EmailsSetting = {
|
||||||
|
emails: string[];
|
||||||
|
};
|
@ -34,10 +34,11 @@ import { useLocation, useNavigate } from "react-router-dom";
|
|||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
||||||
import { accessTypeMap } from "@/domain/access";
|
import { accessTypeMap } from "@/domain/access";
|
||||||
|
import EmailsEdit from "@/components/certimate/EmailsEdit";
|
||||||
|
|
||||||
const Edit = () => {
|
const Edit = () => {
|
||||||
const {
|
const {
|
||||||
config: { accesses },
|
config: { accesses, emails },
|
||||||
} = useConfig();
|
} = useConfig();
|
||||||
|
|
||||||
const [domain, setDomain] = useState<Domain>();
|
const [domain, setDomain] = useState<Domain>();
|
||||||
@ -62,6 +63,7 @@ const Edit = () => {
|
|||||||
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, {
|
||||||
message: "请输入正确的域名",
|
message: "请输入正确的域名",
|
||||||
}),
|
}),
|
||||||
|
email: z.string().email().optional(),
|
||||||
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
access: z.string().regex(/^[a-zA-Z0-9]+$/, {
|
||||||
message: "请选择DNS服务商授权配置",
|
message: "请选择DNS服务商授权配置",
|
||||||
}),
|
}),
|
||||||
@ -78,6 +80,7 @@ const Edit = () => {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
id: "",
|
id: "",
|
||||||
domain: "",
|
domain: "",
|
||||||
|
email: "",
|
||||||
access: "",
|
access: "",
|
||||||
targetAccess: "",
|
targetAccess: "",
|
||||||
targetType: "",
|
targetType: "",
|
||||||
@ -89,6 +92,7 @@ const Edit = () => {
|
|||||||
form.reset({
|
form.reset({
|
||||||
id: domain.id,
|
id: domain.id,
|
||||||
domain: domain.domain,
|
domain: domain.domain,
|
||||||
|
email: domain.email,
|
||||||
access: domain.access,
|
access: domain.access,
|
||||||
targetAccess: domain.targetAccess,
|
targetAccess: domain.targetAccess,
|
||||||
targetType: domain.targetType,
|
targetType: domain.targetType,
|
||||||
@ -119,6 +123,7 @@ const Edit = () => {
|
|||||||
id: data.id as string,
|
id: data.id as string,
|
||||||
crontab: "0 0 * * *",
|
crontab: "0 0 * * *",
|
||||||
domain: data.domain,
|
domain: data.domain,
|
||||||
|
email: data.email,
|
||||||
access: data.access,
|
access: data.access,
|
||||||
targetAccess: data.targetAccess,
|
targetAccess: data.targetAccess,
|
||||||
targetType: data.targetType,
|
targetType: data.targetType,
|
||||||
@ -180,6 +185,51 @@ const Edit = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="flex w-full justify-between">
|
||||||
|
<div>Email(申请证书需要提供邮箱)</div>
|
||||||
|
<EmailsEdit
|
||||||
|
trigger={
|
||||||
|
<div className="font-normal text-primary hover:underline cursor-pointer flex items-center">
|
||||||
|
<Plus size={14} />
|
||||||
|
新增
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
value={field.value}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
form.setValue("email", value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="请选择邮箱" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>邮箱列表</SelectLabel>
|
||||||
|
{emails.content.emails.map((item) => (
|
||||||
|
<SelectItem key={item} value={item}>
|
||||||
|
<div>{item}</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="access"
|
name="access"
|
||||||
|
@ -10,9 +10,12 @@ import {
|
|||||||
useReducer,
|
useReducer,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { configReducer } from "./reducer";
|
import { configReducer } from "./reducer";
|
||||||
|
import { getEmails } from "@/repository/settings";
|
||||||
|
import { Setting } from "@/domain/settings";
|
||||||
|
|
||||||
export type ConfigData = {
|
export type ConfigData = {
|
||||||
accesses: Access[];
|
accesses: Access[];
|
||||||
|
emails: Setting;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ConfigContext = {
|
export type ConfigContext = {
|
||||||
@ -20,6 +23,7 @@ export type ConfigContext = {
|
|||||||
deleteAccess: (id: string) => void;
|
deleteAccess: (id: string) => void;
|
||||||
addAccess: (access: Access) => void;
|
addAccess: (access: Access) => void;
|
||||||
updateAccess: (access: Access) => void;
|
updateAccess: (access: Access) => void;
|
||||||
|
setEmails: (email: Setting) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Context = createContext({} as ConfigContext);
|
const Context = createContext({} as ConfigContext);
|
||||||
@ -31,7 +35,10 @@ interface ContainerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigProvider = ({ children }: ContainerProps) => {
|
export const ConfigProvider = ({ children }: ContainerProps) => {
|
||||||
const [config, dispatchConfig] = useReducer(configReducer, { accesses: [] });
|
const [config, dispatchConfig] = useReducer(configReducer, {
|
||||||
|
accesses: [],
|
||||||
|
emails: { content: { emails: [] } },
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const featchData = async () => {
|
const featchData = async () => {
|
||||||
@ -41,6 +48,18 @@ export const ConfigProvider = ({ children }: ContainerProps) => {
|
|||||||
featchData();
|
featchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const featchEmails = async () => {
|
||||||
|
const emails = await getEmails();
|
||||||
|
dispatchConfig({ type: "SET_EMAILS", payload: emails });
|
||||||
|
};
|
||||||
|
featchEmails();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setEmails = useCallback((emails: Setting) => {
|
||||||
|
dispatchConfig({ type: "SET_EMAILS", payload: emails });
|
||||||
|
}, []);
|
||||||
|
|
||||||
const deleteAccess = useCallback((id: string) => {
|
const deleteAccess = useCallback((id: string) => {
|
||||||
dispatchConfig({ type: "DELETE_ACCESS", payload: id });
|
dispatchConfig({ type: "DELETE_ACCESS", payload: id });
|
||||||
}, []);
|
}, []);
|
||||||
@ -58,9 +77,11 @@ export const ConfigProvider = ({ children }: ContainerProps) => {
|
|||||||
value={{
|
value={{
|
||||||
config: {
|
config: {
|
||||||
accesses: config.accesses,
|
accesses: config.accesses,
|
||||||
|
emails: config.emails,
|
||||||
},
|
},
|
||||||
deleteAccess,
|
deleteAccess,
|
||||||
addAccess,
|
addAccess,
|
||||||
|
setEmails,
|
||||||
updateAccess,
|
updateAccess,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { Access } from "@/domain/access";
|
import { Access } from "@/domain/access";
|
||||||
import { ConfigData } from ".";
|
import { ConfigData } from ".";
|
||||||
|
import { Setting } from "@/domain/settings";
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| { type: "ADD_ACCESS"; payload: Access }
|
| { type: "ADD_ACCESS"; payload: Access }
|
||||||
| { type: "DELETE_ACCESS"; payload: string }
|
| { type: "DELETE_ACCESS"; payload: string }
|
||||||
| { type: "UPDATE_ACCESS"; payload: Access }
|
| { type: "UPDATE_ACCESS"; payload: Access }
|
||||||
| { type: "SET_ACCESSES"; payload: Access[] };
|
| { type: "SET_ACCESSES"; payload: Access[] }
|
||||||
|
| { type: "SET_EMAILS"; payload: Setting }
|
||||||
|
| { type: "ADD_EMAIL"; payload: string };
|
||||||
|
|
||||||
export const configReducer = (
|
export const configReducer = (
|
||||||
state: ConfigData,
|
state: ConfigData,
|
||||||
@ -40,6 +43,23 @@ export const configReducer = (
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "SET_EMAILS": {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
emails: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "ADD_EMAIL": {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
emails: {
|
||||||
|
...state.emails,
|
||||||
|
content: {
|
||||||
|
emails: [...state.emails.content.emails, action.payload],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
26
ui/src/repository/settings.ts
Normal file
26
ui/src/repository/settings.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Setting } from "@/domain/settings";
|
||||||
|
import { getPb } from "./api";
|
||||||
|
|
||||||
|
export const getEmails = async () => {
|
||||||
|
try {
|
||||||
|
const resp = await getPb()
|
||||||
|
.collection("settings")
|
||||||
|
.getFirstListItem<Setting>("name='emails'");
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
content: { emails: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (setting: Setting) => {
|
||||||
|
const pb = getPb();
|
||||||
|
let resp: Setting;
|
||||||
|
if (setting.id) {
|
||||||
|
resp = await pb.collection("settings").update(setting.id, setting);
|
||||||
|
} else {
|
||||||
|
resp = await pb.collection("settings").create(setting);
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user