mirror of
https://github.com/certd/certd.git
synced 2026-04-14 20:40:53 +08:00
Compare commits
47 Commits
6eb20a1f2e
...
v2-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee72d10718 | ||
|
|
831871d37f | ||
|
|
6072550ec1 | ||
|
|
112a565bf7 | ||
|
|
59e5c76286 | ||
|
|
21620ac6bd | ||
|
|
d05129ec67 | ||
|
|
0998de4ae6 | ||
|
|
2bdf1832da | ||
|
|
a846c4b66e | ||
|
|
ee535895a3 | ||
|
|
1e549dfd43 | ||
|
|
6ee718a252 | ||
|
|
557e98c33f | ||
|
|
7a9eec88e8 | ||
|
|
a7a4f66633 | ||
|
|
a88d0a6ae1 | ||
|
|
db87bc770e | ||
|
|
7b6b71cd4b | ||
|
|
df98463325 | ||
|
|
f7492db8bd | ||
|
|
70b46d4a8f | ||
|
|
411486e1e7 | ||
|
|
6f81305232 | ||
|
|
79bc22d8ce | ||
|
|
1c634a702a | ||
|
|
909a9e4050 | ||
|
|
b5cc794061 | ||
|
|
73b8e85976 | ||
|
|
282b5d6893 | ||
|
|
c6628e7311 | ||
|
|
6b109d172f | ||
|
|
6b29972399 | ||
|
|
0fcd3c09fd | ||
|
|
af503442b8 | ||
|
|
c875971b71 | ||
|
|
d1a65922d7 | ||
|
|
6ef34f95d5 | ||
|
|
8b79022179 | ||
|
|
21aec77e5c | ||
|
|
74c5259af8 | ||
|
|
a3e7d4414d | ||
|
|
986d32eb81 | ||
|
|
de0ae14544 | ||
|
|
6b52276fb6 | ||
|
|
a19ea7489c | ||
|
|
14229c2f00 |
@@ -1,115 +0,0 @@
|
||||
---
|
||||
name: using-superpowers
|
||||
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
|
||||
---
|
||||
|
||||
<SUBAGENT-STOP>
|
||||
If you were dispatched as a subagent to execute a specific task, skip this skill.
|
||||
</SUBAGENT-STOP>
|
||||
|
||||
<EXTREMELY-IMPORTANT>
|
||||
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.
|
||||
|
||||
IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
||||
|
||||
This is not negotiable. This is not optional. You cannot rationalize your way out of this.
|
||||
</EXTREMELY-IMPORTANT>
|
||||
|
||||
## Instruction Priority
|
||||
|
||||
Superpowers skills override default system prompt behavior, but **user instructions always take precedence**:
|
||||
|
||||
1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
|
||||
2. **Superpowers skills** — override default system behavior where they conflict
|
||||
3. **Default system prompt** — lowest priority
|
||||
|
||||
If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.
|
||||
|
||||
## How to Access Skills
|
||||
|
||||
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
|
||||
|
||||
**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.
|
||||
|
||||
**In other environments:** Check your platform's documentation for how skills are loaded.
|
||||
|
||||
## Platform Adaptation
|
||||
|
||||
Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
|
||||
|
||||
# Using Skills
|
||||
|
||||
## The Rule
|
||||
|
||||
**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.
|
||||
|
||||
```dot
|
||||
digraph skill_flow {
|
||||
"User message received" [shape=doublecircle];
|
||||
"About to EnterPlanMode?" [shape=doublecircle];
|
||||
"Already brainstormed?" [shape=diamond];
|
||||
"Invoke brainstorming skill" [shape=box];
|
||||
"Might any skill apply?" [shape=diamond];
|
||||
"Invoke Skill tool" [shape=box];
|
||||
"Announce: 'Using [skill] to [purpose]'" [shape=box];
|
||||
"Has checklist?" [shape=diamond];
|
||||
"Create TodoWrite todo per item" [shape=box];
|
||||
"Follow skill exactly" [shape=box];
|
||||
"Respond (including clarifications)" [shape=doublecircle];
|
||||
|
||||
"About to EnterPlanMode?" -> "Already brainstormed?";
|
||||
"Already brainstormed?" -> "Invoke brainstorming skill" [label="no"];
|
||||
"Already brainstormed?" -> "Might any skill apply?" [label="yes"];
|
||||
"Invoke brainstorming skill" -> "Might any skill apply?";
|
||||
|
||||
"User message received" -> "Might any skill apply?";
|
||||
"Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
|
||||
"Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
|
||||
"Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
|
||||
"Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
|
||||
"Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
|
||||
"Has checklist?" -> "Follow skill exactly" [label="no"];
|
||||
"Create TodoWrite todo per item" -> "Follow skill exactly";
|
||||
}
|
||||
```
|
||||
|
||||
## Red Flags
|
||||
|
||||
These thoughts mean STOP—you're rationalizing:
|
||||
|
||||
| Thought | Reality |
|
||||
|---------|---------|
|
||||
| "This is just a simple question" | Questions are tasks. Check for skills. |
|
||||
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
|
||||
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
|
||||
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
|
||||
| "Let me gather information first" | Skills tell you HOW to gather information. |
|
||||
| "This doesn't need a formal skill" | If a skill exists, use it. |
|
||||
| "I remember this skill" | Skills evolve. Read current version. |
|
||||
| "This doesn't count as a task" | Action = task. Check for skills. |
|
||||
| "The skill is overkill" | Simple things become complex. Use it. |
|
||||
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
||||
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
||||
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |
|
||||
|
||||
## Skill Priority
|
||||
|
||||
When multiple skills could apply, use this order:
|
||||
|
||||
1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task
|
||||
2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution
|
||||
|
||||
"Let's build X" → brainstorming first, then implementation skills.
|
||||
"Fix this bug" → debugging first, then domain-specific skills.
|
||||
|
||||
## Skill Types
|
||||
|
||||
**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline.
|
||||
|
||||
**Flexible** (patterns): Adapt principles to context.
|
||||
|
||||
The skill itself tells you which.
|
||||
|
||||
## User Instructions
|
||||
|
||||
Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Codex Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Codex equivalent |
|
||||
|-----------------|------------------|
|
||||
| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) |
|
||||
| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls |
|
||||
| Task returns result | `wait` |
|
||||
| Task completes automatically | `close_agent` to free slot |
|
||||
| `TodoWrite` (task tracking) | `update_plan` |
|
||||
| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions |
|
||||
| `Read`, `Write`, `Edit` (files) | Use your native file tools |
|
||||
| `Bash` (run commands) | Use your native shell tools |
|
||||
|
||||
## Subagent dispatch requires multi-agent support
|
||||
|
||||
Add to your Codex config (`~/.codex/config.toml`):
|
||||
|
||||
```toml
|
||||
[features]
|
||||
multi_agent = true
|
||||
```
|
||||
|
||||
This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`.
|
||||
|
||||
## Named agent dispatch
|
||||
|
||||
Claude Code skills reference named agent types like `superpowers:code-reviewer`.
|
||||
Codex does not have a named agent registry — `spawn_agent` creates generic agents
|
||||
from built-in roles (`default`, `explorer`, `worker`).
|
||||
|
||||
When a skill says to dispatch a named agent type:
|
||||
|
||||
1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's
|
||||
local prompt template like `code-quality-reviewer-prompt.md`)
|
||||
2. Read the prompt content
|
||||
3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.)
|
||||
4. Spawn a `worker` agent with the filled content as the `message`
|
||||
|
||||
| Skill instruction | Codex equivalent |
|
||||
|-------------------|------------------|
|
||||
| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content |
|
||||
| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt |
|
||||
|
||||
### Message framing
|
||||
|
||||
The `message` parameter is user-level input, not a system prompt. Structure it
|
||||
for maximum instruction adherence:
|
||||
|
||||
```
|
||||
Your task is to perform the following. Follow the instructions below exactly.
|
||||
|
||||
<agent-instructions>
|
||||
[filled prompt content from the agent's .md file]
|
||||
</agent-instructions>
|
||||
|
||||
Execute this now. Output ONLY the structured response following the format
|
||||
specified in the instructions above.
|
||||
```
|
||||
|
||||
- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...")
|
||||
- Wrap instructions in XML tags — the model treats tagged blocks as authoritative
|
||||
- End with an explicit execution directive to prevent summarization of the instructions
|
||||
|
||||
### When this workaround can be removed
|
||||
|
||||
This approach compensates for Codex's plugin system not yet supporting an `agents`
|
||||
field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the
|
||||
plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and
|
||||
skills can dispatch named agent types directly.
|
||||
|
||||
## Environment Detection
|
||||
|
||||
Skills that create worktrees or finish branches should detect their
|
||||
environment with read-only git commands before proceeding:
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
BRANCH=$(git branch --show-current)
|
||||
```
|
||||
|
||||
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
|
||||
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
|
||||
|
||||
See `using-git-worktrees` Step 0 and `finishing-a-development-branch`
|
||||
Step 1 for how each skill uses these signals.
|
||||
|
||||
## Codex App Finishing
|
||||
|
||||
When the sandbox blocks branch/push operations (detached HEAD in an
|
||||
externally managed worktree), the agent commits all work and informs
|
||||
the user to use the App's native controls:
|
||||
|
||||
- **"Create branch"** — names the branch, then commit/push/PR via App UI
|
||||
- **"Hand off to local"** — transfers work to the user's local checkout
|
||||
|
||||
The agent can still run tests, stage files, and output suggested branch
|
||||
names, commit messages, and PR descriptions for the user to copy.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Gemini CLI Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Gemini CLI equivalent |
|
||||
|-----------------|----------------------|
|
||||
| `Read` (file reading) | `read_file` |
|
||||
| `Write` (file creation) | `write_file` |
|
||||
| `Edit` (file editing) | `replace` |
|
||||
| `Bash` (run commands) | `run_shell_command` |
|
||||
| `Grep` (search file content) | `grep_search` |
|
||||
| `Glob` (search files by name) | `glob` |
|
||||
| `TodoWrite` (task tracking) | `write_todos` |
|
||||
| `Skill` tool (invoke a skill) | `activate_skill` |
|
||||
| `WebSearch` | `google_web_search` |
|
||||
| `WebFetch` | `web_fetch` |
|
||||
| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents |
|
||||
|
||||
## No subagent support
|
||||
|
||||
Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`.
|
||||
|
||||
## Additional Gemini CLI tools
|
||||
|
||||
These tools are available in Gemini CLI but have no Claude Code equivalent:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `list_directory` | List files and subdirectories |
|
||||
| `save_memory` | Persist facts to GEMINI.md across sessions |
|
||||
| `ask_user` | Request structured input from the user |
|
||||
| `tracker_create_task` | Rich task management (create, update, list, visualize) |
|
||||
| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes |
|
||||
@@ -1,115 +0,0 @@
|
||||
---
|
||||
name: using-superpowers
|
||||
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
|
||||
---
|
||||
|
||||
<SUBAGENT-STOP>
|
||||
If you were dispatched as a subagent to execute a specific task, skip this skill.
|
||||
</SUBAGENT-STOP>
|
||||
|
||||
<EXTREMELY-IMPORTANT>
|
||||
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.
|
||||
|
||||
IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
||||
|
||||
This is not negotiable. This is not optional. You cannot rationalize your way out of this.
|
||||
</EXTREMELY-IMPORTANT>
|
||||
|
||||
## Instruction Priority
|
||||
|
||||
Superpowers skills override default system prompt behavior, but **user instructions always take precedence**:
|
||||
|
||||
1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
|
||||
2. **Superpowers skills** — override default system behavior where they conflict
|
||||
3. **Default system prompt** — lowest priority
|
||||
|
||||
If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.
|
||||
|
||||
## How to Access Skills
|
||||
|
||||
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
|
||||
|
||||
**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.
|
||||
|
||||
**In other environments:** Check your platform's documentation for how skills are loaded.
|
||||
|
||||
## Platform Adaptation
|
||||
|
||||
Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
|
||||
|
||||
# Using Skills
|
||||
|
||||
## The Rule
|
||||
|
||||
**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.
|
||||
|
||||
```dot
|
||||
digraph skill_flow {
|
||||
"User message received" [shape=doublecircle];
|
||||
"About to EnterPlanMode?" [shape=doublecircle];
|
||||
"Already brainstormed?" [shape=diamond];
|
||||
"Invoke brainstorming skill" [shape=box];
|
||||
"Might any skill apply?" [shape=diamond];
|
||||
"Invoke Skill tool" [shape=box];
|
||||
"Announce: 'Using [skill] to [purpose]'" [shape=box];
|
||||
"Has checklist?" [shape=diamond];
|
||||
"Create TodoWrite todo per item" [shape=box];
|
||||
"Follow skill exactly" [shape=box];
|
||||
"Respond (including clarifications)" [shape=doublecircle];
|
||||
|
||||
"About to EnterPlanMode?" -> "Already brainstormed?";
|
||||
"Already brainstormed?" -> "Invoke brainstorming skill" [label="no"];
|
||||
"Already brainstormed?" -> "Might any skill apply?" [label="yes"];
|
||||
"Invoke brainstorming skill" -> "Might any skill apply?";
|
||||
|
||||
"User message received" -> "Might any skill apply?";
|
||||
"Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
|
||||
"Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
|
||||
"Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
|
||||
"Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
|
||||
"Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
|
||||
"Has checklist?" -> "Follow skill exactly" [label="no"];
|
||||
"Create TodoWrite todo per item" -> "Follow skill exactly";
|
||||
}
|
||||
```
|
||||
|
||||
## Red Flags
|
||||
|
||||
These thoughts mean STOP—you're rationalizing:
|
||||
|
||||
| Thought | Reality |
|
||||
|---------|---------|
|
||||
| "This is just a simple question" | Questions are tasks. Check for skills. |
|
||||
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
|
||||
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
|
||||
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
|
||||
| "Let me gather information first" | Skills tell you HOW to gather information. |
|
||||
| "This doesn't need a formal skill" | If a skill exists, use it. |
|
||||
| "I remember this skill" | Skills evolve. Read current version. |
|
||||
| "This doesn't count as a task" | Action = task. Check for skills. |
|
||||
| "The skill is overkill" | Simple things become complex. Use it. |
|
||||
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
||||
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
||||
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |
|
||||
|
||||
## Skill Priority
|
||||
|
||||
When multiple skills could apply, use this order:
|
||||
|
||||
1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task
|
||||
2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution
|
||||
|
||||
"Let's build X" → brainstorming first, then implementation skills.
|
||||
"Fix this bug" → debugging first, then domain-specific skills.
|
||||
|
||||
## Skill Types
|
||||
|
||||
**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline.
|
||||
|
||||
**Flexible** (patterns): Adapt principles to context.
|
||||
|
||||
The skill itself tells you which.
|
||||
|
||||
## User Instructions
|
||||
|
||||
Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Codex Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Codex equivalent |
|
||||
|-----------------|------------------|
|
||||
| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) |
|
||||
| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls |
|
||||
| Task returns result | `wait` |
|
||||
| Task completes automatically | `close_agent` to free slot |
|
||||
| `TodoWrite` (task tracking) | `update_plan` |
|
||||
| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions |
|
||||
| `Read`, `Write`, `Edit` (files) | Use your native file tools |
|
||||
| `Bash` (run commands) | Use your native shell tools |
|
||||
|
||||
## Subagent dispatch requires multi-agent support
|
||||
|
||||
Add to your Codex config (`~/.codex/config.toml`):
|
||||
|
||||
```toml
|
||||
[features]
|
||||
multi_agent = true
|
||||
```
|
||||
|
||||
This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`.
|
||||
|
||||
## Named agent dispatch
|
||||
|
||||
Claude Code skills reference named agent types like `superpowers:code-reviewer`.
|
||||
Codex does not have a named agent registry — `spawn_agent` creates generic agents
|
||||
from built-in roles (`default`, `explorer`, `worker`).
|
||||
|
||||
When a skill says to dispatch a named agent type:
|
||||
|
||||
1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's
|
||||
local prompt template like `code-quality-reviewer-prompt.md`)
|
||||
2. Read the prompt content
|
||||
3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.)
|
||||
4. Spawn a `worker` agent with the filled content as the `message`
|
||||
|
||||
| Skill instruction | Codex equivalent |
|
||||
|-------------------|------------------|
|
||||
| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content |
|
||||
| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt |
|
||||
|
||||
### Message framing
|
||||
|
||||
The `message` parameter is user-level input, not a system prompt. Structure it
|
||||
for maximum instruction adherence:
|
||||
|
||||
```
|
||||
Your task is to perform the following. Follow the instructions below exactly.
|
||||
|
||||
<agent-instructions>
|
||||
[filled prompt content from the agent's .md file]
|
||||
</agent-instructions>
|
||||
|
||||
Execute this now. Output ONLY the structured response following the format
|
||||
specified in the instructions above.
|
||||
```
|
||||
|
||||
- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...")
|
||||
- Wrap instructions in XML tags — the model treats tagged blocks as authoritative
|
||||
- End with an explicit execution directive to prevent summarization of the instructions
|
||||
|
||||
### When this workaround can be removed
|
||||
|
||||
This approach compensates for Codex's plugin system not yet supporting an `agents`
|
||||
field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the
|
||||
plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and
|
||||
skills can dispatch named agent types directly.
|
||||
|
||||
## Environment Detection
|
||||
|
||||
Skills that create worktrees or finish branches should detect their
|
||||
environment with read-only git commands before proceeding:
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
BRANCH=$(git branch --show-current)
|
||||
```
|
||||
|
||||
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
|
||||
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
|
||||
|
||||
See `using-git-worktrees` Step 0 and `finishing-a-development-branch`
|
||||
Step 1 for how each skill uses these signals.
|
||||
|
||||
## Codex App Finishing
|
||||
|
||||
When the sandbox blocks branch/push operations (detached HEAD in an
|
||||
externally managed worktree), the agent commits all work and informs
|
||||
the user to use the App's native controls:
|
||||
|
||||
- **"Create branch"** — names the branch, then commit/push/PR via App UI
|
||||
- **"Hand off to local"** — transfers work to the user's local checkout
|
||||
|
||||
The agent can still run tests, stage files, and output suggested branch
|
||||
names, commit messages, and PR descriptions for the user to copy.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Gemini CLI Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Gemini CLI equivalent |
|
||||
|-----------------|----------------------|
|
||||
| `Read` (file reading) | `read_file` |
|
||||
| `Write` (file creation) | `write_file` |
|
||||
| `Edit` (file editing) | `replace` |
|
||||
| `Bash` (run commands) | `run_shell_command` |
|
||||
| `Grep` (search file content) | `grep_search` |
|
||||
| `Glob` (search files by name) | `glob` |
|
||||
| `TodoWrite` (task tracking) | `write_todos` |
|
||||
| `Skill` tool (invoke a skill) | `activate_skill` |
|
||||
| `WebSearch` | `google_web_search` |
|
||||
| `WebFetch` | `web_fetch` |
|
||||
| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents |
|
||||
|
||||
## No subagent support
|
||||
|
||||
Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`.
|
||||
|
||||
## Additional Gemini CLI tools
|
||||
|
||||
These tools are available in Gemini CLI but have no Claude Code equivalent:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `list_directory` | List files and subdirectories |
|
||||
| `save_memory` | Persist facts to GEMINI.md across sessions |
|
||||
| `ask_user` | Request structured input from the user |
|
||||
| `tracker_create_task` | Rich task management (create, update, list, visualize) |
|
||||
| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes |
|
||||
@@ -163,6 +163,16 @@ async doRequest(req: { action: string, data?: any }) {
|
||||
}
|
||||
```
|
||||
|
||||
--- 开发技巧:实现统一的 API 请求封装
|
||||
|
||||
**好处:**
|
||||
- **代码复用**:避免在每个 API 方法中重复编写相同的 header 设置和错误处理逻辑
|
||||
- **错误处理一致**:统一捕获和处理各种错误情况,确保错误信息格式统一
|
||||
- **日志记录完善**:集中记录详细的错误信息,便于调试和问题排查
|
||||
- **接口调用简化**:调用方只需关注业务逻辑,无需关心底层请求细节
|
||||
- **易于维护**:统一修改 API 调用方式时,只需修改一处代码
|
||||
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
@@ -170,9 +180,12 @@ async doRequest(req: { action: string, data?: any }) {
|
||||
3. **日志输出**:必须使用 `this.ctx.logger` 输出日志,而不是 `console`。
|
||||
4. **错误处理**:API 调用失败时应抛出明确的错误信息。
|
||||
5. **测试方法**:实现 `onTestRequest` 方法,以便用户可以测试授权是否正常。
|
||||
6. **统一接口调用方法**:封装统一的 API 请求方法,避免在每个 API 方法调用中重复编写错误处理逻辑。
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 示例 1: 通用授权插件
|
||||
|
||||
```typescript
|
||||
import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline';
|
||||
import { DomainRecord } from '@certd/plugin-lib';
|
||||
|
||||
@@ -6,9 +6,8 @@ Access:存储用户的第三放应用的授权数据,比如用户名密码
|
||||
Task: 部署任务插件,它继承AbstractTaskPlugin类,被流水线调用execute方法,将证书部署到对应的应用上
|
||||
DnsProvider: DNS提供商插件,它用于在ACME申请证书时给域名添加txt解析记录。
|
||||
|
||||
在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
|
||||
当开发过程中遇到问题,需要参考plugins目录下的其他插件,或者用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
|
||||
|
||||
一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
|
||||
|
||||
完成开发后无需测试,通知用户自己去测试
|
||||
注意事项:
|
||||
1、使用技能:在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
|
||||
2、迭代技能:当开发过程用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
|
||||
3、一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
|
||||
4、完成开发后无需测试,通知用户自己去测试
|
||||
@@ -126,6 +126,8 @@ if (isDev()) {
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 示例:通用 DNS Provider
|
||||
|
||||
```typescript
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { DemoAccess } from './access.js';
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
---
|
||||
name: using-superpowers
|
||||
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
|
||||
---
|
||||
|
||||
<SUBAGENT-STOP>
|
||||
If you were dispatched as a subagent to execute a specific task, skip this skill.
|
||||
</SUBAGENT-STOP>
|
||||
|
||||
<EXTREMELY-IMPORTANT>
|
||||
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.
|
||||
|
||||
IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
||||
|
||||
This is not negotiable. This is not optional. You cannot rationalize your way out of this.
|
||||
</EXTREMELY-IMPORTANT>
|
||||
|
||||
## Instruction Priority
|
||||
|
||||
Superpowers skills override default system prompt behavior, but **user instructions always take precedence**:
|
||||
|
||||
1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
|
||||
2. **Superpowers skills** — override default system behavior where they conflict
|
||||
3. **Default system prompt** — lowest priority
|
||||
|
||||
If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.
|
||||
|
||||
## How to Access Skills
|
||||
|
||||
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
|
||||
|
||||
**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.
|
||||
|
||||
**In other environments:** Check your platform's documentation for how skills are loaded.
|
||||
|
||||
## Platform Adaptation
|
||||
|
||||
Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
|
||||
|
||||
# Using Skills
|
||||
|
||||
## The Rule
|
||||
|
||||
**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.
|
||||
|
||||
```dot
|
||||
digraph skill_flow {
|
||||
"User message received" [shape=doublecircle];
|
||||
"About to EnterPlanMode?" [shape=doublecircle];
|
||||
"Already brainstormed?" [shape=diamond];
|
||||
"Invoke brainstorming skill" [shape=box];
|
||||
"Might any skill apply?" [shape=diamond];
|
||||
"Invoke Skill tool" [shape=box];
|
||||
"Announce: 'Using [skill] to [purpose]'" [shape=box];
|
||||
"Has checklist?" [shape=diamond];
|
||||
"Create TodoWrite todo per item" [shape=box];
|
||||
"Follow skill exactly" [shape=box];
|
||||
"Respond (including clarifications)" [shape=doublecircle];
|
||||
|
||||
"About to EnterPlanMode?" -> "Already brainstormed?";
|
||||
"Already brainstormed?" -> "Invoke brainstorming skill" [label="no"];
|
||||
"Already brainstormed?" -> "Might any skill apply?" [label="yes"];
|
||||
"Invoke brainstorming skill" -> "Might any skill apply?";
|
||||
|
||||
"User message received" -> "Might any skill apply?";
|
||||
"Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
|
||||
"Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
|
||||
"Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
|
||||
"Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
|
||||
"Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
|
||||
"Has checklist?" -> "Follow skill exactly" [label="no"];
|
||||
"Create TodoWrite todo per item" -> "Follow skill exactly";
|
||||
}
|
||||
```
|
||||
|
||||
## Red Flags
|
||||
|
||||
These thoughts mean STOP—you're rationalizing:
|
||||
|
||||
| Thought | Reality |
|
||||
|---------|---------|
|
||||
| "This is just a simple question" | Questions are tasks. Check for skills. |
|
||||
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
|
||||
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
|
||||
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
|
||||
| "Let me gather information first" | Skills tell you HOW to gather information. |
|
||||
| "This doesn't need a formal skill" | If a skill exists, use it. |
|
||||
| "I remember this skill" | Skills evolve. Read current version. |
|
||||
| "This doesn't count as a task" | Action = task. Check for skills. |
|
||||
| "The skill is overkill" | Simple things become complex. Use it. |
|
||||
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
||||
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
||||
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |
|
||||
|
||||
## Skill Priority
|
||||
|
||||
When multiple skills could apply, use this order:
|
||||
|
||||
1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task
|
||||
2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution
|
||||
|
||||
"Let's build X" → brainstorming first, then implementation skills.
|
||||
"Fix this bug" → debugging first, then domain-specific skills.
|
||||
|
||||
## Skill Types
|
||||
|
||||
**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline.
|
||||
|
||||
**Flexible** (patterns): Adapt principles to context.
|
||||
|
||||
The skill itself tells you which.
|
||||
|
||||
## User Instructions
|
||||
|
||||
Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Codex Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Codex equivalent |
|
||||
|-----------------|------------------|
|
||||
| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) |
|
||||
| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls |
|
||||
| Task returns result | `wait` |
|
||||
| Task completes automatically | `close_agent` to free slot |
|
||||
| `TodoWrite` (task tracking) | `update_plan` |
|
||||
| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions |
|
||||
| `Read`, `Write`, `Edit` (files) | Use your native file tools |
|
||||
| `Bash` (run commands) | Use your native shell tools |
|
||||
|
||||
## Subagent dispatch requires multi-agent support
|
||||
|
||||
Add to your Codex config (`~/.codex/config.toml`):
|
||||
|
||||
```toml
|
||||
[features]
|
||||
multi_agent = true
|
||||
```
|
||||
|
||||
This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`.
|
||||
|
||||
## Named agent dispatch
|
||||
|
||||
Claude Code skills reference named agent types like `superpowers:code-reviewer`.
|
||||
Codex does not have a named agent registry — `spawn_agent` creates generic agents
|
||||
from built-in roles (`default`, `explorer`, `worker`).
|
||||
|
||||
When a skill says to dispatch a named agent type:
|
||||
|
||||
1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's
|
||||
local prompt template like `code-quality-reviewer-prompt.md`)
|
||||
2. Read the prompt content
|
||||
3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.)
|
||||
4. Spawn a `worker` agent with the filled content as the `message`
|
||||
|
||||
| Skill instruction | Codex equivalent |
|
||||
|-------------------|------------------|
|
||||
| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content |
|
||||
| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt |
|
||||
|
||||
### Message framing
|
||||
|
||||
The `message` parameter is user-level input, not a system prompt. Structure it
|
||||
for maximum instruction adherence:
|
||||
|
||||
```
|
||||
Your task is to perform the following. Follow the instructions below exactly.
|
||||
|
||||
<agent-instructions>
|
||||
[filled prompt content from the agent's .md file]
|
||||
</agent-instructions>
|
||||
|
||||
Execute this now. Output ONLY the structured response following the format
|
||||
specified in the instructions above.
|
||||
```
|
||||
|
||||
- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...")
|
||||
- Wrap instructions in XML tags — the model treats tagged blocks as authoritative
|
||||
- End with an explicit execution directive to prevent summarization of the instructions
|
||||
|
||||
### When this workaround can be removed
|
||||
|
||||
This approach compensates for Codex's plugin system not yet supporting an `agents`
|
||||
field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the
|
||||
plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and
|
||||
skills can dispatch named agent types directly.
|
||||
|
||||
## Environment Detection
|
||||
|
||||
Skills that create worktrees or finish branches should detect their
|
||||
environment with read-only git commands before proceeding:
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
BRANCH=$(git branch --show-current)
|
||||
```
|
||||
|
||||
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
|
||||
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
|
||||
|
||||
See `using-git-worktrees` Step 0 and `finishing-a-development-branch`
|
||||
Step 1 for how each skill uses these signals.
|
||||
|
||||
## Codex App Finishing
|
||||
|
||||
When the sandbox blocks branch/push operations (detached HEAD in an
|
||||
externally managed worktree), the agent commits all work and informs
|
||||
the user to use the App's native controls:
|
||||
|
||||
- **"Create branch"** — names the branch, then commit/push/PR via App UI
|
||||
- **"Hand off to local"** — transfers work to the user's local checkout
|
||||
|
||||
The agent can still run tests, stage files, and output suggested branch
|
||||
names, commit messages, and PR descriptions for the user to copy.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Gemini CLI Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Gemini CLI equivalent |
|
||||
|-----------------|----------------------|
|
||||
| `Read` (file reading) | `read_file` |
|
||||
| `Write` (file creation) | `write_file` |
|
||||
| `Edit` (file editing) | `replace` |
|
||||
| `Bash` (run commands) | `run_shell_command` |
|
||||
| `Grep` (search file content) | `grep_search` |
|
||||
| `Glob` (search files by name) | `glob` |
|
||||
| `TodoWrite` (task tracking) | `write_todos` |
|
||||
| `Skill` tool (invoke a skill) | `activate_skill` |
|
||||
| `WebSearch` | `google_web_search` |
|
||||
| `WebFetch` | `web_fetch` |
|
||||
| `Task` tool (dispatch subagent) | No equivalent — Gemini CLI does not support subagents |
|
||||
|
||||
## No subagent support
|
||||
|
||||
Gemini CLI has no equivalent to Claude Code's `Task` tool. Skills that rely on subagent dispatch (`subagent-driven-development`, `dispatching-parallel-agents`) will fall back to single-session execution via `executing-plans`.
|
||||
|
||||
## Additional Gemini CLI tools
|
||||
|
||||
These tools are available in Gemini CLI but have no Claude Code equivalent:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `list_directory` | List files and subdirectories |
|
||||
| `save_memory` | Persist facts to GEMINI.md across sessions |
|
||||
| `ask_user` | Request structured input from the user |
|
||||
| `tracker_create_task` | Rich task management (create, update, list, visualize) |
|
||||
| `enter_plan_mode` / `exit_plan_mode` | Switch to read-only research mode before making changes |
|
||||
49
CHANGELOG.md
49
CHANGELOG.md
@@ -3,6 +3,55 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复创建流水线无法选择通知的bug ([a88d0a6](https://github.com/certd/certd/commit/a88d0a6ae15cb6170d0b36e21daf89f0dbd5f681))
|
||||
* 修复流水线任务编辑页面复制粘贴按钮在夜间模式显示问题 ([1e549df](https://github.com/certd/certd/commit/1e549dfd431ed74e2bcdfce63e5f640c51603af3))
|
||||
* 修复用户管理添加用户无法上传头像的bug ([557e98c](https://github.com/certd/certd/commit/557e98c33f5462167d8f6289f70dad68bb114a97))
|
||||
* 修复自定义插件删除后没有反注册的bug ([df98463](https://github.com/certd/certd/commit/df9846332596d2afaba53e66d2897aa1c598f9c4))
|
||||
* 修复spaceship创建record报错的bug ([70b46d4](https://github.com/certd/certd/commit/70b46d4a8f89cf8eded21ebb237e8c8ce6c40d30))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 1panel支持先上传证书再选择证书 ([7a9eec8](https://github.com/certd/certd/commit/7a9eec88e8eddf40dba055c072b5b2b0f67c1407))
|
||||
* 部署到1panel面板支持mux模式 ([d05129e](https://github.com/certd/certd/commit/d05129ec67893b0b639003a4bca6878d128f56ad))
|
||||
* 流水线修改编辑之后,增加未保存提示 ([21620ac](https://github.com/certd/certd/commit/21620ac6bdeb57e43509156a77037fc07c44282a))
|
||||
* 修复检查全部某些情况下无效的bug,优化公共触发站点证书检查定时逻辑 ([ee53589](https://github.com/certd/certd/commit/ee535895a3166c6f9046963e28fa8f22f018b574))
|
||||
* 增加域名管理 子域名检查提醒 ([2bdf183](https://github.com/certd/certd/commit/2bdf1832da73a3728f3ac415837bc26e70531cd6))
|
||||
* 站点监控域名气泡增加端口显示 ([6ee718a](https://github.com/certd/certd/commit/6ee718a25265a9db2115343af9a1a01958f34b81))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复cn域名获取不到到期时间的问题 ([73b8e85](https://github.com/certd/certd/commit/73b8e859766097b5251fc4e5051593d686669eb2))
|
||||
* 修复某些情况下报无法修改通知的问题 ([d1a6592](https://github.com/certd/certd/commit/d1a65922d7e152d6edcf6c53b70079f16b54a0d3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 腾讯云CLB大区增加台北 ([6b109d1](https://github.com/certd/certd/commit/6b109d172f0c7b6ce6ec164dc196d646a65f529f))
|
||||
* 优化腾讯云CLB插件支持选择证书id ([c875971](https://github.com/certd/certd/commit/c875971b71dc6d392e56f0a7605281c40d9bb405))
|
||||
* 支持域名到期时间监控通知 ([c6628e7](https://github.com/certd/certd/commit/c6628e7311d6c43c2a784581fb25ec37b29c168d))
|
||||
* **monitor:** 支持查看监控执行记录 ([b5cc794](https://github.com/certd/certd/commit/b5cc794061c11b7200b669473c25c4bbfc944b61))
|
||||
* **plugin-dnsmgr:** 添加彩虹DNS插件支持 ([af50344](https://github.com/certd/certd/commit/af503442b8298c5b89d11cf2ea351d62e66a609e))
|
||||
* **spaceship:** 新增Spaceship DNS插件和授权模块 ([21aec77](https://github.com/certd/certd/commit/21aec77e5c3307b5973d4185baba33edcb28926f))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
|
||||
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
|
||||
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
|
||||
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -211,3 +211,4 @@ https://certd.handfree.work/
|
||||
| --------- |--------- |----------- |
|
||||
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | 基于vue3的crud快速开发框架 |
|
||||
| [dev-sidecar](https://github.com/docmirror/dev-sidecar/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"/> | 直连访问github工具,无需FQ,解决github无法访问的问题 |
|
||||
| [winsvc-manager](https://github.com/greper/winsvc-manager/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/greper/winsvc-manager?logo=github"/> | 可视化包装应用成为一个Windows服务,使其后台运行 |
|
||||
|
||||
@@ -124,6 +124,7 @@ export default defineConfig({
|
||||
{text: "子域名托管", link: "/guide/use/cert/subdomain.md"},
|
||||
{text: "流水线有效期", link: "/guide/use/pipeline/valid.md"},
|
||||
{text: "IP证书申请", link: "/guide/use/cert/ip.md"},
|
||||
{text: "企业模式", link: "/guide/use/mode/enterprise.md"},
|
||||
{text: "插件开发", link: "/guide/use/dev/plugin.md"},
|
||||
]
|
||||
},
|
||||
|
||||
@@ -3,6 +3,55 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复创建流水线无法选择通知的bug ([a88d0a6](https://github.com/certd/certd/commit/a88d0a6ae15cb6170d0b36e21daf89f0dbd5f681))
|
||||
* 修复流水线任务编辑页面复制粘贴按钮在夜间模式显示问题 ([1e549df](https://github.com/certd/certd/commit/1e549dfd431ed74e2bcdfce63e5f640c51603af3))
|
||||
* 修复用户管理添加用户无法上传头像的bug ([557e98c](https://github.com/certd/certd/commit/557e98c33f5462167d8f6289f70dad68bb114a97))
|
||||
* 修复自定义插件删除后没有反注册的bug ([df98463](https://github.com/certd/certd/commit/df9846332596d2afaba53e66d2897aa1c598f9c4))
|
||||
* 修复spaceship创建record报错的bug ([70b46d4](https://github.com/certd/certd/commit/70b46d4a8f89cf8eded21ebb237e8c8ce6c40d30))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 1panel支持先上传证书再选择证书 ([7a9eec8](https://github.com/certd/certd/commit/7a9eec88e8eddf40dba055c072b5b2b0f67c1407))
|
||||
* 部署到1panel面板支持mux模式 ([d05129e](https://github.com/certd/certd/commit/d05129ec67893b0b639003a4bca6878d128f56ad))
|
||||
* 流水线修改编辑之后,增加未保存提示 ([21620ac](https://github.com/certd/certd/commit/21620ac6bdeb57e43509156a77037fc07c44282a))
|
||||
* 修复检查全部某些情况下无效的bug,优化公共触发站点证书检查定时逻辑 ([ee53589](https://github.com/certd/certd/commit/ee535895a3166c6f9046963e28fa8f22f018b574))
|
||||
* 增加域名管理 子域名检查提醒 ([2bdf183](https://github.com/certd/certd/commit/2bdf1832da73a3728f3ac415837bc26e70531cd6))
|
||||
* 站点监控域名气泡增加端口显示 ([6ee718a](https://github.com/certd/certd/commit/6ee718a25265a9db2115343af9a1a01958f34b81))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复cn域名获取不到到期时间的问题 ([73b8e85](https://github.com/certd/certd/commit/73b8e859766097b5251fc4e5051593d686669eb2))
|
||||
* 修复某些情况下报无法修改通知的问题 ([d1a6592](https://github.com/certd/certd/commit/d1a65922d7e152d6edcf6c53b70079f16b54a0d3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 腾讯云CLB大区增加台北 ([6b109d1](https://github.com/certd/certd/commit/6b109d172f0c7b6ce6ec164dc196d646a65f529f))
|
||||
* 优化腾讯云CLB插件支持选择证书id ([c875971](https://github.com/certd/certd/commit/c875971b71dc6d392e56f0a7605281c40d9bb405))
|
||||
* 支持域名到期时间监控通知 ([c6628e7](https://github.com/certd/certd/commit/c6628e7311d6c43c2a784581fb25ec37b29c168d))
|
||||
* **monitor:** 支持查看监控执行记录 ([b5cc794](https://github.com/certd/certd/commit/b5cc794061c11b7200b669473c25c4bbfc944b61))
|
||||
* **plugin-dnsmgr:** 添加彩虹DNS插件支持 ([af50344](https://github.com/certd/certd/commit/af503442b8298c5b89d11cf2ea351d62e66a609e))
|
||||
* **spaceship:** 新增Spaceship DNS插件和授权模块 ([21aec77](https://github.com/certd/certd/commit/21aec77e5c3307b5973d4185baba33edcb28926f))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
|
||||
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
|
||||
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
|
||||
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -28,51 +28,53 @@
|
||||
| 24.| **中国移动CND授权** | |
|
||||
| 25.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 |
|
||||
| 26.| **dns.la授权** | |
|
||||
| 27.| **多吉云** | |
|
||||
| 28.| **Dokploy授权** | |
|
||||
| 29.| **farcdn授权** | |
|
||||
| 30.| **FlexCDN授权** | |
|
||||
| 31.| **Gcore** | Gcore |
|
||||
| 32.| **Github授权** | |
|
||||
| 33.| **godaddy授权** | |
|
||||
| 34.| **金山云授权** | |
|
||||
| 35.| **FTP授权** | |
|
||||
| 36.| **七牛OSS授权** | |
|
||||
| 37.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 38.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 39.| **namesilo授权** | |
|
||||
| 40.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||
| 41.| **1panel授权** | 账号和密码 |
|
||||
| 42.| **支付宝** | |
|
||||
| 43.| **白山云授权** | |
|
||||
| 44.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||
| 45.| **cdnfly授权** | |
|
||||
| 46.| **k8s授权** | |
|
||||
| 47.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 48.| **LeCDN授权** | |
|
||||
| 49.| **lucky** | |
|
||||
| 50.| **猫云授权** | |
|
||||
| 51.| **plesk授权** | |
|
||||
| 52.| **长亭雷池授权** | |
|
||||
| 53.| **群晖登录授权** | |
|
||||
| 54.| **uniCloud** | unicloud授权 |
|
||||
| 55.| **微信支付** | |
|
||||
| 56.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 57.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 58.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 59.| **易支付** | |
|
||||
| 60.| **proxmox** | |
|
||||
| 61.| **UCloud授权** | 优刻得授权 |
|
||||
| 62.| **又拍云** | |
|
||||
| 63.| **网宿授权** | |
|
||||
| 64.| **西部数码授权** | |
|
||||
| 65.| **我爱云授权** | 我爱云CDN |
|
||||
| 66.| **新网授权(代理方式)** | |
|
||||
| 67.| **新网授权** | |
|
||||
| 68.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||
| 69.| **Zenlayer授权** | Zenlayer授权 |
|
||||
| 70.| **GoEdge授权** | |
|
||||
| 71.| **雨云授权** | https://app.rainyun.com/ |
|
||||
| 27.| **彩虹DNS** | 彩虹DNS管理系统授权 |
|
||||
| 28.| **多吉云** | |
|
||||
| 29.| **Dokploy授权** | |
|
||||
| 30.| **farcdn授权** | |
|
||||
| 31.| **FlexCDN授权** | |
|
||||
| 32.| **Gcore** | Gcore |
|
||||
| 33.| **Github授权** | |
|
||||
| 34.| **godaddy授权** | |
|
||||
| 35.| **金山云授权** | |
|
||||
| 36.| **FTP授权** | |
|
||||
| 37.| **七牛OSS授权** | |
|
||||
| 38.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 39.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 40.| **namesilo授权** | |
|
||||
| 41.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||
| 42.| **1panel授权** | 账号和密码 |
|
||||
| 43.| **支付宝** | |
|
||||
| 44.| **白山云授权** | |
|
||||
| 45.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||
| 46.| **cdnfly授权** | |
|
||||
| 47.| **k8s授权** | |
|
||||
| 48.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 49.| **LeCDN授权** | |
|
||||
| 50.| **lucky** | |
|
||||
| 51.| **猫云授权** | |
|
||||
| 52.| **plesk授权** | |
|
||||
| 53.| **长亭雷池授权** | |
|
||||
| 54.| **群晖登录授权** | |
|
||||
| 55.| **uniCloud** | unicloud授权 |
|
||||
| 56.| **微信支付** | |
|
||||
| 57.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 58.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 59.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 60.| **易支付** | |
|
||||
| 61.| **proxmox** | |
|
||||
| 62.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
||||
| 63.| **UCloud授权** | 优刻得授权 |
|
||||
| 64.| **又拍云** | |
|
||||
| 65.| **网宿授权** | |
|
||||
| 66.| **西部数码授权** | |
|
||||
| 67.| **我爱云授权** | 我爱云CDN |
|
||||
| 68.| **新网授权(代理方式)** | |
|
||||
| 69.| **新网授权** | |
|
||||
| 70.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||
| 71.| **Zenlayer授权** | Zenlayer授权 |
|
||||
| 72.| **GoEdge授权** | |
|
||||
| 73.| **雨云授权** | https://app.rainyun.com/ |
|
||||
|
||||
<style module>
|
||||
table th:first-of-type {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 任务插件
|
||||
共 `128` 款任务插件
|
||||
共 `129` 款任务插件
|
||||
## 1. 证书申请
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
@@ -155,8 +155,9 @@
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **百度云-部署证书到负载均衡** | 部署到百度云负载均衡,包括BLB、APPBLB |
|
||||
| 2.| **百度云-部署证书到CDN** | 部署到百度云CDN |
|
||||
| 3.| **百度云-上传到证书托管** | 上传证书到百度云证书托管中心 |
|
||||
| 2.| **百度云-部署到CCE** | 部署到百度云CCE集群Ingress等通过Secret管理证书的应用 |
|
||||
| 3.| **百度云-部署证书到CDN** | 部署到百度云CDN |
|
||||
| 4.| **百度云-上传到证书托管** | 上传证书到百度云证书托管中心 |
|
||||
## 12. 七牛云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
| 16.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
|
||||
| 17.| **西部数码** | west dns provider |
|
||||
| 18.| **Dns提供商Demo** | dns provider示例 |
|
||||
| 19.| **51dns** | 51DNS |
|
||||
| 20.| **新网互联** | 新网互联 |
|
||||
| 19.| **彩虹DNS** | 彩虹DNS管理系统 |
|
||||
| 20.| **Spaceship** | Spaceship 域名解析 |
|
||||
| 21.| **51dns** | 51DNS |
|
||||
| 22.| **新网互联** | 新网互联 |
|
||||
|
||||
<style module>
|
||||
table th:first-of-type {
|
||||
|
||||
40
docs/guide/use/mode/enterprise.md
Normal file
40
docs/guide/use/mode/enterprise.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 企业模式(项目管理)
|
||||
|
||||
## 模式简介
|
||||
Certd支持两种管理模式,`SaaS模式(默认)`和`企业模式`。
|
||||
|
||||
|
||||

|
||||
|
||||
## SaaS模式
|
||||
* 默认的模式,每个用户管理自己的流水线和授权资源,每个用户独立使用。
|
||||
* Certd系统作为SaaS提供证书自动申请部署服务,您的客户注册即可使用,无需自己部署
|
||||
|
||||
|
||||
## 企业模式
|
||||
|
||||
* 通过项目合作管理流水线证书和授权资源,所有用户视为企业内部员工。
|
||||
|
||||
* 当你想在企业内部使用,企业内部有多个项目,各个项目成员共同管理项目资源和证书时可以启用此模式
|
||||
|
||||
* 需要在"系统设置->管理模式"中开启`企业模式`
|
||||
|
||||

|
||||
|
||||
|
||||
::: warning
|
||||
* 建议在开始使用时固定一个合适的模式,之后就不要随意切换了。
|
||||
* 商业版不能使用企业模式,因为商业版提供功能价值在于SaaS服务,与企业模式冲突
|
||||
:::
|
||||
|
||||
### 数据迁移
|
||||
模式之间数据不互通,您可以通过个人数据迁移功能将数据转到项目之下
|
||||
|
||||
#### 个人数据迁移到项目
|
||||
注意:此操作不可逆,请谨慎操作
|
||||

|
||||
|
||||
#### 流水线数据转到其他项目
|
||||
项目之间流水线数据可以转移,依赖的授权数据会同步复制一份
|
||||
|
||||

|
||||
BIN
docs/guide/use/mode/images/admin_mode.jpg
Normal file
BIN
docs/guide/use/mode/images/admin_mode.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/guide/use/mode/images/move.png
Normal file
BIN
docs/guide/use/mode/images/move.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
docs/guide/use/mode/images/transfer.jpg
Normal file
BIN
docs/guide/use/mode/images/transfer.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.39.7"
|
||||
"version": "1.39.10"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/publishlab/node-acme-client/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.39.9](https://github.com/publishlab/node-acme-client/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.39.8](https://github.com/publishlab/node-acme-client/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.39.7](https://github.com/publishlab/node-acme-client/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"module": "scr/index.js",
|
||||
"main": "src/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -70,5 +70,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线修改编辑之后,增加未保存提示 ([21620ac](https://github.com/certd/certd/commit/21620ac6bdeb57e43509156a77037fc07c44282a))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **spaceship:** 新增Spaceship DNS插件和授权模块 ([21aec77](https://github.com/certd/certd/commit/21aec77e5c3307b5973d4185baba33edcb28926f))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
@@ -1 +1 @@
|
||||
01:02
|
||||
23:43
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -47,5 +47,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
||||
if (config.skipSslVerify || config.httpProxy) {
|
||||
let rejectUnauthorized = true;
|
||||
if (config.skipSslVerify) {
|
||||
logger.info("跳过SSL验证");
|
||||
logger.info("忽略接口请求的SSL校验");
|
||||
rejectUnauthorized = false;
|
||||
}
|
||||
const proxy: any = {};
|
||||
@@ -271,7 +271,7 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
||||
}
|
||||
|
||||
const originalRequest = error.config || {};
|
||||
logger.info(`config`, originalRequest);
|
||||
// logger.info(`config`, originalRequest);
|
||||
const retry = originalRequest.retry || {};
|
||||
if (retry.status && retry.status.includes(status)) {
|
||||
if (retry.max > 0 && retry.count < retry.max) {
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 支持域名到期时间监控通知 ([c6628e7](https://github.com/certd/certd/commit/c6628e7311d6c43c2a784581fb25ec37b29c168d))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
|
||||
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -18,8 +18,8 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/plus-core": "^1.39.7",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@certd/plus-core": "^1.39.10",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -45,5 +45,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { IEmailService } from "../service/index.js";
|
||||
|
||||
export type NotificationBody = {
|
||||
userId?: number;
|
||||
projectId?: number;
|
||||
title: string;
|
||||
content: string;
|
||||
pipeline?: Pipeline;
|
||||
@@ -20,6 +21,7 @@ export type NotificationBody = {
|
||||
pipelineResult?: string;
|
||||
pipelineTitle?: string;
|
||||
errors?: string;
|
||||
[key: string]: any; // 其他templateData
|
||||
};
|
||||
|
||||
export type NotificationRequestHandleReqInput<T = any> = {
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Registrable } from "../registry/index.js";
|
||||
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
|
||||
import { FileStore } from "../core/file-store.js";
|
||||
import { accessRegistry, IAccessService } from "../access/index.js";
|
||||
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
|
||||
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
|
||||
import { domainUtils, HttpRequestConfig, ILogger, logger, optionsUtils, utils } from "@certd/basic";
|
||||
import { HttpClient } from "@certd/basic";
|
||||
import { domainUtils, HttpClient, HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
|
||||
import dayjs from "dayjs";
|
||||
import { IPluginConfigService } from "../service/config.js";
|
||||
import { cloneDeep, upperFirst } from "lodash-es";
|
||||
import { INotificationService } from "../notification/index.js";
|
||||
import { TaskEmitter } from "../service/emit.js";
|
||||
import { accessRegistry, IAccessService } from "../access/index.js";
|
||||
import { PageSearch } from "../context/index.js";
|
||||
import { FileStore } from "../core/file-store.js";
|
||||
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
|
||||
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
|
||||
import { INotificationService } from "../notification/index.js";
|
||||
import { Registrable } from "../registry/index.js";
|
||||
import { IPluginConfigService } from "../service/config.js";
|
||||
import { TaskEmitter } from "../service/emit.js";
|
||||
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
|
||||
|
||||
export type PluginRequestHandleReq<T = any> = {
|
||||
typeName: string;
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -24,5 +24,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -31,5 +31,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -56,5 +56,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -14,10 +14,11 @@
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"pub": "npm publish"
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -32,5 +33,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ export class K8sClient {
|
||||
const yml = loadYaml<KubernetesObject>(manifest);
|
||||
const client = this.getKubeClient();
|
||||
try {
|
||||
this.logger.info("apply yaml:", yml);
|
||||
await client.create(yml);
|
||||
} catch (e) {
|
||||
this.logger.error("apply error", e.response?.body);
|
||||
if (e.response?.body?.reason === "AlreadyExists") {
|
||||
//patch
|
||||
this.logger.info("patch existing resource: ", yml.metadata?.name);
|
||||
@@ -70,13 +70,26 @@ export class K8sClient {
|
||||
yml.metadata = {};
|
||||
}
|
||||
yml.metadata.resourceVersion = existing.body.metadata.resourceVersion;
|
||||
await client.patch(yml);
|
||||
return;
|
||||
const res = await client.patch(yml);
|
||||
return res?.body;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async applyPatch(manifest: string) {
|
||||
const yml = loadYaml<KubernetesObject>(manifest);
|
||||
const client = this.getKubeClient();
|
||||
this.logger.info("patch yaml:", yml);
|
||||
const existing = await client.read(yml as any);
|
||||
if (!yml.metadata) {
|
||||
yml.metadata = {};
|
||||
}
|
||||
yml.metadata.resourceVersion = existing.body.metadata.resourceVersion;
|
||||
const res = await client.patch(yml);
|
||||
return res?.body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param localRecords { [domain]:{ip:'xxx.xx.xxx'} }
|
||||
@@ -112,6 +125,7 @@ export class K8sClient {
|
||||
*/
|
||||
async createSecret(opts: { namespace: string; body: V1Secret }) {
|
||||
const namespace = opts.namespace || "default";
|
||||
this.logger.info("create secret:", opts.body.metadata);
|
||||
const created = await this.client.createNamespacedSecret(namespace, opts.body);
|
||||
this.logger.info("new secrets:", opts.body.metadata);
|
||||
return created.body;
|
||||
@@ -152,6 +166,8 @@ export class K8sClient {
|
||||
this.logger.info(`secret ${secretName} 已创建`);
|
||||
return res;
|
||||
}
|
||||
|
||||
throw new Error(`secret ${secretName} 不存在`);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复自定义插件删除后没有反注册的bug ([df98463](https://github.com/certd/certd/commit/df9846332596d2afaba53e66d2897aa1c598f9c4))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -28,11 +28,11 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.39.7",
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/pipeline": "^1.39.7",
|
||||
"@certd/plugin-lib": "^1.39.7",
|
||||
"@certd/plus-core": "^1.39.7",
|
||||
"@certd/acme-client": "^1.39.10",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@certd/pipeline": "^1.39.10",
|
||||
"@certd/plugin-lib": "^1.39.10",
|
||||
"@certd/plus-core": "^1.39.10",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
"@midwayjs/core": "3.20.11",
|
||||
"@midwayjs/i18n": "3.20.13",
|
||||
@@ -64,5 +64,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -76,10 +76,12 @@ export abstract class BaseService<T> {
|
||||
* @param where
|
||||
*/
|
||||
async delete(ids: string | any[], where?: any) {
|
||||
const idArr = this.resolveIdArr(ids);
|
||||
let idArr = this.resolveIdArr(ids);
|
||||
idArr = this.filterIds(idArr);
|
||||
if (idArr.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getRepository().delete({
|
||||
id: In(idArr),
|
||||
...where,
|
||||
@@ -94,7 +96,9 @@ export abstract class BaseService<T> {
|
||||
}
|
||||
if (typeof ids === 'string') {
|
||||
return ids.split(',');
|
||||
} else {
|
||||
} else if(!Array.isArray(ids)){
|
||||
return [ids];
|
||||
}else {
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +221,7 @@ export abstract class BaseService<T> {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
ids = this.filterIds(ids);
|
||||
const res = await this.getRepository().find({
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -234,7 +239,16 @@ export abstract class BaseService<T> {
|
||||
throw new PermissionException('权限不足');
|
||||
}
|
||||
|
||||
filterIds(ids: any[]) {
|
||||
if (!ids) {
|
||||
throw new ValidateException('ids不能为空');
|
||||
}
|
||||
return ids.filter((item) => {
|
||||
return item!=null && item != ""
|
||||
});
|
||||
}
|
||||
async batchDelete(ids: number[], userId: number,projectId?:number) {
|
||||
ids = this.filterIds(ids);
|
||||
if(userId!=null){
|
||||
const list = await this.getRepository().find({
|
||||
where: {
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -46,5 +46,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -17,10 +17,10 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.39.7",
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/pipeline": "^1.39.7",
|
||||
"@certd/plugin-lib": "^1.39.7",
|
||||
"@certd/acme-client": "^1.39.10",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@certd/pipeline": "^1.39.10",
|
||||
"@certd/plugin-lib": "^1.39.10",
|
||||
"psl": "^1.9.0",
|
||||
"punycode.js": "^2.3.1"
|
||||
},
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 1panel支持先上传证书再选择证书 ([7a9eec8](https://github.com/certd/certd/commit/7a9eec88e8eddf40dba055c072b5b2b0f67c1407))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -22,10 +22,10 @@
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@alicloud/tea-util": "^1.4.11",
|
||||
"@aws-sdk/client-s3": "^3.964.0",
|
||||
"@certd/acme-client": "^1.39.7",
|
||||
"@certd/basic": "^1.39.7",
|
||||
"@certd/pipeline": "^1.39.7",
|
||||
"@certd/plus-core": "^1.39.7",
|
||||
"@certd/acme-client": "^1.39.10",
|
||||
"@certd/basic": "^1.39.10",
|
||||
"@certd/pipeline": "^1.39.10",
|
||||
"@certd/plus-core": "^1.39.10",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.22.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
@@ -57,5 +57,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import { ILogger } from "@certd/basic";
|
||||
import dayjs from "dayjs";
|
||||
import { uniq } from "lodash-es";
|
||||
|
||||
export interface ICertInfoGetter {
|
||||
getByPipelineId: (pipelineId: number) => Promise<CertInfo>;
|
||||
}
|
||||
export type CertInfo = {
|
||||
crt: string; //fullchain证书
|
||||
key: string; //私钥
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export const CertApplyPluginNames = [":cert:"];
|
||||
export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
|
||||
export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./convert.js";
|
||||
export * from "./cert-reader.js";
|
||||
export * from "./consts.js";
|
||||
export * from "./dns-provider/index.js";
|
||||
export * from "./dns-provider/index.js";
|
||||
|
||||
@@ -44,6 +44,10 @@ export function createRemoteSelectInputDefine(opts?: {
|
||||
component?: any;
|
||||
value?: any;
|
||||
pageSize?: number;
|
||||
uploadCert?: {
|
||||
title?: string;
|
||||
columns?: Record<string, any>;
|
||||
};
|
||||
}) {
|
||||
const title = opts?.title || "请选择";
|
||||
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
|
||||
@@ -74,6 +78,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
||||
multi,
|
||||
pageSize: opts?.pageSize,
|
||||
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
||||
uploadCert: opts?.uploadCert,
|
||||
...opts.component,
|
||||
},
|
||||
value: opts.value,
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复创建流水线无法选择通知的bug ([a88d0a6](https://github.com/certd/certd/commit/a88d0a6ae15cb6170d0b36e21daf89f0dbd5f681))
|
||||
* 修复流水线任务编辑页面复制粘贴按钮在夜间模式显示问题 ([1e549df](https://github.com/certd/certd/commit/1e549dfd431ed74e2bcdfce63e5f640c51603af3))
|
||||
* 修复用户管理添加用户无法上传头像的bug ([557e98c](https://github.com/certd/certd/commit/557e98c33f5462167d8f6289f70dad68bb114a97))
|
||||
* 修复自定义插件删除后没有反注册的bug ([df98463](https://github.com/certd/certd/commit/df9846332596d2afaba53e66d2897aa1c598f9c4))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 1panel支持先上传证书再选择证书 ([7a9eec8](https://github.com/certd/certd/commit/7a9eec88e8eddf40dba055c072b5b2b0f67c1407))
|
||||
* 流水线修改编辑之后,增加未保存提示 ([21620ac](https://github.com/certd/certd/commit/21620ac6bdeb57e43509156a77037fc07c44282a))
|
||||
* 修复检查全部某些情况下无效的bug,优化公共触发站点证书检查定时逻辑 ([ee53589](https://github.com/certd/certd/commit/ee535895a3166c6f9046963e28fa8f22f018b574))
|
||||
* 增加域名管理 子域名检查提醒 ([2bdf183](https://github.com/certd/certd/commit/2bdf1832da73a3728f3ac415837bc26e70531cd6))
|
||||
* 站点监控域名气泡增加端口显示 ([6ee718a](https://github.com/certd/certd/commit/6ee718a25265a9db2115343af9a1a01958f34b81))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复某些情况下报无法修改通知的问题 ([d1a6592](https://github.com/certd/certd/commit/d1a65922d7e152d6edcf6c53b70079f16b54a0d3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 支持域名到期时间监控通知 ([c6628e7](https://github.com/certd/certd/commit/c6628e7311d6c43c2a784581fb25ec37b29c168d))
|
||||
* **monitor:** 支持查看监控执行记录 ([b5cc794](https://github.com/certd/certd/commit/b5cc794061c11b7200b669473c25c4bbfc944b61))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.39.7",
|
||||
"version": "1.39.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -106,8 +106,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.39.7",
|
||||
"@certd/pipeline": "^1.39.7",
|
||||
"@certd/lib-iframe": "^1.39.10",
|
||||
"@certd/pipeline": "^1.39.10",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -13,13 +13,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import parser from "cron-parser";
|
||||
import { computed, ref } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { getCronNextTimes } from "/@/components/cron-editor/utils";
|
||||
|
||||
const { t } = useI18n();
|
||||
import { getCronNextTimes } from "/@/components/cron-editor/utils";
|
||||
defineOptions({
|
||||
name: "CronEditor",
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="domains-verify-plan-editor" :class="{ fullscreen }">
|
||||
<div class="fullscreen-modal" @click="fullscreenExit"></div>
|
||||
<div class="plan-wrapper">
|
||||
<div class="plan-box">
|
||||
<div class="plan-box bg-white dark:bg-neutral-700">
|
||||
<div class="fullscreen-button pointer flex-center" @click="fullscreen = !fullscreen">
|
||||
<span v-if="!fullscreen" style="font-size: 10px" class="flex-center">
|
||||
这里可以放大
|
||||
@@ -273,7 +273,7 @@ watch(
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(74, 74, 74, 0.78);
|
||||
// background-color: rgba(74, 74, 74, 0.78);
|
||||
z-index: 1000;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
@@ -287,7 +287,6 @@ watch(
|
||||
.plan-box {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +314,7 @@ watch(
|
||||
height: 100%;
|
||||
//table-layout: fixed;
|
||||
th {
|
||||
background-color: #f5f5f5;
|
||||
// background-color: #f5f5f5;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
border-left: 1px solid #e8e8e8;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
async function onCreate() {
|
||||
await pluginStore.init();
|
||||
options.value = pluginStore.group.getPreStepOutputOptions({
|
||||
pipeline: pipeline.value,
|
||||
pipeline: pipeline?.value,
|
||||
currentStageIndex: currentStageIndex.value,
|
||||
currentTaskIndex: currentTaskIndex.value,
|
||||
currentStepIndex: currentStepIndex.value,
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
</div>
|
||||
</template>
|
||||
</a-select>
|
||||
<div class="ml-5">
|
||||
<div class="ml-5 flex flex-row no-wrap">
|
||||
<fs-button :loading="loading" title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||
<UploadCert v-if="uploadCert" class="ml-5" v-bind="uploadCert" @submit="refreshOptions"></UploadCert>
|
||||
</div>
|
||||
</div>
|
||||
<div class="helper" :class="{ error: hasError }">
|
||||
@@ -39,6 +40,8 @@ import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||
import { PluginDefine } from "@certd/pipeline";
|
||||
import { getInputFromForm } from "./utils";
|
||||
import UploadCert from "./upload-cert.vue";
|
||||
import { UploadCertProps } from "./types";
|
||||
|
||||
defineOptions({
|
||||
name: "RemoteSelect",
|
||||
@@ -65,9 +68,10 @@ const props = defineProps<
|
||||
pager?: boolean;
|
||||
multi?: boolean;
|
||||
pageSize?: number;
|
||||
uploadCert?: UploadCertProps;
|
||||
} & ComponentPropsType
|
||||
>();
|
||||
|
||||
debugger;
|
||||
const emit = defineEmits<{
|
||||
"update:value": any;
|
||||
}>();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface UploadCertProps {
|
||||
title?: string;
|
||||
columns?: Record<string, any>;
|
||||
button?: any;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="upload-cert">
|
||||
<fs-button v-model:loading="loading" type="primary" text="上传" v-bind="props.button" @click="openUploadCertDialog"></fs-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { message } from "ant-design-vue";
|
||||
import { useFormDialog } from "../../../use/use-dialog";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { doRequest } from "../lib";
|
||||
import { getInputFromForm } from "./utils";
|
||||
import { UploadCertProps } from "./types";
|
||||
import { merge } from "lodash-es";
|
||||
|
||||
const props = defineProps<UploadCertProps>();
|
||||
const loading = ref(false);
|
||||
|
||||
const emit = defineEmits(["submit"]);
|
||||
const { openFormDialog } = useFormDialog();
|
||||
const pipeline = inject("pipeline", null);
|
||||
|
||||
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine", () => {
|
||||
return {};
|
||||
});
|
||||
const getScope: any = inject("get:scope", () => {
|
||||
return {};
|
||||
});
|
||||
const getPluginType: any = inject("get:plugin:type", () => {
|
||||
return "plugin";
|
||||
});
|
||||
const title = computed(() => props.title || "上传证书");
|
||||
function openUploadCertDialog() {
|
||||
const columns = merge(
|
||||
{
|
||||
certName: {
|
||||
title: "证书名称",
|
||||
form: {
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
helper: "上传后证书显示名称",
|
||||
},
|
||||
},
|
||||
},
|
||||
props.columns
|
||||
);
|
||||
openFormDialog({
|
||||
title: title.value,
|
||||
columns: {
|
||||
certName: {
|
||||
title: "证书名称",
|
||||
form: {
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
...props.columns,
|
||||
},
|
||||
onSubmit: async (form: any) => {
|
||||
const pluginType = getPluginType();
|
||||
const scope = getScope();
|
||||
const { input, record } = getInputFromForm(scope.form, pluginType);
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await doRequest(
|
||||
{
|
||||
type: pluginType,
|
||||
typeName: scope.form.type,
|
||||
action: "onUploadCert",
|
||||
input,
|
||||
record,
|
||||
data: {
|
||||
pipelineId: pipeline?.value?.id,
|
||||
...form,
|
||||
},
|
||||
},
|
||||
{
|
||||
// onError(err: any) {
|
||||
// message.error(err.message);
|
||||
// },
|
||||
showErrorNotify: true,
|
||||
}
|
||||
);
|
||||
message.success("上传成功");
|
||||
emit("submit");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.upload-cert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="step-item overflow-hidden">
|
||||
<div class="text">
|
||||
<h3 class="title">{{ number }} {{ currentStepItem.title }}</h3>
|
||||
<h3 class="title font-bold">{{ number }} {{ currentStepItem.title }}</h3>
|
||||
<div class="description mt-5">
|
||||
<div v-for="(desc, index) of currentStepItem.descriptions" :key="index">{{ desc }}</div>
|
||||
</div>
|
||||
@@ -247,6 +247,7 @@ function previewMask() {
|
||||
|
||||
<style lang="less">
|
||||
.tutorial-steps {
|
||||
display: flex;
|
||||
.step-item {
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -251,7 +251,7 @@ function openUpgrade() {
|
||||
class: "vip-modal",
|
||||
maskClosable: true,
|
||||
okText: t("vip.close"),
|
||||
width: 1100,
|
||||
width: 1180,
|
||||
content: () => {
|
||||
return <VipModalContent placeholder={placeholder} isPlus={isPlus} productInfo={productInfo} goBuyPlusPage={goBuyPlusPage} goBuyCommPage={goBuyCommPage} openStarModal={openStarModal} modalRef={modalRef} />;
|
||||
},
|
||||
|
||||
@@ -65,6 +65,11 @@
|
||||
</div>
|
||||
<div class="get-show">
|
||||
<template v-if="item.type === 'plus'">
|
||||
<span v-if="todayOrderCount.showVipTotal" class="mr-5">
|
||||
已有
|
||||
<span class="color-red"> {{ todayOrderCount.vipTotal }}</span>
|
||||
位伙伴支持
|
||||
</span>
|
||||
<a-tooltip :title="t('vip.afdian_support_vip')">
|
||||
<a-button size="small" type="primary" @click="goBuyPlusPage">
|
||||
{{ t("vip.get_after_support") }}
|
||||
@@ -204,13 +209,13 @@ const vipTypeDefine: any = {
|
||||
desc: t("vip.community_free_version"),
|
||||
type: "free",
|
||||
icon: "lucide:package-open",
|
||||
privilege: [t("vip.unlimited_certificate_application"), t("vip.unlimited_domain_count"), t("vip.unlimited_certificate_pipelines"), t("vip.common_deployment_plugins"), t("vip.email_webhook_notifications")],
|
||||
privilege: t("vip.free_privilege").split("\n"),
|
||||
},
|
||||
plus: {
|
||||
title: t("vip.professional_edition"),
|
||||
desc: t("vip.open_source_support"),
|
||||
type: "plus",
|
||||
privilege: [t("vip.vip_group_priority"), t("vip.unlimited_site_certificate_monitoring"), t("vip.more_notification_methods"), t("vip.plugins_fully_open")],
|
||||
privilege: t("vip.plus_privilege").split("\n"),
|
||||
trial: {
|
||||
title: t("vip.click_to_get_7_day_trial"),
|
||||
click: () => {
|
||||
@@ -227,7 +232,7 @@ const vipTypeDefine: any = {
|
||||
desc: t("vip.commercial_license"),
|
||||
type: "comm",
|
||||
icon: "vaadin:handshake",
|
||||
privilege: [t("vip.all_pro_privileges"), t("vip.allow_commercial_use_modify_logo_title"), t("vip.data_statistics"), t("vip.plugin_management"), t("vip.unlimited_multi_users"), t("vip.support_user_payment")],
|
||||
privilege: t("vip.comm_privilege").split("\n"),
|
||||
priceText: props.productInfo.comm.priceText || `¥${props.productInfo.comm.price}/${t("vip.years")}`,
|
||||
discountText: props.productInfo.comm.discountText || `¥${props.productInfo.comm.price3}/3${t("vip.years")}`,
|
||||
tooltip: props.productInfo.comm.tooltip,
|
||||
@@ -260,12 +265,16 @@ const todayOrderCount = computed(() => {
|
||||
const lastStage = countInfo?.stages?.[countInfo?.stages?.length - 1] || {};
|
||||
lastStage.orderCount = orderCount;
|
||||
|
||||
const vipTotal = countInfo?.vipTotal || 0;
|
||||
const showVipTotal = countInfo?.showVipTotal || false;
|
||||
const userTotal = countInfo?.userTotal || 0;
|
||||
const stages: any = [];
|
||||
stages.push({
|
||||
title: countInfo.title,
|
||||
vipTotal: countInfo?.vipTotal || 0,
|
||||
orderCount: orderCount,
|
||||
bg: lastStage.bg,
|
||||
showVipTotal: showVipTotal,
|
||||
});
|
||||
if (lastStage.orderCount > 0) {
|
||||
stages.push(lastStage);
|
||||
@@ -273,6 +282,9 @@ const todayOrderCount = computed(() => {
|
||||
return {
|
||||
enabled: enabled,
|
||||
stages: stages,
|
||||
showVipTotal: showVipTotal,
|
||||
vipTotal: vipTotal,
|
||||
userTotal: userTotal,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ export default {
|
||||
projectJoin: "Join Project",
|
||||
currentProject: "Current Project",
|
||||
projectMemberManager: "Project Member",
|
||||
domainMonitorSetting: "Domain Monitor Settings",
|
||||
},
|
||||
certificateRepo: {
|
||||
title: "Certificate Repository",
|
||||
|
||||
@@ -64,10 +64,41 @@ export default {
|
||||
dnsServerHelper: "Use a custom domain name resolution server, such as: 1.1.1.1 , support multiple",
|
||||
certValidDays: "Certificate Valid Days",
|
||||
certValidDaysHelper: "Number of days before expiration to send a notification",
|
||||
|
||||
domain: {
|
||||
monitorSettings: "Domain Monitor Settings",
|
||||
enabled: "Enable Domain Monitor",
|
||||
enabledHelper: "Enable to monitor all domain registration expiration time",
|
||||
notificationChannel: "Notification Channel",
|
||||
setNotificationChannel: "Set the notification channel",
|
||||
willExpireDays: "Will Expire Days",
|
||||
willExpireDaysHelper: "Number of days before expiration to send a notification",
|
||||
monitorCronSetting: "Monitoring Schedule",
|
||||
cronTrigger: "Scheduled trigger for monitoring",
|
||||
},
|
||||
},
|
||||
cert: {
|
||||
expired: "Expired",
|
||||
expiring: "Expiring",
|
||||
noExpired: "Not Expired",
|
||||
},
|
||||
history: {
|
||||
title: "Monitoring Execution Records",
|
||||
description: "Monitoring execution records",
|
||||
resultTitle: "Status",
|
||||
contentTitle: "Content",
|
||||
titleTitle: "Title",
|
||||
jobTypeTitle: "Job Type",
|
||||
startAtTitle: "Start Time",
|
||||
endAtTitle: "End Time",
|
||||
jobResultTitle: "Result",
|
||||
jobResult: {
|
||||
done: "Done",
|
||||
start: "Start",
|
||||
},
|
||||
jobType: {
|
||||
domainExpirationCheck: "Domain Expiration Check",
|
||||
siteCertMonitor: "Site Certificate Monitor",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ export default {
|
||||
unlimited_certificate_pipelines: "Unlimited certificate pipelines",
|
||||
common_deployment_plugins: "Common host, cloud platform, CDN, Baota, 1Panel deployment plugins",
|
||||
email_webhook_notifications: "Email, webhook notification methods",
|
||||
free_privilege: "Unlimited certificate applications\nUnlimited domain count\nUnlimited certificate pipelines\nCommon host, cloud platform, CDN, Baota, 1Panel deployment plugins\nEmail, webhook notification methods",
|
||||
|
||||
professional_edition: "Professional Edition",
|
||||
open_source_support: "Open source requires your sponsorship support",
|
||||
@@ -60,10 +61,11 @@ export default {
|
||||
unlimited_site_certificate_monitoring: "Unlimited site certificate monitoring",
|
||||
more_notification_methods: "More notification methods",
|
||||
plugins_fully_open: "All plugins open, including Synology and more",
|
||||
plus_privilege: "Access to VIP group, your requests will have priority\nUnlimited site certificate monitoring\nMore notification methods\nAll plugins open, including Synology and more",
|
||||
click_to_get_7_day_trial: "Click to get 7-day trial",
|
||||
years: "years",
|
||||
afdian_support_vip: "Obtain the permanent professional version coupon",
|
||||
get_after_support: "Get after sponsoring",
|
||||
get_after_support: "sponsoring",
|
||||
|
||||
business_edition: "Business Edition",
|
||||
commercial_license: "Commercial license, allowed for external operation",
|
||||
@@ -73,6 +75,7 @@ export default {
|
||||
plugin_management: "Plugin management",
|
||||
unlimited_multi_users: "Unlimited multi-users",
|
||||
support_user_payment: "Supports user payments",
|
||||
comm_privilege: "All professional edition privileges\nAllows commercial use, can modify logo and title\nData statistics\nPlugin management\nUnlimited multi-users\nSupports user payments",
|
||||
contact_author_for_trial: "Buy It Now",
|
||||
activate: "Activate",
|
||||
get_pro_code_after_support: "Go to sponsoring",
|
||||
|
||||
@@ -226,6 +226,8 @@ export default {
|
||||
projectJoin: "加入项目",
|
||||
currentProject: "当前项目",
|
||||
projectMemberManager: "项目成员管理",
|
||||
domainMonitorSetting: "域名监控设置",
|
||||
jobHistory: "监控执行记录",
|
||||
},
|
||||
certificateRepo: {
|
||||
title: "证书仓库",
|
||||
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
activateCertDesc2: "让证书生效",
|
||||
taskSuccessTitle: "部署任务添加成功",
|
||||
taskSuccessDesc: "现在可以运行",
|
||||
pluginsTitle: "本系统提供茫茫多的部署插件",
|
||||
pluginsTitle: "本系统提供海量的部署插件",
|
||||
pluginsDesc: "您可以根据自身需求将证书部署到各种应用和平台",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -68,10 +68,40 @@ export default {
|
||||
dnsServerHelper: "使用自定义的域名解析服务器,如:1.1.1.1 , 支持多个",
|
||||
certValidDays: "证书到期前天数",
|
||||
certValidDaysHelper: "证书到期前多少天发送通知",
|
||||
domain: {
|
||||
monitorSettings: "域名监控设置",
|
||||
enabled: "启用域名监控",
|
||||
enabledHelper: "启用后,监控“域名管理”中域名的过期时间,到期前通知提醒",
|
||||
notificationChannel: "通知渠道",
|
||||
setNotificationChannel: "设置通知渠道",
|
||||
willExpireDays: "到期前天数",
|
||||
willExpireDaysHelper: "域名有效期到期前多少天发送通知",
|
||||
monitorCronSetting: "监控定时设置",
|
||||
cronTrigger: "定时触发监控",
|
||||
},
|
||||
},
|
||||
cert: {
|
||||
expired: "已过期",
|
||||
expiring: "即将过期",
|
||||
noExpired: "未过期",
|
||||
},
|
||||
history: {
|
||||
title: "监控执行记录",
|
||||
description: "站点证书、域名等监控任务的执行记录",
|
||||
resultTitle: "状态",
|
||||
contentTitle: "内容",
|
||||
titleTitle: "标题",
|
||||
jobTypeTitle: "任务类型",
|
||||
startAtTitle: "开始时间",
|
||||
endAtTitle: "结束时间",
|
||||
jobResultTitle: "任务结果",
|
||||
jobResult: {
|
||||
done: "完成",
|
||||
start: "开始",
|
||||
},
|
||||
jobType: {
|
||||
domainExpirationCheck: "域名到期检查",
|
||||
siteCertMonitor: "站点证书监控",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ export default {
|
||||
unlimited_certificate_pipelines: "证书流水线数量无限制",
|
||||
common_deployment_plugins: "常用的主机、云平台、cdn、宝塔、1Panel等部署插件",
|
||||
email_webhook_notifications: "邮件、webhook通知方式",
|
||||
free_privilege: "证书申请无限制\n域名数量无限制\n证书流水线数量无限制\n常用的主机、云平台、cdn、宝塔、1Panel等部署插件\n邮件、webhook通知方式",
|
||||
|
||||
professional_edition: "专业版",
|
||||
open_source_support: "开源需要您的赞助支持,个人和企业内部使用",
|
||||
@@ -60,6 +61,7 @@ export default {
|
||||
unlimited_site_certificate_monitoring: "站点证书监控无限制",
|
||||
more_notification_methods: "更多通知方式",
|
||||
plugins_fully_open: "插件全开放,群晖等更多插件",
|
||||
plus_privilege: "可加VIP群,您的需求将优先实现\n站点证书监控无限制\n更多通知方式\n插件全开放,群晖等更多插件\n企业模式,项目管理\n域名到期监控\n第三方登录,PassKey登录",
|
||||
click_to_get_7_day_trial: "点击获取7天试用",
|
||||
years: "年",
|
||||
afdian_support_vip: "新用户开通永久专业版立享50优惠券",
|
||||
@@ -73,6 +75,7 @@ export default {
|
||||
plugin_management: "插件管理",
|
||||
unlimited_multi_users: "多用户无限制",
|
||||
support_user_payment: "支持用户支付(购买套餐,按流水线条数、域名数量、部署次数计费)",
|
||||
comm_privilege: "拥有专业版所有特权\n允许商用,可修改logo、标题\n数据统计\n插件管理\n多用户无限制\n支持用户支付(购买套餐,按流水线条数、域名数量、部署次数计费)",
|
||||
activate: "激活",
|
||||
get_pro_code_after_support: "前往获取",
|
||||
business_contact_author: "",
|
||||
|
||||
@@ -241,6 +241,28 @@ export const certdResources = [
|
||||
isMenu: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.domainMonitorSetting",
|
||||
name: "DomainMonitorSetting",
|
||||
path: "/certd/cert/domain/setting",
|
||||
component: "/certd/cert/domain/setting/index.vue",
|
||||
meta: {
|
||||
icon: "ion:stopwatch-outline",
|
||||
auth: true,
|
||||
isMenu: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.jobHistory",
|
||||
name: "JobHistory",
|
||||
path: "/certd/monitor/history",
|
||||
component: "/certd/monitor/history/index.vue",
|
||||
meta: {
|
||||
icon: "ion:barcode-outline",
|
||||
auth: true,
|
||||
isMenu: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.userSecurity",
|
||||
name: "UserSecurity",
|
||||
|
||||
@@ -130,4 +130,16 @@ button.ant-btn.ant-btn-default.isPlus{
|
||||
background-color: rgba(50, 54, 57, 0.04);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dark{
|
||||
button.ant-btn.ant-btn-default.isPlus{
|
||||
color: #c5913f;
|
||||
border: 1px solid #c5913f;
|
||||
|
||||
&:disabled {
|
||||
border-color: hsl(0, 0%, 31%);
|
||||
color: rgba(233, 233, 233, 0.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ import { defineComponent, reactive, ref, watch, inject } from "vue";
|
||||
import CertAccessModal from "./access/index.vue";
|
||||
import { createAccessApi } from "../api";
|
||||
import { message } from "ant-design-vue";
|
||||
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
export default defineComponent({
|
||||
name: "AccessSelector",
|
||||
components: { CertAccessModal },
|
||||
@@ -71,11 +72,27 @@ export default defineComponent({
|
||||
emitValue(null);
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const projectStore = useProjectStore();
|
||||
|
||||
async function emitValue(value) {
|
||||
if (pipeline && pipeline?.value && target?.value && pipeline.value.userId !== target.value.userId) {
|
||||
message.error("对不起,您不能修改他人流水线的授权");
|
||||
return;
|
||||
const userId = userStore.userInfo.id;
|
||||
const isEnterprice = projectStore.isEnterprise;
|
||||
if (pipeline?.value) {
|
||||
if (isEnterprice) {
|
||||
const projectId = projectStore.currentProjectId;
|
||||
if (pipeline?.value?.projectId !== projectId) {
|
||||
message.error(`对不起,您不能修改其他项目流水线的授权`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (pipeline?.value && pipeline.value.userId !== userId) {
|
||||
message.error(`对不起,您不能修改他人流水线的授权`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
selectedId.value = "";
|
||||
target.value = null;
|
||||
|
||||
@@ -48,6 +48,7 @@ import createCrudOptions from "../crud";
|
||||
import { addonProvide } from "../common";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -127,13 +128,24 @@ function clear() {
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const projectStore = useProjectStore();
|
||||
async function emitValue(value: any) {
|
||||
// target.value = optionsDictRef.dataMap[value];
|
||||
const userId = userStore.userInfo.id;
|
||||
if (pipeline?.value && pipeline.value.userId !== userId) {
|
||||
message.error(`对不起,您不能修改他人流水线的${props.addonType}设置`);
|
||||
return;
|
||||
if (pipeline?.value) {
|
||||
const userId = userStore.userInfo.id;
|
||||
const isEnterprice = projectStore.isEnterprise;
|
||||
if (isEnterprice) {
|
||||
const projectId = projectStore.currentProjectId;
|
||||
if (pipeline?.value?.projectId !== projectId) {
|
||||
message.error(`对不起,您不能修改其他项目流水线的${props.addonType}设置`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (pipeline?.value && pipeline.value.userId !== userId) {
|
||||
message.error(`对不起,您不能修改他人流水线的${props.addonType}设置`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
emit("change", value);
|
||||
emit("update:modelValue", value);
|
||||
|
||||
@@ -99,3 +99,11 @@ export async function SyncExpirationStatus() {
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
export async function IsSubdomain(body: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/isSubdomain",
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
});
|
||||
|
||||
const openDomainImportManageDialog = useDomainImportManage();
|
||||
|
||||
const subdomainConfirmed = ref(false);
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
@@ -85,10 +87,29 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
fixed: "right",
|
||||
},
|
||||
form: {
|
||||
beforeSubmit({ form }) {
|
||||
async beforeSubmit({ form }) {
|
||||
if (form.challengeType === "cname") {
|
||||
throw new Error("CNAME方式请前往CNAME记录页面进行管理");
|
||||
}
|
||||
if (form.challengeType === "dns") {
|
||||
const isSubdomain = await api.IsSubdomain({ domain: form.domain });
|
||||
if (isSubdomain && !subdomainConfirmed.value) {
|
||||
Modal.confirm({
|
||||
title: "子域名确认",
|
||||
content: `检测到${form.domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?`,
|
||||
okText: "确认",
|
||||
okType: "danger",
|
||||
onOk: () => {
|
||||
subdomainConfirmed.value = true;
|
||||
crudExpose.getFormWrapperRef().submit();
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
afterSubmit({ form }) {
|
||||
subdomainConfirmed.value = false;
|
||||
},
|
||||
},
|
||||
actionbar: {
|
||||
@@ -128,6 +149,18 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}, 2000);
|
||||
},
|
||||
},
|
||||
monitorSettingSave: {
|
||||
show: hasActionPermission("write"),
|
||||
title: "域名过期监控设置",
|
||||
type: "primary",
|
||||
icon: "ion:save-outline",
|
||||
text: "域名过期监控设置",
|
||||
click: async () => {
|
||||
router.push({
|
||||
path: "/certd/cert/domain/setting",
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
@@ -151,6 +184,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
helper: "注意:DNS校验方式下,子域名不需要在此处维护,否则会影响证书申请(子域名托管或免费二级域名除外)",
|
||||
},
|
||||
editForm: {
|
||||
component: {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// @ts-ignore
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/cert/domain/setting";
|
||||
export type UserDomainMonitorSetting = {
|
||||
enabled?: boolean;
|
||||
notificationId?: number;
|
||||
cron?: string;
|
||||
willExpireDays?: number;
|
||||
};
|
||||
|
||||
export async function DomainMonitorSettingsGet() {
|
||||
const res = await request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "post",
|
||||
});
|
||||
if (!res) {
|
||||
return {};
|
||||
}
|
||||
return res as UserDomainMonitorSetting;
|
||||
}
|
||||
export async function DomainMonitorSettingsSave(data: UserDomainMonitorSetting) {
|
||||
await request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<fs-page class="page-user-settings page-domain-monitor-setting">
|
||||
<template #header>
|
||||
<div class="title">{{ t("monitor.setting.domain.monitorSettings") }}</div>
|
||||
</template>
|
||||
<div class="user-settings-form settings-form">
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
|
||||
<a-form-item :label="t('monitor.setting.domain.enabled')" :name="['enabled']">
|
||||
<div class="flex flex-baseline">
|
||||
<a-switch v-model:checked="formState.enabled" :disabled="!settingsStore.isPlus" />
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</div>
|
||||
<div class="helper">{{ t("monitor.setting.domain.enabledHelper") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="formState.enabled" :label="t('monitor.setting.domain.notificationChannel')" :name="['notificationId']">
|
||||
<div class="flex">
|
||||
<NotificationSelector v-model="formState.notificationId" />
|
||||
</div>
|
||||
<div class="helper">{{ t("monitor.setting.domain.setNotificationChannel") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="formState.enabled" :label="t('monitor.setting.domain.willExpireDays')" :name="['willExpireDays']">
|
||||
<div class="flex">
|
||||
<a-input-number v-model:value="formState.willExpireDays" />
|
||||
</div>
|
||||
<div class="helper">{{ t("monitor.setting.domain.willExpireDaysHelper") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="formState.enabled" :label="t('monitor.setting.domain.monitorCronSetting')" :name="['cron']">
|
||||
<div class="flex flex-baseline">
|
||||
<cron-editor v-model="formState.cron" :disabled="!settingsStore.isPlus" :allow-every-min="userStore.isAdmin" />
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</div>
|
||||
<div class="helper">{{ t("monitor.setting.domain.cronTrigger") }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||
<loading-button type="primary" html-type="button" :click="doSave" :disabled="!hasActionPermission('write')">{{ t("certd.save") }}</loading-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { notification } from "ant-design-vue";
|
||||
import { merge } from "lodash-es";
|
||||
import { reactive } from "vue";
|
||||
import * as api from "./api";
|
||||
import { UserDomainMonitorSetting } from "./api";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { utils } from "/@/utils";
|
||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { useSettingStore } from "/src/store/settings";
|
||||
import { useCrudPermission } from "/@/plugin/permission";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const settingsStore = useSettingStore();
|
||||
const userStore = useUserStore();
|
||||
defineOptions({
|
||||
name: "DomainMonitorSetting",
|
||||
});
|
||||
|
||||
const randomHour = Math.floor(Math.random() * 9);
|
||||
const randomMin = Math.floor(Math.random() * 59);
|
||||
const randomCron = `0 ${randomMin} ${randomHour} * * *`;
|
||||
|
||||
const formState = reactive<Partial<UserDomainMonitorSetting>>({
|
||||
enabled: false,
|
||||
notificationId: 0,
|
||||
cron: randomCron,
|
||||
willExpireDays: 30,
|
||||
});
|
||||
|
||||
async function loadUserSettings() {
|
||||
const data: any = await api.DomainMonitorSettingsGet();
|
||||
merge(formState, data);
|
||||
}
|
||||
|
||||
const { hasActionPermission } = useCrudPermission({ permission: { isProjectPermission: true } });
|
||||
|
||||
loadUserSettings();
|
||||
const doSave = async (form: any) => {
|
||||
await utils.sleep(300);
|
||||
await api.DomainMonitorSettingsSave({
|
||||
...formState,
|
||||
});
|
||||
notification.success({
|
||||
message: t("certd.saveSuccess"),
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.page-domain-monitor-setting {
|
||||
.settings-form {
|
||||
width: 700px;
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/monitor/job-history";
|
||||
|
||||
export const jobHistoryApi = {
|
||||
async GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query,
|
||||
});
|
||||
},
|
||||
|
||||
async DelObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
async BatchDelObj(ids: number[]) {
|
||||
return await request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids },
|
||||
});
|
||||
},
|
||||
|
||||
async GetObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,257 @@
|
||||
// @ts-ignore
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { ref } from "vue";
|
||||
import { createGroupDictRef } from "../../basic/group/api";
|
||||
import { useDicts } from "../../dicts";
|
||||
import { jobHistoryApi } from "./api";
|
||||
import { useCrudPermission } from "/@/plugin/permission";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
import { useI18n } from "/src/locales";
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const { t } = useI18n();
|
||||
const api = jobHistoryApi;
|
||||
const { crudBinding } = crudExpose;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async (req: EditReq) => {};
|
||||
const delRequest = async (req: DelReq) => {
|
||||
const { row } = req;
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async (req: AddReq) => {};
|
||||
const { myProjectDict } = useDicts();
|
||||
|
||||
const historyResultDict = dict({
|
||||
data: [
|
||||
{ label: t("monitor.history.jobResult.done"), value: "done", color: "green" },
|
||||
{ label: t("monitor.history.jobResult.start"), value: "start", color: "blue" },
|
||||
],
|
||||
});
|
||||
|
||||
const jobTypeDict = dict({
|
||||
data: [
|
||||
{ label: t("monitor.history.jobType.domainExpirationCheck"), value: "domainExpirationCheck", color: "green" },
|
||||
{ label: t("monitor.history.jobType.siteCertMonitor"), value: "siteCertMonitor", color: "blue" },
|
||||
],
|
||||
});
|
||||
|
||||
const selectedRowKeys = ref([]);
|
||||
|
||||
const handleBatchDelete = () => {
|
||||
if (selectedRowKeys.value?.length > 0) {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
|
||||
async onOk() {
|
||||
await api.BatchDelObj(selectedRowKeys.value);
|
||||
message.info("删除成功");
|
||||
crudExpose.doRefresh();
|
||||
selectedRowKeys.value = [];
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.error("请先勾选记录");
|
||||
}
|
||||
};
|
||||
|
||||
context.handleBatchDelete = handleBatchDelete;
|
||||
|
||||
const GroupTypeSite = "site";
|
||||
const groupDictRef = createGroupDictRef(GroupTypeSite);
|
||||
|
||||
function getDefaultGroupId() {
|
||||
const searchFrom = crudExpose.getSearchValidatedFormData();
|
||||
if (searchFrom.groupId) {
|
||||
return searchFrom.groupId;
|
||||
}
|
||||
}
|
||||
|
||||
const projectStore = useProjectStore();
|
||||
const { hasActionPermission } = useCrudPermission({ permission: context.permission });
|
||||
return {
|
||||
id: "jobHistoryCrud",
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
// tabs: {
|
||||
// name: "groupId",
|
||||
// show: true,
|
||||
// },
|
||||
toolbar: {
|
||||
buttons: {
|
||||
export: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
pageSizeOptions: ["10", "20", "50", "100", "200"],
|
||||
},
|
||||
settings: {
|
||||
plugins: {
|
||||
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
|
||||
rowSelection: {
|
||||
enabled: true,
|
||||
props: {
|
||||
multiple: true,
|
||||
crossPage: false,
|
||||
selectedRowKeys: () => {
|
||||
return selectedRowKeys;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
labelCol: {
|
||||
//固定label宽度
|
||||
span: null,
|
||||
style: {
|
||||
width: "100px",
|
||||
},
|
||||
},
|
||||
col: {
|
||||
span: 22,
|
||||
},
|
||||
wrapper: {
|
||||
width: 600,
|
||||
},
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
width: 280,
|
||||
buttons: {
|
||||
edit: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// tabs: {
|
||||
// name: "disabled",
|
||||
// show: true,
|
||||
// },
|
||||
search: {
|
||||
initialForm: {
|
||||
...projectStore.getSearchForm(),
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 80,
|
||||
align: "center",
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
type: {
|
||||
title: t("monitor.history.jobTypeTitle"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "dict-select",
|
||||
dict: jobTypeDict,
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 120,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
title: t("monitor.history.titleTitle"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "text",
|
||||
column: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
content: {
|
||||
title: t("monitor.history.contentTitle"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "text",
|
||||
column: {
|
||||
width: 460,
|
||||
ellipsis: true,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
title: t("monitor.history.resultTitle"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "dict-select",
|
||||
dict: historyResultDict,
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
align: "center",
|
||||
sorter: true,
|
||||
cellRender({ value, row }) {
|
||||
return (
|
||||
<a-tooltip title={row.error}>
|
||||
<fs-values-format v-model={value} dict={historyResultDict}></fs-values-format>
|
||||
</a-tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
startAt: {
|
||||
title: t("monitor.history.startAtTitle"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "datetime",
|
||||
column: {
|
||||
width: 160,
|
||||
},
|
||||
},
|
||||
endAt: {
|
||||
title: t("monitor.history.endAtTitle"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "datetime",
|
||||
column: {
|
||||
width: 160,
|
||||
},
|
||||
},
|
||||
projectId: {
|
||||
title: t("certd.fields.projectName"),
|
||||
type: "dict-select",
|
||||
dict: myProjectDict,
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title flex items-center">
|
||||
{{ t("monitor.history.title") }}
|
||||
<div class="sub flex-1">
|
||||
<div>
|
||||
{{ t("monitor.history.description") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #pagination-left>
|
||||
<a-tooltip title="批量删除">
|
||||
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useI18n } from "/src/locales";
|
||||
const { t } = useI18n();
|
||||
defineOptions({
|
||||
name: "JobHistory",
|
||||
});
|
||||
const context: any = {
|
||||
permission: {
|
||||
isProjectPermission: true,
|
||||
},
|
||||
};
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
|
||||
const handleBatchDelete = context.handleBatchDelete;
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
@@ -346,12 +346,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
width: 230,
|
||||
sorter: true,
|
||||
cellRender({ value, row }) {
|
||||
const url = `https://${value}:${row.httpsPort}`;
|
||||
const domainPort = value + ":" + row.httpsPort;
|
||||
const url = `https://${domainPort}`;
|
||||
return (
|
||||
<a-tooltip title={value} placement="left">
|
||||
<fs-copyable modelValue={value}>
|
||||
<a-tooltip title={domainPort} placement="left">
|
||||
<fs-copyable modelValue={domainPort} title={domainPort}>
|
||||
<a target="_blank" href={url}>
|
||||
{value}:{row.httpsPort}
|
||||
{domainPort}
|
||||
</a>
|
||||
</fs-copyable>
|
||||
</a-tooltip>
|
||||
|
||||
@@ -42,6 +42,7 @@ import createCrudOptions from "../crud";
|
||||
import { notificationProvide } from "/@/views/certd/notification/common";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -127,14 +128,27 @@ function clear() {
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const projectStore = useProjectStore();
|
||||
async function emitValue(value: any) {
|
||||
// target.value = optionsDictRef.dataMap[value];
|
||||
const userId = userStore.userInfo.id;
|
||||
if (pipeline?.value && pipeline.value.userId !== userId) {
|
||||
message.error("对不起,您不能修改他人流水线的通知");
|
||||
return;
|
||||
const isEnterprice = projectStore.isEnterprise;
|
||||
|
||||
if (pipeline?.value) {
|
||||
if (isEnterprice) {
|
||||
const projectId = projectStore.currentProjectId;
|
||||
if (pipeline?.value?.projectId !== projectId) {
|
||||
message.error("对不起,您不能修改其他项目流水线的通知");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (pipeline?.value?.userId !== userId) {
|
||||
message.error("对不起,您不能修改他人流水线的通知");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit("change", value);
|
||||
emit("update:modelValue", value);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
</a-tabs>
|
||||
<template #footer>
|
||||
<fs-button v-if="settingsStore.sysPublic.aiChatEnabled !== false" key="aiChat" :tooltip="{ title: 'AI分析异常' }" type="primary" icon="ion:color-wand-outline" @click="taskModal.onAiChat">AI分析</fs-button>
|
||||
<!-- <fs-button v-if="!settingsStore.isComm && currentStatus === 'error'" key="1v1" :tooltip="{ title: '升级专业版,获得一对一分析服务,为您排忧解难' }" class="isPlus" icon="imingcute:vip-1-line" @click="callService">
|
||||
呼叫专家
|
||||
</fs-button> -->
|
||||
<fs-button key="rerun" type="primary" :tooltip="{ title: '强制重新执行此步骤' }" text="重新运行" icon="icon-park-outline:replay-music" @click="triggerRun(activeKey)"></fs-button>
|
||||
<fs-button key="downloadLogs" type="primary" :tooltip="{ title: '当前任务日志下载' }" icon="ion:arrow-down-circle-outline" @click="taskModal.onDownloadLogs">下载日志</fs-button>
|
||||
<fs-button key="cancel" :tooltip="{ title: '关闭窗口' }" icon="ion:close-circle-outline" @click="taskModal.onOk">关闭</fs-button>
|
||||
@@ -39,6 +42,7 @@ import PiStatusShow from "/@/views/certd/pipeline/pipeline/component/status-show
|
||||
import { usePreferences } from "/@/vben/preferences";
|
||||
import { useSettingStore } from "/@/store/settings/index";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { mitter } from "/@/utils/util.mitt";
|
||||
export default {
|
||||
name: "PiTaskView",
|
||||
components: { PiStatusShow },
|
||||
@@ -196,6 +200,19 @@ export default {
|
||||
taskModal.value.open = false;
|
||||
}
|
||||
|
||||
const currentNode = computed(() => {
|
||||
return detail.value?.nodes?.find(item => item.node.id === activeKey.value);
|
||||
});
|
||||
const currentStatus = computed(() => {
|
||||
return currentNode.value?.node?.status?.result || "";
|
||||
});
|
||||
|
||||
function callService() {
|
||||
if (!settingsStore.isPlus) {
|
||||
mitter.emit("openVipModal");
|
||||
}
|
||||
}
|
||||
|
||||
const settingsStore = useSettingStore();
|
||||
return {
|
||||
detail,
|
||||
@@ -206,6 +223,9 @@ export default {
|
||||
tabPosition,
|
||||
triggerRun,
|
||||
settingsStore,
|
||||
currentNode,
|
||||
currentStatus,
|
||||
callService,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -334,7 +334,7 @@ import { useCertViewer } from "/@/views/certd/pipeline/use";
|
||||
import { useI18n } from "/@/locales";
|
||||
import TriggerIcon from "./component/trigger-icon.vue";
|
||||
import { useCrudPermission } from "/@/plugin/permission";
|
||||
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
export default defineComponent({
|
||||
name: "PipelineEdit",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
@@ -372,10 +372,22 @@ export default defineComponent({
|
||||
},
|
||||
emits: ["update:modelValue", "update:editMode"],
|
||||
setup(props, ctx) {
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
const newPipelineStr = JSON.stringify(pipeline.value || {});
|
||||
if (pipelineOriginStr.value && pipelineOriginStr.value !== newPipelineStr) {
|
||||
const answer = window.confirm("流水线还未保存,确定要离开吗?");
|
||||
if (!answer) {
|
||||
return false; // 返回 false 即可阻止本次路由跳转
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
//右侧选中的pipeline
|
||||
const currentPipeline: Ref<any> = ref({});
|
||||
const pipeline: Ref<any> = ref({});
|
||||
const pipelineOriginStr = ref("");
|
||||
const pipelineDetail: Ref<any> = ref({});
|
||||
const histories: Ref<RunHistory[]> = ref([]);
|
||||
|
||||
@@ -423,6 +435,7 @@ export default defineComponent({
|
||||
await loadCurrentHistoryDetail();
|
||||
pipeline.value = currentHistory.value.pipeline;
|
||||
currentPipeline.value = currentHistory.value.pipeline;
|
||||
pipelineOriginStr.value = JSON.stringify(pipeline.value);
|
||||
};
|
||||
|
||||
async function loadHistoryList(reload = false, historyId: number = null) {
|
||||
@@ -880,6 +893,7 @@ export default defineComponent({
|
||||
pipeline.value.version = version;
|
||||
currentPipeline.value.version = version;
|
||||
}
|
||||
pipelineOriginStr.value = JSON.stringify(pipeline.value);
|
||||
}
|
||||
if (offEdit) {
|
||||
toggleEditMode(false);
|
||||
|
||||
@@ -118,6 +118,10 @@ export function useTransfer() {
|
||||
<div class="text-2xl font-bold"> 迁移到→ </div>
|
||||
<div>项目:"{projectStore.currentProject?.name}"</div>
|
||||
</div>
|
||||
<div class="text-center m-4">
|
||||
<p class="text-red-500">注意;此操作不可逆,一旦迁移,数据将无法还原回个人用户名下。</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-center w-full">
|
||||
<a-button type="primary" onClick={doTransfer}>
|
||||
确认迁移
|
||||
|
||||
@@ -238,10 +238,14 @@ const avatar = computed(() => {
|
||||
}
|
||||
return `/api/basic/file/download?key=${avt}`;
|
||||
});
|
||||
const dateNow = ref(Date.now());
|
||||
const now = computed(() => {
|
||||
const serverTime = Date.now() - settingStore.app.deltaTime;
|
||||
const serverTime = dateNow.value - settingStore.app.deltaTime;
|
||||
return dayjs(serverTime).format("YYYY-MM-DD HH:mm:ss");
|
||||
});
|
||||
setInterval(() => {
|
||||
dateNow.value = Date.now();
|
||||
}, 5000);
|
||||
|
||||
const deltaTimeWarning = computed(() => {
|
||||
return Math.abs(settingStore.app.deltaTime) > 1000 * 60 * 4;
|
||||
|
||||
@@ -172,7 +172,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||
width: "auto",
|
||||
},
|
||||
buildUrl(key: string) {
|
||||
return `api/basic/file/download?&key=` + key;
|
||||
return `api/basic/file/download?token=${userStore.getToken}&key=` + key;
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -188,7 +188,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||
onReady: null,
|
||||
uploader: {
|
||||
type: "form",
|
||||
action: "/basic/file/upload",
|
||||
action: "/basic/file/upload?token=" + userStore.getToken,
|
||||
name: "file",
|
||||
headers: {
|
||||
Authorization: "Bearer " + userStore.getToken,
|
||||
|
||||
@@ -9,6 +9,7 @@ import yaml from "js-yaml";
|
||||
import { usePluginImport } from "./use-import";
|
||||
import { usePluginConfig } from "./use-config";
|
||||
import { useSettingStore } from "/src/store/settings/index";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
@@ -43,6 +44,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const { openConfigDialog } = usePluginConfig();
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const pluginStore = usePluginStore();
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
@@ -83,6 +85,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
rowKey: "name",
|
||||
remove: {
|
||||
afterRemove: async context => {
|
||||
await pluginStore.reload();
|
||||
},
|
||||
confirmMessage: "确定要删除吗?如果该插件已被使用,删除可能会导致流水线执行失败!",
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
show: true,
|
||||
minWidth: 200,
|
||||
@@ -142,9 +153,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
rowKey: "name",
|
||||
},
|
||||
tabs: {
|
||||
name: "type",
|
||||
show: true,
|
||||
|
||||
@@ -29,7 +29,7 @@ import { SysSettings } from "/@/views/sys/settings/api";
|
||||
import * as api from "/@/views/sys/settings/api";
|
||||
import { merge } from "lodash-es";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
@@ -80,6 +80,24 @@ const onFinish = async (form: any) => {
|
||||
notification.success({
|
||||
message: t("certd.saveSuccess"),
|
||||
});
|
||||
|
||||
if (formState.public.adminMode === "enterprise") {
|
||||
Modal.confirm({
|
||||
title: "数据迁移",
|
||||
okText: "去迁移",
|
||||
content: () => {
|
||||
return (
|
||||
<div>
|
||||
<div>设置为企业模式之后,之前创建的个人数据不会显示</div>
|
||||
<div>是否前往迁移数据到项目? </div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
onOk: () => {
|
||||
goCurrentProject();
|
||||
},
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/sys/site";
|
||||
|
||||
export async function SettingsGet(key: string) {
|
||||
export async function SettingsGet() {
|
||||
return await request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "post",
|
||||
|
||||
@@ -88,7 +88,7 @@ const onFinish = async (form: any) => {
|
||||
const userStore = useUserStore();
|
||||
const uploaderConfig = ref({
|
||||
type: "form",
|
||||
action: "/basic/file/upload",
|
||||
action: "/basic/file/upload?token=" + userStore.getToken,
|
||||
name: "file",
|
||||
headers: {
|
||||
Authorization: "Bearer " + userStore.getToken,
|
||||
|
||||
@@ -3,6 +3,49 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复自定义插件删除后没有反注册的bug ([df98463](https://github.com/certd/certd/commit/df9846332596d2afaba53e66d2897aa1c598f9c4))
|
||||
* 修复spaceship创建record报错的bug ([70b46d4](https://github.com/certd/certd/commit/70b46d4a8f89cf8eded21ebb237e8c8ce6c40d30))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 1panel支持先上传证书再选择证书 ([7a9eec8](https://github.com/certd/certd/commit/7a9eec88e8eddf40dba055c072b5b2b0f67c1407))
|
||||
* 部署到1panel面板支持mux模式 ([d05129e](https://github.com/certd/certd/commit/d05129ec67893b0b639003a4bca6878d128f56ad))
|
||||
* 流水线修改编辑之后,增加未保存提示 ([21620ac](https://github.com/certd/certd/commit/21620ac6bdeb57e43509156a77037fc07c44282a))
|
||||
* 修复检查全部某些情况下无效的bug,优化公共触发站点证书检查定时逻辑 ([ee53589](https://github.com/certd/certd/commit/ee535895a3166c6f9046963e28fa8f22f018b574))
|
||||
* 增加域名管理 子域名检查提醒 ([2bdf183](https://github.com/certd/certd/commit/2bdf1832da73a3728f3ac415837bc26e70531cd6))
|
||||
|
||||
## [1.39.9](https://github.com/certd/certd/compare/v1.39.8...v1.39.9) (2026-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复cn域名获取不到到期时间的问题 ([73b8e85](https://github.com/certd/certd/commit/73b8e859766097b5251fc4e5051593d686669eb2))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 腾讯云CLB大区增加台北 ([6b109d1](https://github.com/certd/certd/commit/6b109d172f0c7b6ce6ec164dc196d646a65f529f))
|
||||
* 优化腾讯云CLB插件支持选择证书id ([c875971](https://github.com/certd/certd/commit/c875971b71dc6d392e56f0a7605281c40d9bb405))
|
||||
* 支持域名到期时间监控通知 ([c6628e7](https://github.com/certd/certd/commit/c6628e7311d6c43c2a784581fb25ec37b29c168d))
|
||||
* **monitor:** 支持查看监控执行记录 ([b5cc794](https://github.com/certd/certd/commit/b5cc794061c11b7200b669473c25c4bbfc944b61))
|
||||
* **plugin-dnsmgr:** 添加彩虹DNS插件支持 ([af50344](https://github.com/certd/certd/commit/af503442b8298c5b89d11cf2ea351d62e66a609e))
|
||||
* **spaceship:** 新增Spaceship DNS插件和授权模块 ([21aec77](https://github.com/certd/certd/commit/21aec77e5c3307b5973d4185baba33edcb28926f))
|
||||
|
||||
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
|
||||
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
|
||||
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
|
||||
* dcdn自动匹配部署,支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
|
||||
|
||||
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
CREATE TABLE `cd_job_history`
|
||||
(
|
||||
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||
`user_id` bigint NOT NULL,
|
||||
`project_id` bigint ,
|
||||
`type` varchar(100) NOT NULL,
|
||||
`title` varchar(512) NOT NULL,
|
||||
`related_id` varchar(100),
|
||||
`result` varchar(100) NOT NULL,
|
||||
`content` longtext ,
|
||||
`start_at` bigint NOT NULL,
|
||||
`end_at` bigint ,
|
||||
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX `index_job_history_user_id` ON `cd_job_history` (`user_id`);
|
||||
CREATE INDEX `index_job_history_project_id` ON `cd_job_history` (`project_id`);
|
||||
CREATE INDEX `index_job_history_type` ON `cd_job_history` (`type`);
|
||||
|
||||
ALTER TABLE `cd_job_history` ENGINE = InnoDB;
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
CREATE TABLE "cd_job_history"
|
||||
(
|
||||
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||
"user_id" bigint NOT NULL,
|
||||
"project_id" bigint ,
|
||||
"type" varchar(100) NOT NULL,
|
||||
"title" varchar(512) NOT NULL,
|
||||
"related_id" varchar(100),
|
||||
"result" varchar(100) NOT NULL,
|
||||
"content" text ,
|
||||
"start_at" bigint NOT NULL,
|
||||
"end_at" bigint ,
|
||||
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX "index_job_history_user_id" ON "cd_job_history" ("user_id");
|
||||
CREATE INDEX "index_job_history_project_id" ON "cd_job_history" ("project_id");
|
||||
CREATE INDEX "index_job_history_type" ON "cd_job_history" ("type");
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
CREATE TABLE "cd_job_history"
|
||||
(
|
||||
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"project_id" integer ,
|
||||
"type" varchar(100) NOT NULL,
|
||||
"title" varchar(512) NOT NULL,
|
||||
"related_id" varchar(100),
|
||||
"result" varchar(100) NOT NULL,
|
||||
"content" text ,
|
||||
"start_at" integer NOT NULL,
|
||||
"end_at" integer ,
|
||||
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE INDEX "index_job_history_user_id" ON "cd_job_history" ("user_id");
|
||||
CREATE INDEX "index_job_history_project_id" ON "cd_job_history" ("project_id");
|
||||
CREATE INDEX "index_job_history_type" ON "cd_job_history" ("type");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user