mirror of
https://github.com/certd/certd.git
synced 2026-07-20 05:37:32 +08:00
Compare commits
67 Commits
v1.39.3
...
8debac2edf
| Author | SHA1 | Date | |
|---|---|---|---|
| 8debac2edf | |||
| a68301e4dc | |||
| c6a988bc92 | |||
| fe02ce7b64 | |||
| df012dec90 | |||
| 5969425a6f | |||
| b17b1e6463 | |||
| c99e61c402 | |||
| f4aaec8b3c | |||
| adc3e6118b | |||
| d933493c31 | |||
| f91d591b03 | |||
| af6deb99cd | |||
| c5d285f755 | |||
| b1eb706925 | |||
| 5a01634ca3 | |||
| 487676ce13 | |||
| 0280ca7b1a | |||
| b0ccab41e1 | |||
| ccda3a3325 | |||
| 4b7eeaa6e0 | |||
| 2951f0030d | |||
| acc2df29de | |||
| 431afd618f | |||
| 6d5e5bd692 | |||
| ffd2e8149e | |||
| 2ab92dc13e | |||
| 7f6a8bc87e | |||
| b1ff163a28 | |||
| 440d55ccb8 | |||
| 285532d431 | |||
| f2c47459f8 | |||
| 49703f08e5 | |||
| 1d0aa9573b | |||
| b2014cf88b | |||
| a0e0078bad | |||
| 5ebca21c32 | |||
| 970aea90c9 | |||
| 5f9341ad8e | |||
| 574c0983f5 | |||
| be6c7c7ac8 | |||
| 4fd31f276b | |||
| 224db7da57 | |||
| 1413e1aff4 | |||
| 68b669d3ff | |||
| 29f44c67c8 | |||
| 3332d2288f | |||
| 34702196e1 | |||
| d45c8d1e9b | |||
| bc19825ada | |||
| 72bb640239 | |||
| aacee4a94c | |||
| 3ab37d5c5d | |||
| 3dd3ecf8f1 | |||
| a3831827d0 | |||
| 6aa6c957ee | |||
| 9e12412f5f | |||
| 0f9eb31740 | |||
| 6be8ab581d | |||
| d8425bc9c5 | |||
| 0ddcb9c00a | |||
| 6d43623f45 | |||
| 196cd88010 | |||
| 7f37df4227 | |||
| 985a12a63b | |||
| 9058c0e9fc | |||
| 4b0cd32d12 |
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 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 |
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 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 |
|
||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
branches: ['v2-dev']
|
branches: ['v2-dev']
|
||||||
paths:
|
paths:
|
||||||
- "trigger/build.trigger"
|
- "trigger/build.trigger"
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
# schedule:
|
# schedule:
|
||||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
# - cron: '17 19 * * *'
|
# - cron: '17 19 * * *'
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: 'v2-dev'
|
||||||
- name: get_certd_version
|
- name: get_certd_version
|
||||||
id: get_certd_version
|
id: get_certd_version
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ on:
|
|||||||
workflows: [ "build-image" ]
|
workflows: [ "build-image" ]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# schedule:
|
# schedule:
|
||||||
@@ -26,6 +28,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: v2-dev
|
ref: v2-dev
|
||||||
|
|
||||||
- name: get_certd_version
|
- name: get_certd_version
|
||||||
id: get_certd_version
|
id: get_certd_version
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ on:
|
|||||||
workflows: [ "build-image-for-release" ]
|
workflows: [ "build-image-for-release" ]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
# schedule:
|
# schedule:
|
||||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
# - cron: '17 19 * * *'
|
# - cron: '17 19 * * *'
|
||||||
@@ -19,13 +19,17 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
publish-atomgit:
|
publish-atomgit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
if: |
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(github.event.workflow_run.conclusion == 'success')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: 'v2-dev'
|
||||||
|
|
||||||
- name: get_certd_version
|
- name: get_certd_version
|
||||||
id: get_certd_version
|
id: get_certd_version
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ on:
|
|||||||
workflows: [ "build-image-for-release" ]
|
workflows: [ "build-image-for-release" ]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
# schedule:
|
# schedule:
|
||||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
# - cron: '17 19 * * *'
|
# - cron: '17 19 * * *'
|
||||||
@@ -19,13 +19,16 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
publish-gitee:
|
publish-gitee:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
if: |
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(github.event.workflow_run.conclusion == 'success')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: 'v2-dev'
|
||||||
|
|
||||||
- name: publish_to_gitee
|
- name: publish_to_gitee
|
||||||
id: publish_to_gitee
|
id: publish_to_gitee
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ on:
|
|||||||
workflows: [ "build-image-for-release" ]
|
workflows: [ "build-image-for-release" ]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
# schedule:
|
# schedule:
|
||||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
# - cron: '17 19 * * *'
|
# - cron: '17 19 * * *'
|
||||||
@@ -19,13 +19,16 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
publish-github:
|
publish-github:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
if: |
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(github.event.workflow_run.conclusion == 'success')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: 'v2-dev'
|
||||||
|
|
||||||
- name: publish_to_github
|
- name: publish_to_github
|
||||||
id: publish_to_github
|
id: publish_to_github
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ on:
|
|||||||
branches: ['v2-dev']
|
branches: ['v2-dev']
|
||||||
paths:
|
paths:
|
||||||
- "trigger/release.trigger"
|
- "trigger/release.trigger"
|
||||||
|
workflow_dispatch: # 添加手动触发
|
||||||
# workflow_run:
|
# workflow_run:
|
||||||
# workflows: [ "deploy-demo" ]
|
# workflows: [ "deploy-demo" ]
|
||||||
# types:
|
# types:
|
||||||
@@ -25,6 +26,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: 'v2-dev'
|
||||||
|
|
||||||
- name: get_certd_version
|
- name: get_certd_version
|
||||||
id: get_certd_version
|
id: get_certd_version
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: v2-dev
|
||||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "xiaojunnuo"
|
git config --global user.name "xiaojunnuo"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: v2
|
||||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "xiaojunnuo"
|
git config --global user.name "xiaojunnuo"
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: sync-to-cnb-dev
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['v2-dev']
|
||||||
|
# schedule:
|
||||||
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
|
# - cron: '17 19 * * *'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
lfs: true
|
||||||
|
ref: v2-dev
|
||||||
|
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||||
|
run: |
|
||||||
|
git config --global user.name "xiaojunnuo"
|
||||||
|
git config --global user.email "xiaojunnuo@qq.com"
|
||||||
|
|
||||||
|
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||||
|
uses: de-vri-es/setup-git-credentials@v2
|
||||||
|
with: # token 格式为: username:password
|
||||||
|
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||||
|
|
||||||
|
- name: push to cnb # 4. 执行同步
|
||||||
|
run: |
|
||||||
|
git remote add upstream https://cnb.cool/certd/certd.git
|
||||||
|
git push --set-upstream upstream v2-dev
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: sync-to-cnb
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['v2']
|
||||||
|
# schedule:
|
||||||
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
|
# - cron: '17 19 * * *'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
lfs: true
|
||||||
|
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||||
|
run: |
|
||||||
|
git config --global user.name "xiaojunnuo"
|
||||||
|
git config --global user.email "xiaojunnuo@qq.com"
|
||||||
|
|
||||||
|
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||||
|
uses: de-vri-es/setup-git-credentials@v2
|
||||||
|
with: # token 格式为: username:password
|
||||||
|
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||||
|
|
||||||
|
- name: push to cnb # 4. 执行同步
|
||||||
|
run: |
|
||||||
|
git remote add upstream https://cnb.cool/certd/certd.git
|
||||||
|
git push --set-upstream upstream v2
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
ref: v2-dev
|
||||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "xiaojunnuo"
|
git config --global user.name "xiaojunnuo"
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
你是一名资深nodejs工程师,擅长开发Certd开源系统的任务插件。
|
||||||
|
certd是一款全自动证书申请部署管理工具,基于流水线的方式,通过里面申请证书插件申请证书,然后将证书传递给下一个部署任务插件,不同的部署任务插件将证书部署到用户的各个应用系统当中。
|
||||||
|
|
||||||
|
certd插件分成以下几种类型:
|
||||||
|
Access:存储用户的第三放应用的授权数据,比如用户名密码,accessSecret 或 accessToken等。同时它里面的方法还负责对接第三方的api接口
|
||||||
|
Task: 部署任务插件,它继承AbstractTaskPlugin类,被流水线调用execute方法,将证书部署到对应的应用上
|
||||||
|
DnsProvider: DNS提供商插件,它用于在ACME申请证书时给域名添加txt解析记录。
|
||||||
|
|
||||||
|
在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
|
||||||
|
当开发过程中遇到问题,需要参考plugins目录下的其他插件,或者用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
|
||||||
|
|
||||||
|
一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
|
||||||
|
|
||||||
|
完成开发后无需测试,通知用户自己去测试
|
||||||
@@ -89,16 +89,55 @@ certDomains!: string[];
|
|||||||
accessId!: string;
|
accessId!: string;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 实现插件方法
|
### 4. 动态显隐配置(mergeScript)
|
||||||
|
|
||||||
#### 4.1 插件实例化时执行的方法
|
使用 `mergeScript` 可以实现根据其他输入值动态控制当前输入项的显隐状态。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@TaskInput({
|
||||||
|
title: '匹配模式',
|
||||||
|
component: {
|
||||||
|
name: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '手动选择', value: 'manual' },
|
||||||
|
{ label: '根据证书匹配', value: 'auto' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: 'manual',
|
||||||
|
})
|
||||||
|
domainMatchMode!: 'manual' | 'auto';
|
||||||
|
|
||||||
|
@TaskInput(
|
||||||
|
createRemoteSelectInputDefine({
|
||||||
|
title: 'DCDN加速域名',
|
||||||
|
helper: '你在阿里云上配置的DCDN加速域名',
|
||||||
|
action: DeployCertToAliyunDCDN.prototype.onGetDomainList.name,
|
||||||
|
watches: ['certDomains', 'accessId'],
|
||||||
|
required: true,
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show: ctx.compute(({form})=>{
|
||||||
|
return domainMatchMode === "manual"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
domainName!: string | string[];
|
||||||
|
```
|
||||||
|
|
||||||
|
`mergeScript` 中的 `ctx.compute` 函数接收一个回调函数,通过 `form` 参数可以访问表单中的其他字段值。
|
||||||
|
|
||||||
|
### 5. 实现插件方法
|
||||||
|
|
||||||
|
#### 5.1 插件实例化时执行的方法
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 插件实例化时执行的方法
|
// 插件实例化时执行的方法
|
||||||
async onInstance() {}
|
async onInstance() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4.2 插件执行方法
|
#### 5.2 插件执行方法
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 插件执行方法
|
// 插件执行方法
|
||||||
@@ -130,7 +169,9 @@ async execute(): Promise<void> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4.3 后端获取选项方法
|
#### 5.3 后端获取选项方法
|
||||||
|
|
||||||
|
使用 `createRemoteSelectInputDefine` 创建远程选择输入项,`action` 指向的方法接收 `PageSearch` 参数并返回 `{ list, total }` 格式。
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@TaskInput(
|
@TaskInput(
|
||||||
@@ -145,8 +186,8 @@ async execute(): Promise<void> {
|
|||||||
)
|
)
|
||||||
siteName!: string | string[];
|
siteName!: string | string[];
|
||||||
|
|
||||||
// 从后端获取选项的方法
|
// 从后端获取选项的方法,接收PageSearch参数
|
||||||
async onGetSiteList(req: PageSearch) {
|
async onGetSiteList(data: PageSearch) {
|
||||||
if (!this.accessId) {
|
if (!this.accessId) {
|
||||||
throw new Error('请选择Access授权');
|
throw new Error('请选择Access授权');
|
||||||
}
|
}
|
||||||
@@ -154,7 +195,7 @@ async onGetSiteList(req: PageSearch) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const access = await this.getAccess(this.accessId);
|
const access = await this.getAccess(this.accessId);
|
||||||
|
|
||||||
// const siteRes = await access.GetDomainList(req);
|
// const siteRes = await access.GetDomainList(data);
|
||||||
// 以下是模拟数据
|
// 以下是模拟数据
|
||||||
const siteRes = [
|
const siteRes = [
|
||||||
{ id: 1, siteName: 'site1.com' },
|
{ id: 1, siteName: 'site1.com' },
|
||||||
@@ -169,8 +210,12 @@ async onGetSiteList(req: PageSearch) {
|
|||||||
domain: item.siteName,
|
domain: item.siteName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// 将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
|
|
||||||
return optionsUtils.buildGroupOptions(options, this.certDomains);
|
// 返回{list, total}格式
|
||||||
|
return {
|
||||||
|
list: optionsUtils.buildGroupOptions(options, this.certDomains),
|
||||||
|
total: siteRes.length,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -383,7 +428,10 @@ export class DemoTest extends AbstractTaskPlugin {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
//将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
|
//将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
|
||||||
return optionsUtils.buildGroupOptions(options, this.certDomains);
|
return {
|
||||||
|
list: optionsUtils.buildGroupOptions(options, this.certDomains),
|
||||||
|
total: siteRes.length,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 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 |
|
||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到火山云tos自定义域名证书 ([af6deb9](https://github.com/certd/certd/commit/af6deb99cd24a69a189b1fdd1df51c8f7816dcda))
|
||||||
|
* 支持部署证书到火山引擎vod ([f91d591](https://github.com/certd/certd/commit/f91d591b03c50166d9fa352ba11c62d963869aa5))
|
||||||
|
|
||||||
|
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
|
||||||
|
* 修复批量执行按钮无效的bug ([49703f0](https://github.com/certd/certd/commit/49703f08e55b303851086d9f36aca562d7999be6))
|
||||||
|
* remote-select默认pageSize设置为50,阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 ([ffd2e81](https://github.com/certd/certd/commit/ffd2e8149e3a06bf3eec456ff85dbed793af9e90))
|
||||||
|
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
|
||||||
|
* 新增阿里云证书清理插件 ([4b7eeaa](https://github.com/certd/certd/commit/4b7eeaa6e0a14d2e461c7c473a920a0966b1fe8e))
|
||||||
|
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
|
||||||
|
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
|
||||||
|
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
|
||||||
|
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
|
||||||
|
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
|
||||||
|
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -3,6 +3,64 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到火山云tos自定义域名证书 ([af6deb9](https://github.com/certd/certd/commit/af6deb99cd24a69a189b1fdd1df51c8f7816dcda))
|
||||||
|
* 支持部署证书到火山引擎vod ([f91d591](https://github.com/certd/certd/commit/f91d591b03c50166d9fa352ba11c62d963869aa5))
|
||||||
|
|
||||||
|
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
|
||||||
|
* 修复批量执行按钮无效的bug ([49703f0](https://github.com/certd/certd/commit/49703f08e55b303851086d9f36aca562d7999be6))
|
||||||
|
* remote-select默认pageSize设置为50,阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 ([ffd2e81](https://github.com/certd/certd/commit/ffd2e8149e3a06bf3eec456ff85dbed793af9e90))
|
||||||
|
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
|
||||||
|
* 新增阿里云证书清理插件 ([4b7eeaa](https://github.com/certd/certd/commit/4b7eeaa6e0a14d2e461c7c473a920a0966b1fe8e))
|
||||||
|
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
|
||||||
|
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
|
||||||
|
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
|
||||||
|
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
|
||||||
|
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
|
||||||
|
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
|
||||||
|
|
||||||
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复多选框只能单选的bug ([12700e1](https://github.com/certd/certd/commit/12700e1754319513ac02822ff1588d63420b964e))
|
||||||
|
* 修复旧版1panel插件 报sslIds is not iterable的错误 ([50db6f0](https://github.com/certd/certd/commit/50db6f0765e7ec9a5698cd99540d90e188634fb1))
|
||||||
|
|
||||||
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
|
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# 任务插件
|
# 任务插件
|
||||||
共 `125` 款任务插件
|
共 `128` 款任务插件
|
||||||
## 1. 证书申请
|
## 1. 证书申请
|
||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
@@ -81,22 +81,23 @@
|
|||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
|-----|-----|-----|
|
|-----|-----|-----|
|
||||||
| 1.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
|
| 1.| **阿里云-删除即将过期证书** | 仅删除未使用的证书 |
|
||||||
| 2.| **阿里云-部署至ALB(应用负载均衡)** | ALB,更新监听器的默认证书 |
|
| 2.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
|
||||||
| 3.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数,支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
|
| 3.| **阿里云-部署至ALB(应用负载均衡)** | ALB,更新监听器的默认证书 |
|
||||||
| 4.| **阿里云-部署至云原生API网关/AI网关** | 自动部署域名证书至云原生API网关、AI网关 |
|
| 4.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数,支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
|
||||||
| 5.| **阿里云-部署证书至API网关** | 自动部署域名证书至阿里云API网关(APIGateway) |
|
| 5.| **阿里云-部署至云原生API网关/AI网关** | 自动部署域名证书至云原生API网关、AI网关 |
|
||||||
| 6.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
|
| 6.| **阿里云-部署证书至API网关** | 自动部署域名证书至阿里云API网关(APIGateway) |
|
||||||
| 7.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务,自动部署域名证书至阿里云DCDN |
|
| 7.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
|
||||||
| 8.| **阿里云-部署至ESA** | 部署证书到阿里云ESA(边缘安全加速),自动删除过期证书 |
|
| 8.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务,自动部署域名证书至阿里云DCDN |
|
||||||
| 9.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算(FC3.0) |
|
| 9.| **阿里云-部署至ESA** | 部署证书到阿里云ESA(边缘安全加速),自动删除过期证书 |
|
||||||
| 10.| **阿里云-部署至GA** | 部署证书到阿里云GA(全球加速),支持更新默认证书和扩展证书 |
|
| 10.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算(FC3.0) |
|
||||||
| 11.| **阿里云-部署至NLB(网络负载均衡)** | NLB,网络负载均衡,更新监听器的默认证书 |
|
| 11.| **阿里云-部署至GA** | 部署证书到阿里云GA(全球加速),支持更新默认证书和扩展证书 |
|
||||||
| 12.| **阿里云-部署证书至OSS** | 部署域名证书至阿里云OSS自定义域名,不是上传到阿里云oss |
|
| 12.| **阿里云-部署至NLB(网络负载均衡)** | NLB,网络负载均衡,更新监听器的默认证书 |
|
||||||
| 13.| **阿里云-部署至CLB(传统负载均衡)** | 部署证书到阿里云CLB(传统负载均衡) |
|
| 13.| **阿里云-部署证书至OSS** | 部署域名证书至阿里云OSS自定义域名,不是上传到阿里云oss |
|
||||||
| 14.| **阿里云-部署至VOD** | 部署证书到阿里云视频点播(vod) |
|
| 14.| **阿里云-部署至CLB(传统负载均衡)** | 部署证书到阿里云CLB(传统负载均衡) |
|
||||||
| 15.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
|
| 15.| **阿里云-部署至VOD** | 部署证书到阿里云视频点播(vod) |
|
||||||
| 16.| **阿里云-上传证书到CAS** | 上传证书到阿里云证书管理服务(CAS),如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出 |
|
| 16.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
|
||||||
|
| 17.| **阿里云-上传证书到CAS** | 上传证书到阿里云证书管理服务(CAS),如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出 |
|
||||||
## 6. 华为云
|
## 6. 华为云
|
||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
@@ -130,8 +131,9 @@
|
|||||||
| 3.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
|
| 3.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
|
||||||
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
|
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
|
||||||
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
|
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
|
||||||
| 6.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播(暂不可用) |
|
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件 |
|
||||||
| 7.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
| 7.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
|
||||||
|
| 8.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
||||||
## 9. 京东云
|
## 9. 京东云
|
||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.39.3"
|
"version": "1.39.7"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
## [1.39.6](https://github.com/publishlab/node-acme-client/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/publishlab/node-acme-client/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/publishlab/node-acme-client/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
## [1.39.3](https://github.com/publishlab/node-acme-client/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/publishlab/node-acme-client/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/acme-client
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "scr/index.js",
|
"module": "scr/index.js",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
@@ -70,5 +70,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -588,7 +588,8 @@ class AcmeClient {
|
|||||||
|
|
||||||
if (invalidStates.includes(resp.data.status)) {
|
if (invalidStates.includes(resp.data.status)) {
|
||||||
abort();
|
abort();
|
||||||
throw new Error(util.formatResponseError(resp));
|
this.log(`[${d}] : 检查状态 = ${resp.data.status} : ${JSON.stringify(resp.data)}`);
|
||||||
|
throw new Error("校验失败:" + util.formatResponseError(resp));
|
||||||
}
|
}
|
||||||
else if (pendingStates.includes(resp.data.status)) {
|
else if (pendingStates.includes(resp.data.status)) {
|
||||||
throw new Error(`[${d}] Operation is pending or processing(当前仍然在等待状态)`);
|
throw new Error(`[${d}] Operation is pending or processing(当前仍然在等待状态)`);
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/basic
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
13:58
|
01:02
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -47,5 +47,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/pipeline
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@certd/plus-core": "^1.39.3",
|
"@certd/plus-core": "^1.39.7",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
@@ -45,5 +45,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export class Executor {
|
|||||||
//参数没有变化
|
//参数没有变化
|
||||||
inputChanged = false;
|
inputChanged = false;
|
||||||
}
|
}
|
||||||
if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
|
if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed && define.runStrategy !== RunStrategy.AlwaysRun) {
|
||||||
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
|
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
|
||||||
step.status!.output = lastNode?.status?.output;
|
step.status!.output = lastNode?.status?.output;
|
||||||
step.status!.files = lastNode?.status?.files;
|
step.status!.files = lastNode?.status?.files;
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { FileStore } from "../core/file-store.js";
|
|||||||
import { accessRegistry, IAccessService } from "../access/index.js";
|
import { accessRegistry, IAccessService } from "../access/index.js";
|
||||||
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
|
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
|
||||||
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
|
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
|
||||||
import { HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
|
import { domainUtils, HttpRequestConfig, ILogger, logger, optionsUtils, utils } from "@certd/basic";
|
||||||
import { HttpClient } from "@certd/basic";
|
import { HttpClient } from "@certd/basic";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { IPluginConfigService } from "../service/config.js";
|
import { IPluginConfigService } from "../service/config.js";
|
||||||
import { upperFirst } from "lodash-es";
|
import { cloneDeep, upperFirst } from "lodash-es";
|
||||||
import { INotificationService } from "../notification/index.js";
|
import { INotificationService } from "../notification/index.js";
|
||||||
import { TaskEmitter } from "../service/emit.js";
|
import { TaskEmitter } from "../service/emit.js";
|
||||||
|
import { PageSearch } from "../context/index.js";
|
||||||
|
|
||||||
export type PluginRequestHandleReq<T = any> = {
|
export type PluginRequestHandleReq<T = any> = {
|
||||||
typeName: string;
|
typeName: string;
|
||||||
@@ -64,6 +65,7 @@ export type PluginDefine = Registrable & {
|
|||||||
onlyAdmin?: boolean;
|
onlyAdmin?: boolean;
|
||||||
needPlus?: boolean;
|
needPlus?: boolean;
|
||||||
showRunStrategy?: boolean;
|
showRunStrategy?: boolean;
|
||||||
|
runStrategy?: any;
|
||||||
pluginType?: string; //类型
|
pluginType?: string; //类型
|
||||||
type?: string; //来源
|
type?: string; //来源
|
||||||
};
|
};
|
||||||
@@ -81,6 +83,12 @@ export type TaskResult = {
|
|||||||
pipelineVars: Record<string, any>;
|
pipelineVars: Record<string, any>;
|
||||||
pipelinePrivateVars?: Record<string, any>;
|
pipelinePrivateVars?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CertTargetItem = {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
domain: string | string[];
|
||||||
|
};
|
||||||
export type TaskInstanceContext = {
|
export type TaskInstanceContext = {
|
||||||
//流水线定义
|
//流水线定义
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
@@ -315,6 +323,103 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
getLastOutput(key: string) {
|
getLastOutput(key: string) {
|
||||||
return this.getLastStatus().status?.output?.[key];
|
return this.getLastStatus().status?.output?.[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDomainMatched(domainList: string | string[], certDomains: string[]): boolean {
|
||||||
|
const matched = domainUtils.match(domainList, certDomains);
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
isNotChanged() {
|
||||||
|
const lastResult = this.ctx?.lastStatus?.status?.status;
|
||||||
|
return !this.ctx.inputChanged && lastResult === "success";
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAutoMatchedTargets(req: {
|
||||||
|
targetName: string;
|
||||||
|
certDomains: string[];
|
||||||
|
pageSize: number;
|
||||||
|
getDeployTargetList: (req: PageSearch) => Promise<{ list: CertTargetItem[]; total: number }>;
|
||||||
|
}): Promise<CertTargetItem[]> {
|
||||||
|
const matchedDomains: CertTargetItem[] = [];
|
||||||
|
let pageNo = 1;
|
||||||
|
const { certDomains } = req;
|
||||||
|
|
||||||
|
const pageSize = req.pageSize || 100;
|
||||||
|
while (true) {
|
||||||
|
const result = await req.getDeployTargetList({
|
||||||
|
pageNo,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
const pageData = result.list;
|
||||||
|
this.logger.info(`获取到 ${pageData.length} 个 ${req.targetName}`);
|
||||||
|
|
||||||
|
if (!pageData || pageData.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of pageData) {
|
||||||
|
const domainName = item.domain;
|
||||||
|
if (this.isDomainMatched(domainName, certDomains)) {
|
||||||
|
matchedDomains.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalCount = result.total || 0;
|
||||||
|
if (pageNo * pageSize >= totalCount || matchedDomains.length == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNo++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedDomains;
|
||||||
|
}
|
||||||
|
|
||||||
|
async autoMatchedDeploy(req: {
|
||||||
|
targetName: string;
|
||||||
|
getCertDomains: () => Promise<string[]>;
|
||||||
|
uploadCert: () => Promise<any>;
|
||||||
|
deployOne: (req: { target: CertTargetItem; cert: any }) => Promise<void>;
|
||||||
|
getDeployTargetList: (req: PageSearch) => Promise<{ list: CertTargetItem[]; total: number }>;
|
||||||
|
}): Promise<{ result: string; deployedList: string[] }> {
|
||||||
|
this.logger.info("证书匹配模式部署");
|
||||||
|
const certDomains = await req.getCertDomains();
|
||||||
|
const certTargetList = await this.getAutoMatchedTargets({
|
||||||
|
targetName: req.targetName,
|
||||||
|
pageSize: 200,
|
||||||
|
certDomains,
|
||||||
|
getDeployTargetList: req.getDeployTargetList,
|
||||||
|
});
|
||||||
|
if (certTargetList.length === 0) {
|
||||||
|
this.logger.warn(`未找到匹配的${req.targetName}`);
|
||||||
|
return { result: "skip", deployedList: [] };
|
||||||
|
}
|
||||||
|
this.logger.info(`找到 ${certTargetList.length} 个匹配的${req.targetName}`);
|
||||||
|
|
||||||
|
//开始部署,检查是否已经部署过
|
||||||
|
const deployedList = cloneDeep(this.getLastStatus()?.status?.output?.deployedList || []);
|
||||||
|
const unDeployedTargets = certTargetList.filter(item => !deployedList.includes(item.value));
|
||||||
|
const count = unDeployedTargets.length;
|
||||||
|
const deployedCount = certTargetList.length - count;
|
||||||
|
if (deployedCount > 0) {
|
||||||
|
this.logger.info(`跳过 ${deployedCount} 个已部署过的${req.targetName}`);
|
||||||
|
}
|
||||||
|
this.logger.info(`需要部署 ${count} 个${req.targetName}`);
|
||||||
|
if (count === 0) {
|
||||||
|
return { result: "skip", deployedList };
|
||||||
|
}
|
||||||
|
this.logger.info(`开始部署`);
|
||||||
|
const aliCrtId = await req.uploadCert();
|
||||||
|
for (const target of unDeployedTargets) {
|
||||||
|
await req.deployOne({
|
||||||
|
cert: aliCrtId,
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
deployedList.push(target.value);
|
||||||
|
}
|
||||||
|
this.logger.info(`本次成功部署 ${count} 个${req.targetName}`);
|
||||||
|
return { result: "success", deployedList };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OutputVO = {
|
export type OutputVO = {
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-huawei
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -24,5 +24,5 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-iframe
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/jdcloud
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/jdcloud",
|
"name": "@certd/jdcloud",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"description": "jdcloud openApi sdk",
|
"description": "jdcloud openApi sdk",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
@@ -56,5 +56,5 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-k8s
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -32,5 +32,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
|
||||||
|
|
||||||
|
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-server
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -28,11 +28,11 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.39.3",
|
"@certd/acme-client": "^1.39.7",
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@certd/pipeline": "^1.39.3",
|
"@certd/pipeline": "^1.39.7",
|
||||||
"@certd/plugin-lib": "^1.39.3",
|
"@certd/plugin-lib": "^1.39.7",
|
||||||
"@certd/plus-core": "^1.39.3",
|
"@certd/plus-core": "^1.39.7",
|
||||||
"@midwayjs/cache": "3.14.0",
|
"@midwayjs/cache": "3.14.0",
|
||||||
"@midwayjs/core": "3.20.11",
|
"@midwayjs/core": "3.20.11",
|
||||||
"@midwayjs/i18n": "3.20.13",
|
"@midwayjs/i18n": "3.20.13",
|
||||||
@@ -64,5 +64,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { IAccessService } from '@certd/pipeline';
|
|||||||
export class AccessGetter implements IAccessService {
|
export class AccessGetter implements IAccessService {
|
||||||
userId: number;
|
userId: number;
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
getter: <T>(id: any, userId?: number, projectId?: number) => Promise<T>;
|
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean) => Promise<T>;
|
||||||
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number) => Promise<any>) {
|
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean) => Promise<any>) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.projectId = projectId;
|
this.projectId = projectId;
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -46,5 +46,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-cert
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.39.3",
|
"@certd/acme-client": "^1.39.7",
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@certd/pipeline": "^1.39.3",
|
"@certd/pipeline": "^1.39.7",
|
||||||
"@certd/plugin-lib": "^1.39.3",
|
"@certd/plugin-lib": "^1.39.7",
|
||||||
"psl": "^1.9.0",
|
"psl": "^1.9.0",
|
||||||
"punycode.js": "^2.3.1"
|
"punycode.js": "^2.3.1"
|
||||||
},
|
},
|
||||||
@@ -38,5 +38,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-lib
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -22,10 +22,10 @@
|
|||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@alicloud/tea-util": "^1.4.11",
|
"@alicloud/tea-util": "^1.4.11",
|
||||||
"@aws-sdk/client-s3": "^3.964.0",
|
"@aws-sdk/client-s3": "^3.964.0",
|
||||||
"@certd/acme-client": "^1.39.3",
|
"@certd/acme-client": "^1.39.7",
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@certd/pipeline": "^1.39.3",
|
"@certd/pipeline": "^1.39.7",
|
||||||
"@certd/plus-core": "^1.39.3",
|
"@certd/plus-core": "^1.39.7",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
"ali-oss": "^6.22.0",
|
"ali-oss": "^6.22.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
@@ -57,5 +57,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "f47773348312d5480b248c02beed9e162f59e3c7"
|
"gitHead": "adc3e6118b941818926705c3536babfca117c247"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ const formats = {
|
|||||||
jks: ["jks"],
|
jks: ["jks"],
|
||||||
p7b: ["p7b", "key"],
|
p7b: ["p7b", "key"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SimpleCertDetail = {
|
||||||
|
notBefore: Date;
|
||||||
|
notAfter: Date;
|
||||||
|
domains: string[];
|
||||||
|
};
|
||||||
export class CertReader {
|
export class CertReader {
|
||||||
cert: CertInfo;
|
cert: CertInfo;
|
||||||
|
|
||||||
@@ -116,6 +122,15 @@ export class CertReader {
|
|||||||
return CertReader.readCertDetail(crt);
|
return CertReader.readCertDetail(crt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSimpleDetail() {
|
||||||
|
const { detail } = this.getCrtDetail();
|
||||||
|
return {
|
||||||
|
notBefore: detail.notBefore,
|
||||||
|
notAfter: detail.notAfter,
|
||||||
|
domains: this.getAllDomains(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static readCertDetail(crt: string) {
|
static readCertDetail(crt: string) {
|
||||||
const detail = crypto.readCertificateInfo(crt.toString());
|
const detail = crypto.readCertificateInfo(crt.toString());
|
||||||
const effective = detail.notBefore;
|
const effective = detail.notBefore;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
|||||||
pager?: boolean;
|
pager?: boolean;
|
||||||
component?: any;
|
component?: any;
|
||||||
value?: any;
|
value?: any;
|
||||||
|
pageSize?: number;
|
||||||
}) {
|
}) {
|
||||||
const title = opts?.title || "请选择";
|
const title = opts?.title || "请选择";
|
||||||
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
|
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
|
||||||
@@ -71,6 +72,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
|||||||
search,
|
search,
|
||||||
pager,
|
pager,
|
||||||
multi,
|
multi,
|
||||||
|
pageSize: opts?.pageSize,
|
||||||
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
||||||
...opts.component,
|
...opts.component,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
|
||||||
|
* remote-select默认pageSize设置为50,阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
|
||||||
|
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
|
||||||
|
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
|
||||||
|
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
|
||||||
|
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
|
||||||
|
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-client",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
"zod-defaults": "^0.1.3"
|
"zod-defaults": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.39.3",
|
"@certd/lib-iframe": "^1.39.7",
|
||||||
"@certd/pipeline": "^1.39.3",
|
"@certd/pipeline": "^1.39.7",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ const props = defineProps<
|
|||||||
search?: boolean;
|
search?: boolean;
|
||||||
pager?: boolean;
|
pager?: boolean;
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
|
pageSize?: number;
|
||||||
} & ComponentPropsType
|
} & ComponentPropsType
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ const loading = ref(false);
|
|||||||
const pagerRef: Ref = ref({
|
const pagerRef: Ref = ref({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
total: 0,
|
total: 0,
|
||||||
pageSize: 100,
|
pageSize: props.pageSize || 50,
|
||||||
});
|
});
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
@@ -181,7 +182,7 @@ const getOptions = async () => {
|
|||||||
pagerRef.value.pageNo = res.pageNo ?? 1;
|
pagerRef.value.pageNo = res.pageNo ?? 1;
|
||||||
}
|
}
|
||||||
if (res.pageSize != null) {
|
if (res.pageSize != null) {
|
||||||
pagerRef.value.pageSize = res.pageSize ?? 100;
|
pagerRef.value.pageSize = res.pageSize ?? pageSize;
|
||||||
}
|
}
|
||||||
if (res.total != null) {
|
if (res.total != null) {
|
||||||
pagerRef.value.total = res.total ?? list.length;
|
pagerRef.value.total = res.total ?? list.length;
|
||||||
@@ -236,7 +237,7 @@ watch(
|
|||||||
let changed = oldForm == null || optionsRef.value.length == 0;
|
let changed = oldForm == null || optionsRef.value.length == 0;
|
||||||
if (props.watches && props.watches.length > 0) {
|
if (props.watches && props.watches.length > 0) {
|
||||||
for (const key of props.watches) {
|
for (const key of props.watches) {
|
||||||
if (oldForm && form[key] != oldForm[key]) {
|
if (oldForm && JSON.stringify(form[key]) != JSON.stringify(oldForm[key])) {
|
||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<div class="rounded pl-3 pr-3 px-2 py-1 flex-center flex pointer items-center bg-accent h-10 button-text" title="当前项目">
|
<div class="rounded pl-3 pr-3 px-2 py-1 flex-center flex pointer items-center bg-accent h-10 button-text" title="当前项目">
|
||||||
<!-- <fs-icon icon="ion:apps" class="mr-1"></fs-icon> -->
|
<!-- <fs-icon icon="ion:apps" class="mr-1"></fs-icon> -->
|
||||||
当前项目:{{ projectStore.currentProject?.name || "..." }}
|
<span class="hidden md:flex"> 当前项目:</span>
|
||||||
|
<span class="text-ellipsis">{{ projectStore.currentProject?.name || "..." }}</span>
|
||||||
<fs-icon :icon="currentIcon" class="ml-5"></fs-icon>
|
<fs-icon :icon="currentIcon" class="ml-5"></fs-icon>
|
||||||
</div>
|
</div>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
@@ -58,7 +59,7 @@ const currentIcon = computed(() => {
|
|||||||
<style lang="less">
|
<style lang="less">
|
||||||
.project-selector {
|
.project-selector {
|
||||||
&.button-text {
|
&.button-text {
|
||||||
min-width: 150px;
|
min-width: 100px;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<div class="fs-user-info">{{ t("user.greeting") }},{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }}</div>
|
<div class="fs-user-info" @click="goUserProfile">{{ t("user.greeting") }},{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }}</div>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item>
|
<a-menu-item>
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ const avatar = computed(() => {
|
|||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
await userStore.logout(true);
|
await userStore.logout(true);
|
||||||
}
|
}
|
||||||
|
function goUserProfile() {
|
||||||
|
router.push("/certd/mine/user-profile");
|
||||||
|
}
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
|
|
||||||
@@ -90,7 +93,7 @@ const projectStore = useProjectStore();
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #user-dropdown>
|
<template #user-dropdown>
|
||||||
<UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" />
|
<UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" @user-profile="goUserProfile" />
|
||||||
</template>
|
</template>
|
||||||
<template #lock-screen>
|
<template #lock-screen>
|
||||||
<LockScreen :avatar @to-login="handleLogout" />
|
<LockScreen :avatar @to-login="handleLogout" />
|
||||||
|
|||||||
@@ -103,4 +103,6 @@ export default {
|
|||||||
deviceNameHelper: "Please enter the device name, used to identify the device",
|
deviceNameHelper: "Please enter the device name, used to identify the device",
|
||||||
passkeyRegisterHelper: "Site domain change will invalidate passkey",
|
passkeyRegisterHelper: "Site domain change will invalidate passkey",
|
||||||
userInfo: "User Info",
|
userInfo: "User Info",
|
||||||
|
securitySettingTip: "2FA Setting",
|
||||||
|
securitySetting: "2FA Setting",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -795,6 +795,8 @@ export default {
|
|||||||
reverseProxyEmpty: "No reverse proxy list configured",
|
reverseProxyEmpty: "No reverse proxy list configured",
|
||||||
environmentVars: "Environment Variables",
|
environmentVars: "Environment Variables",
|
||||||
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
|
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
|
||||||
|
|
||||||
|
bindUrl: "Bind URL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
|
|||||||
@@ -105,4 +105,6 @@ export default {
|
|||||||
deviceNameHelper: "请输入当前设备名称,绑定多个时好做区分",
|
deviceNameHelper: "请输入当前设备名称,绑定多个时好做区分",
|
||||||
passkeyRegisterHelper: "1、站点域名变更会导致passkey失效;\n2、同一设备同一个用户绑定多次只有最后一次的有效,之前绑定的会失效,需要手动删除",
|
passkeyRegisterHelper: "1、站点域名变更会导致passkey失效;\n2、同一设备同一个用户绑定多次只有最后一次的有效,之前绑定的会失效,需要手动删除",
|
||||||
userInfo: "账号信息",
|
userInfo: "账号信息",
|
||||||
|
securitySettingTip: "2FA设置",
|
||||||
|
securitySetting: "2FA设置",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -589,11 +589,11 @@ export default {
|
|||||||
userValidityPeriodHelper: "有效期内用户可正常使用,失效后用户的流水线将被停用",
|
userValidityPeriodHelper: "有效期内用户可正常使用,失效后用户的流水线将被停用",
|
||||||
enableUsernameRegistration: "开启用户名注册",
|
enableUsernameRegistration: "开启用户名注册",
|
||||||
enableEmailRegistration: "开启邮箱注册",
|
enableEmailRegistration: "开启邮箱注册",
|
||||||
proFeature: "专业版功能",
|
proFeature: "Certd专业版功能",
|
||||||
emailServerSetup: "设置邮箱服务器",
|
emailServerSetup: "设置邮箱服务器",
|
||||||
enableSmsLoginRegister: "开启手机号登录、注册",
|
enableSmsLoginRegister: "开启手机号登录、注册",
|
||||||
defaultLoginType: "默认登录方式",
|
defaultLoginType: "默认登录方式",
|
||||||
commFeature: "商业版功能",
|
commFeature: "Certd商业版功能",
|
||||||
smsProvider: "短信提供商",
|
smsProvider: "短信提供商",
|
||||||
aliyunSms: "阿里云短信",
|
aliyunSms: "阿里云短信",
|
||||||
tencentSms: "腾讯云短信",
|
tencentSms: "腾讯云短信",
|
||||||
@@ -804,6 +804,7 @@ export default {
|
|||||||
reverseProxyEmpty: "未配置反向代理",
|
reverseProxyEmpty: "未配置反向代理",
|
||||||
environmentVars: "环境变量",
|
environmentVars: "环境变量",
|
||||||
environmentVarsHelper: "配置运行时环境变量,每行一个,格式:KEY=VALUE",
|
environmentVarsHelper: "配置运行时环境变量,每行一个,格式:KEY=VALUE",
|
||||||
|
bindUrl: "绑定URL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
@@ -812,7 +813,7 @@ export default {
|
|||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
domainManager: "域名管理",
|
domainManager: "域名管理",
|
||||||
domainDescription: "管理域名的校验方式,用于申请证书时自动选择验证方式",
|
domainDescription: "流水线校验方式选择“自动选择”时此处配置才有用,支持自动导入;注意:这里只需要管理主域名即可,子域名不要填写(子域名托管和免费二级子域名除外)",
|
||||||
domain: "域名",
|
domain: "域名",
|
||||||
challengeType: "校验类型",
|
challengeType: "校验类型",
|
||||||
dnsProviderType: "DNS提供商类型",
|
dnsProviderType: "DNS提供商类型",
|
||||||
|
|||||||
@@ -88,13 +88,13 @@ export default {
|
|||||||
activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请",
|
activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请",
|
||||||
bind_account: "绑定账号",
|
bind_account: "绑定账号",
|
||||||
transfer_vip: '然后"转移VIP"即可',
|
transfer_vip: '然后"转移VIP"即可',
|
||||||
needVipTip: "此为专业版功能,请先开通专业版",
|
needVipTip: "此为Certd专业版功能,请先开通Certd专业版",
|
||||||
manual_activation: "激活码手动激活",
|
manual_activation: "激活码手动激活",
|
||||||
close: "关闭",
|
close: "关闭",
|
||||||
have_activation_code: "已经有激活码了?",
|
have_activation_code: "已经有激活码了?",
|
||||||
buy: "立即购买",
|
buy: "立即购买",
|
||||||
already_plus: "已经是专业版了,是否升级为商业版?注意:专业版时长将被覆盖",
|
already_plus: "已经是Certd专业版了,是否升级为商业版?注意:Certd专业版时长将被覆盖",
|
||||||
already_comm: "已经是商业版了,不能降级为专业版",
|
already_comm: "已经是Certd商业版了,不能降级为专业版",
|
||||||
already_perpetual_plus: "您已经是永久专业版了,无法继续升级",
|
already_perpetual_plus: "您已经是永久专业版了,无法继续升级",
|
||||||
confirm: "确认",
|
confirm: "确认",
|
||||||
not_effective: "VIP没有生效/时长未同步?",
|
not_effective: "VIP没有生效/时长未同步?",
|
||||||
|
|||||||
@@ -272,14 +272,27 @@ export const useSettingStore = defineStore({
|
|||||||
},
|
},
|
||||||
async checkUrlBound() {
|
async checkUrlBound() {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const settingStore = useSettingStore();
|
|
||||||
if (!userStore.isAdmin) {
|
if (!userStore.isAdmin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const bindUrl = this.installInfo.bindUrl;
|
||||||
|
const bindUrl2 = this.installInfo.bindUrl2;
|
||||||
|
if (!bindUrl) {
|
||||||
|
//绑定url
|
||||||
|
await this.doBindUrl("url");
|
||||||
|
} else {
|
||||||
|
//检查当前url 是否与绑定的url一致
|
||||||
|
const url = window.location.href;
|
||||||
|
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
|
||||||
|
this.openBindUrlModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
openBindUrlModal() {
|
||||||
const event: any = { ModalRef: null };
|
const event: any = { ModalRef: null };
|
||||||
mitter.emit("getModal", event);
|
mitter.emit("getModal", event);
|
||||||
const Modal = event.ModalRef;
|
const Modal = event.ModalRef;
|
||||||
let modalRef: any = null;
|
|
||||||
const bindUrl = this.installInfo.bindUrl;
|
const bindUrl = this.installInfo.bindUrl;
|
||||||
const bindUrl2 = this.installInfo.bindUrl2;
|
const bindUrl2 = this.installInfo.bindUrl2;
|
||||||
|
|
||||||
@@ -289,57 +302,47 @@ export const useSettingStore = defineStore({
|
|||||||
modalRef.destroy();
|
modalRef.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const modalRef: any = Modal.warning({
|
||||||
if (!bindUrl) {
|
title: "URL地址未绑定,是否绑定此地址?",
|
||||||
//绑定url
|
width: 500,
|
||||||
await this.doBindUrl("url");
|
keyboard: false,
|
||||||
} else {
|
content: () => {
|
||||||
//检查当前url 是否与绑定的url一致
|
return (
|
||||||
const url = window.location.href;
|
<div class="p-4">
|
||||||
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
|
<div class="flex items-center justify-between">
|
||||||
modalRef = Modal.warning({
|
<span>
|
||||||
title: "URL地址未绑定,是否绑定此地址?",
|
绑定地址1:
|
||||||
width: 500,
|
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
|
||||||
keyboard: false,
|
</span>
|
||||||
content: () => {
|
<a-button type="primary" onClick={() => doBindRequest("url")}>
|
||||||
return (
|
绑定到地址1
|
||||||
<div class="p-4">
|
</a-button>
|
||||||
<div class="flex items-center justify-between">
|
</div>
|
||||||
<span>
|
<div class="flex items-center justify-between mt-3">
|
||||||
绑定地址1:
|
<span>
|
||||||
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
|
绑定地址2:
|
||||||
</span>
|
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
|
||||||
<a-button type="primary" onClick={() => doBindRequest("url")}>
|
</span>
|
||||||
绑定到地址1
|
<a-button type="primary" onClick={() => doBindRequest("url2")}>
|
||||||
</a-button>
|
绑定到地址2
|
||||||
</div>
|
</a-button>
|
||||||
<div class="flex items-center justify-between mt-3">
|
</div>
|
||||||
<span>
|
</div>
|
||||||
绑定地址2:
|
);
|
||||||
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
|
},
|
||||||
</span>
|
onOk: async () => {
|
||||||
<a-button type="primary" onClick={() => doBindRequest("url2")}>
|
// await this.doBindUrl();
|
||||||
绑定到地址2
|
window.location.href = bindUrl;
|
||||||
</a-button>
|
},
|
||||||
</div>
|
okButtonProps: {
|
||||||
</div>
|
danger: true,
|
||||||
);
|
},
|
||||||
},
|
okText: "不,回到原来的地址",
|
||||||
onOk: async () => {
|
cancelText: "不,回到原来的地址",
|
||||||
// await this.doBindUrl();
|
onCancel: () => {
|
||||||
window.location.href = bindUrl;
|
window.location.href = bindUrl;
|
||||||
},
|
},
|
||||||
okButtonProps: {
|
});
|
||||||
danger: true,
|
|
||||||
},
|
|
||||||
okText: "不,回到原来的地址",
|
|
||||||
cancelText: "不,回到原来的地址",
|
|
||||||
onCancel: () => {
|
|
||||||
window.location.href = bindUrl;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async loadProductInfo() {
|
async loadProductInfo() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -118,3 +118,16 @@ span.fs-icon-svg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button.ant-btn.ant-btn-default.isPlus{
|
||||||
|
color: #c5913f;
|
||||||
|
border: 1px solid #c5913f;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: hsl(240 5.9% 90%);
|
||||||
|
color: rgba(50, 54, 57, 0.25);
|
||||||
|
background-color: rgba(50, 54, 57, 0.04);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
hoverDelay: 500,
|
hoverDelay: 500,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ logout: [] }>();
|
const emit = defineEmits<{ logout: []; userProfile: [] }>();
|
||||||
|
|
||||||
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } = usePreferences();
|
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } = usePreferences();
|
||||||
const lockStore = useLockStore();
|
const lockStore = useLockStore();
|
||||||
@@ -132,6 +132,11 @@ function handleSubmitLogout() {
|
|||||||
logoutModalApi.close();
|
logoutModalApi.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUserProfile() {
|
||||||
|
emit("userProfile");
|
||||||
|
openPopover.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (enableShortcutKey.value) {
|
if (enableShortcutKey.value) {
|
||||||
const keys = useMagicKeys();
|
const keys = useMagicKeys();
|
||||||
whenever(keys["Alt+KeyQ"]!, () => {
|
whenever(keys["Alt+KeyQ"]!, () => {
|
||||||
@@ -173,7 +178,7 @@ if (enableShortcutKey.value) {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
|
<DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
|
||||||
<div ref="refContent">
|
<div ref="refContent">
|
||||||
<DropdownMenuLabel class="flex items-center p-3">
|
<DropdownMenuLabel class="flex items-center p-3 pointer" @click="handleUserProfile">
|
||||||
<VbenAvatar :alt="text" :src="avatar" class="size-12" dot dot-class="bottom-0 right-1 border-2 size-4 bg-green-500" />
|
<VbenAvatar :alt="text" :src="avatar" class="size-12" dot dot-class="bottom-0 right-1 border-2 size-4 bg-green-500" />
|
||||||
<div class="ml-2 w-full">
|
<div class="ml-2 w-full">
|
||||||
<div v-if="tagText || text || $slots.tagText" class="text-foreground mb-1 flex items-center text-sm font-medium">
|
<div v-if="tagText || text || $slots.tagText" class="text-foreground mb-1 flex items-center text-sm font-medium">
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
form.type = props.type;
|
form.type = props.type;
|
||||||
|
delete form.access;
|
||||||
|
delete form.keyId;
|
||||||
const res = await context.api.UpdateObj(form);
|
const res = await context.api.UpdateObj(form);
|
||||||
lastResRef.value = res;
|
lastResRef.value = res;
|
||||||
return res;
|
return res;
|
||||||
@@ -30,6 +32,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
form.type = props.type;
|
form.type = props.type;
|
||||||
|
delete form.access;
|
||||||
const res = await context.api.AddObj(form);
|
const res = await context.api.AddObj(form);
|
||||||
lastResRef.value = res;
|
lastResRef.value = res;
|
||||||
return res;
|
return res;
|
||||||
@@ -70,6 +73,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
width: "1050px",
|
width: "1050px",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
@@ -89,6 +93,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
}, // 点击行
|
}, // 点击行
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
remove: {
|
||||||
|
confirmMessage: "授权如果已经被使用,可能会导致流水线无法正常运行,请谨慎操作",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const editRequest = async (req: EditReq) => {
|
const editRequest = async (req: EditReq) => {
|
||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
|
delete form.access;
|
||||||
const res = await api.UpdateObj(form);
|
const res = await api.UpdateObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
|
|
||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
|
delete form.access;
|
||||||
const res = await api.AddObj(form);
|
const res = await api.AddObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const editRequest = async (req: EditReq) => {
|
const editRequest = async (req: EditReq) => {
|
||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
|
delete form.body;
|
||||||
const res = await api.UpdateObj(form);
|
const res = await api.UpdateObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
@@ -26,6 +27,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
|
|
||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
|
delete form.body;
|
||||||
const res = await api.AddObj(form);
|
const res = await api.AddObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,11 +35,15 @@
|
|||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons gap-2">
|
||||||
<a-button type="primary" class="action-btn" @click="doUpdate">
|
<a-button type="primary" class="action-btn" @click="doUpdate">
|
||||||
{{ t("authentication.updateProfile") }}
|
{{ t("authentication.updateProfile") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<change-password-button class="ml-10" :show-button="true" />
|
<change-password-button :show-button="true" />
|
||||||
|
|
||||||
|
<a-button type="primary" class="action-btn" @click="goSecuritySetting">
|
||||||
|
{{ t("authentication.securitySettingTip") }}
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="passkey-list">
|
<div class="passkey-list">
|
||||||
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item">
|
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item">
|
||||||
<div class="passkey-icon">
|
<div class="passkey-icon hidden md:flex">
|
||||||
<fs-icon icon="ion:finger-print" class="icon" />
|
<fs-icon icon="ion:finger-print" class="icon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="passkey-info">
|
<div class="passkey-info">
|
||||||
@@ -142,6 +146,7 @@ import { useSettingStore } from "/@/store/settings";
|
|||||||
import { isEmpty } from "lodash-es";
|
import { isEmpty } from "lodash-es";
|
||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -175,6 +180,11 @@ function doUpdate() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
function goSecuritySetting() {
|
||||||
|
router.push("/certd/mine/security");
|
||||||
|
}
|
||||||
|
|
||||||
const oauthBounds = ref([]);
|
const oauthBounds = ref([]);
|
||||||
const oauthProviders = ref([]);
|
const oauthProviders = ref([]);
|
||||||
|
|
||||||
@@ -278,20 +288,32 @@ async function doRegisterPasskey(deviceName: string) {
|
|||||||
// type: "public-key",
|
// type: "public-key",
|
||||||
// }));
|
// }));
|
||||||
|
|
||||||
const credential = await (navigator.credentials as any).create({
|
console.log("passkey register options:", options, JSON.stringify(options));
|
||||||
publicKey: {
|
const publicKey = {
|
||||||
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
|
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
|
||||||
rp: options.rp,
|
rp: options.rp,
|
||||||
pubKeyCredParams: options.pubKeyCredParams,
|
pubKeyCredParams: options.pubKeyCredParams,
|
||||||
timeout: options.timeout || 60000,
|
timeout: options.timeout || 60000,
|
||||||
attestation: options.attestation,
|
// attestation: options.attestation,
|
||||||
// excludeCredentials: excludeCredentials,
|
// excludeCredentials: excludeCredentials,
|
||||||
user: {
|
// extensions: options.extensions,
|
||||||
id: new TextEncoder().encode(options.userId + ""),
|
// authenticatorSelection: options.authenticatorSelection,
|
||||||
name: userInfo.value.username,
|
// hints: options.hints,
|
||||||
displayName: deviceName,
|
user: {
|
||||||
},
|
id: new TextEncoder().encode(options.userId + ""),
|
||||||
|
name: userInfo.value.username + "@" + deviceName,
|
||||||
|
displayName: deviceName,
|
||||||
},
|
},
|
||||||
|
// 关键配置在这里 👇
|
||||||
|
authenticatorSelection: {
|
||||||
|
residentKey: "required", // 或 "preferred",请求创建可发现凭证
|
||||||
|
requireResidentKey: true, // 为兼容旧浏览器,设置与 residentKey 相同的值
|
||||||
|
userVerification: "preferred", // 用户验证策略
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log("passkey register publicKey:", publicKey, JSON.stringify(publicKey));
|
||||||
|
const credential = await (navigator.credentials as any).create({
|
||||||
|
publicKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!credential) {
|
if (!credential) {
|
||||||
@@ -310,6 +332,7 @@ async function doRegisterPasskey(deviceName: string) {
|
|||||||
console.log("credential", credential, response);
|
console.log("credential", credential, response);
|
||||||
|
|
||||||
const verifyRes: any = await api.verifyPasskeyRegistration(response, options.challenge, deviceName);
|
const verifyRes: any = await api.verifyPasskeyRegistration(response, options.challenge, deviceName);
|
||||||
|
console.log("verifyRes:", verifyRes, JSON.stringify(verifyRes));
|
||||||
await loadPasskeys();
|
await loadPasskeys();
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("Passkey注册失败:", e);
|
console.error("Passkey注册失败:", e);
|
||||||
@@ -721,7 +744,6 @@ onMounted(async () => {
|
|||||||
.passkey-icon {
|
.passkey-icon {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||||
@@ -749,8 +771,8 @@ onMounted(async () => {
|
|||||||
.passkey-meta {
|
.passkey-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 4px;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta-item {
|
.meta-item {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const editRequest = async (req: EditReq) => {
|
const editRequest = async (req: EditReq) => {
|
||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
|
delete form.body;
|
||||||
const res = await api.UpdateObj(form);
|
const res = await api.UpdateObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
@@ -21,6 +22,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
|
|
||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
|
delete form.body;
|
||||||
const res = await api.AddObj(form);
|
const res = await api.AddObj(form);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|||||||
+2
@@ -14,6 +14,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
const editRequest = async (req: EditReq) => {
|
const editRequest = async (req: EditReq) => {
|
||||||
const { form, row } = req;
|
const { form, row } = req;
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
|
delete form.body;
|
||||||
const res = await context.api.UpdateObj(form);
|
const res = await context.api.UpdateObj(form);
|
||||||
lastResRef.value = res;
|
lastResRef.value = res;
|
||||||
return res;
|
return res;
|
||||||
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
|
|
||||||
const addRequest = async (req: AddReq) => {
|
const addRequest = async (req: AddReq) => {
|
||||||
const { form } = req;
|
const { form } = req;
|
||||||
|
delete form.body;
|
||||||
const res = await context.api.AddObj(form);
|
const res = await context.api.AddObj(form);
|
||||||
lastResRef.value = res;
|
lastResRef.value = res;
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ function formatDate(dateStr: string): string {
|
|||||||
.cert-detail {
|
.cert-detail {
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
table-layout: fixed;
|
table-layout: fixed !important;
|
||||||
|
}
|
||||||
|
.ant-descriptions-item-label {
|
||||||
|
width: 90px;
|
||||||
}
|
}
|
||||||
.fingerprint {
|
.fingerprint {
|
||||||
.label {
|
.label {
|
||||||
|
|||||||
@@ -29,15 +29,15 @@ const pipelineOptions: PipelineOptions = {
|
|||||||
onLoaded(detail);
|
onLoaded(detail);
|
||||||
return {
|
return {
|
||||||
pipeline: {
|
pipeline: {
|
||||||
id: detail.pipeline.id,
|
|
||||||
stages: [],
|
stages: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
...JSON.parse(detail.pipeline.content || "{}"),
|
...JSON.parse(detail.pipeline.content || "{}"),
|
||||||
type: detail.pipeline.type,
|
id: detail.pipeline.id,
|
||||||
from: detail.pipeline.from,
|
|
||||||
userId: detail.pipeline.userId,
|
userId: detail.pipeline.userId,
|
||||||
projectId: detail.pipeline.projectId,
|
projectId: detail.pipeline.projectId,
|
||||||
},
|
},
|
||||||
|
type: detail.pipeline.type,
|
||||||
|
from: detail.pipeline.from,
|
||||||
validTime: detail.pipeline.validTime,
|
validTime: detail.pipeline.validTime,
|
||||||
webhookKey: detail.pipeline.webhookKey,
|
webhookKey: detail.pipeline.webhookKey,
|
||||||
id: detail.pipeline.id,
|
id: detail.pipeline.id,
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@
|
|||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex-col h-100 overflow-hidden md:ml-5 md:mr-5 step-form-body">
|
<div class="flex-col h-100 overflow-hidden md:ml-5 md:mr-5 step-form-body">
|
||||||
<a-tabs v-model:active-key="pluginGroupActive" tab-position="left" class="flex-1 overflow-hidden">
|
<a-tabs v-model:active-key="pluginGroupActive" tab-position="left" class="flex-1 overflow-hidden h-full">
|
||||||
<template v-for="group of computedPluginGroups" :key="group.key">
|
<template v-for="group of computedPluginGroups" :key="group.key">
|
||||||
<a-tab-pane v-if="(group.key === 'admin' && userStore.isAdmin) || group.key !== 'admin'" :key="group.key" class="scroll-y">
|
<a-tab-pane v-if="(group.key === 'admin' && userStore.isAdmin) || group.key !== 'admin'" :key="group.key" class="scroll-y">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
import { reactive, ref } from "vue";
|
||||||
|
|
||||||
|
export class CopyeStore {
|
||||||
|
type: "step" | "steps" | "task" | "tasks";
|
||||||
|
target: any;
|
||||||
|
|
||||||
|
getCopyedCount() {
|
||||||
|
if (this.type === "step") {
|
||||||
|
return 1;
|
||||||
|
} else if (this.type === "steps") {
|
||||||
|
return this.target.length;
|
||||||
|
} else if (this.type === "task") {
|
||||||
|
return 1;
|
||||||
|
} else if (this.type === "tasks") {
|
||||||
|
return this.target.length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStep(target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this.type = "step";
|
||||||
|
}
|
||||||
|
setSteps(target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this.type = "steps";
|
||||||
|
}
|
||||||
|
setTask(target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this.type = "task";
|
||||||
|
}
|
||||||
|
setTasks(target: any) {
|
||||||
|
this.target = target;
|
||||||
|
this.type = "tasks";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Copyed: any = reactive(new CopyeStore());
|
||||||
+54
-9
@@ -29,7 +29,17 @@
|
|||||||
<a-form-item :value="currentTask.steps" name="steps" label="" :wrapper-col="{ span: 24 }" :rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]">
|
<a-form-item :value="currentTask.steps" name="steps" label="" :wrapper-col="{ span: 24 }" :rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]">
|
||||||
<a-descriptions title="任务步骤" size="small">
|
<a-descriptions title="任务步骤" size="small">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-button type="primary" @click="stepAdd(currentTask)">添加步骤</a-button>
|
<div class="flex gap-1">
|
||||||
|
<a-button type="primary" @click="stepAdd(currentTask)">添加步骤</a-button>
|
||||||
|
<a-tooltip title="复制此任务下的所有步骤">
|
||||||
|
<a-button type="default" class="isPlus" :disabled="currentTask.steps?.length === 0" @click="stepsCopy(currentTask)">复制</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="可以从其他任务复制后到此处粘贴">
|
||||||
|
<a-badge :count="Copyed.getCopyedCount()">
|
||||||
|
<a-button type="default" class="isPlus" :disabled="Copyed.getCopyedCount() === 0" @click="stepPaste(currentTask)">粘贴</a-button>
|
||||||
|
</a-badge>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
<v-draggable v-model="currentTask.steps" class="step-list" handle=".handle" item-key="id" :disabled="!settingStore.isPlus">
|
<v-draggable v-model="currentTask.steps" class="step-list" handle=".handle" item-key="id" :disabled="!settingStore.isPlus">
|
||||||
@@ -68,14 +78,15 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { provide, Ref, ref } from "vue";
|
import { provide, Ref, ref } from "vue";
|
||||||
import * as _ from "lodash-es";
|
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import PiStepForm from "../step-form/index.vue";
|
import PiStepForm from "../step-form/index.vue";
|
||||||
import { Modal } from "ant-design-vue";
|
import { message, Modal } from "ant-design-vue";
|
||||||
import VDraggable from "vuedraggable";
|
import VDraggable from "vuedraggable";
|
||||||
import { useUserStore } from "/@/store/user";
|
import { useUserStore } from "/@/store/user";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { filter } from "lodash-es";
|
import { filter } from "lodash-es";
|
||||||
|
import { Copyed } from "./copy";
|
||||||
|
import { cloneDeep, merge } from "lodash-es";
|
||||||
export default {
|
export default {
|
||||||
name: "PiTaskForm",
|
name: "PiTaskForm",
|
||||||
components: { PiStepForm, VDraggable },
|
components: { PiStepForm, VDraggable },
|
||||||
@@ -89,6 +100,7 @@ export default {
|
|||||||
setup(props: any, ctx: any) {
|
setup(props: any, ctx: any) {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
|
|
||||||
function useStep() {
|
function useStep() {
|
||||||
const stepFormRef: Ref<any> = ref(null);
|
const stepFormRef: Ref<any> = ref(null);
|
||||||
const currentStepIndex = ref(0);
|
const currentStepIndex = ref(0);
|
||||||
@@ -106,10 +118,42 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const stepCopy = (task: any, step: any, stepIndex: any) => {
|
const stepCopy = (task: any, step: any, stepIndex: any) => {
|
||||||
step = _.cloneDeep(step);
|
settingStore.checkPlus();
|
||||||
|
step = cloneDeep(step);
|
||||||
step.id = nanoid();
|
step.id = nanoid();
|
||||||
step.title = step.title + "_copy";
|
Copyed.type = "step";
|
||||||
stepAdd(task, step);
|
Copyed.target = step;
|
||||||
|
message.success("步骤配置复制成功,您可以到其他任务编辑页面进行粘贴");
|
||||||
|
};
|
||||||
|
|
||||||
|
const stepsCopy = (task: any) => {
|
||||||
|
settingStore.checkPlus();
|
||||||
|
const steps = cloneDeep(task.steps);
|
||||||
|
Copyed.type = "steps";
|
||||||
|
Copyed.target = steps;
|
||||||
|
message.success("本任务的所有步骤复制成功,您可以到其他任务编辑页面进行粘贴");
|
||||||
|
};
|
||||||
|
|
||||||
|
const stepPaste = (task: any) => {
|
||||||
|
settingStore.checkPlus();
|
||||||
|
if (!Copyed.target) {
|
||||||
|
message.error("请先复制");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Copyed.type === "step") {
|
||||||
|
const step = cloneDeep(Copyed.target);
|
||||||
|
step.id = nanoid();
|
||||||
|
step.title = step.title + "_copy";
|
||||||
|
task.steps.push(step);
|
||||||
|
} else if (Copyed.type === "steps") {
|
||||||
|
const steps = cloneDeep(Copyed.target);
|
||||||
|
for (const item of steps) {
|
||||||
|
item.id = nanoid();
|
||||||
|
item.title = item.title + "_copy";
|
||||||
|
task.steps.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message.success("粘贴成功");
|
||||||
};
|
};
|
||||||
const stepEdit = (task: any, step: any, stepIndex: any) => {
|
const stepEdit = (task: any, step: any, stepIndex: any) => {
|
||||||
currentStepIndex.value = stepIndex;
|
currentStepIndex.value = stepIndex;
|
||||||
@@ -144,7 +188,7 @@ export default {
|
|||||||
step.disabled = !!!step.disabled;
|
step.disabled = !!!step.disabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { stepAdd, stepEdit, stepCopy, stepDelete, toggleDisabled, stepFormRef };
|
return { stepAdd, stepEdit, stepCopy, stepDelete, toggleDisabled, stepFormRef, stepPaste, stepsCopy };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,7 +225,7 @@ export default {
|
|||||||
|
|
||||||
const taskOpen = (task: any, emit: any) => {
|
const taskOpen = (task: any, emit: any) => {
|
||||||
callback.value = emit;
|
callback.value = emit;
|
||||||
currentTask.value = _.merge({ steps: {} }, task);
|
currentTask.value = merge({ steps: {} }, task);
|
||||||
console.log("currentTaskOpen", currentTask.value);
|
console.log("currentTaskOpen", currentTask.value);
|
||||||
taskDrawerShow();
|
taskDrawerShow();
|
||||||
};
|
};
|
||||||
@@ -189,7 +233,7 @@ export default {
|
|||||||
const taskAdd = (emit: any, taskMerge: any) => {
|
const taskAdd = (emit: any, taskMerge: any) => {
|
||||||
mode.value = "add";
|
mode.value = "add";
|
||||||
const blankTask: any = { id: nanoid(), title: "新任务", steps: [], status: null };
|
const blankTask: any = { id: nanoid(), title: "新任务", steps: [], status: null };
|
||||||
const task: any = _.merge(blankTask, taskMerge);
|
const task: any = merge(blankTask, taskMerge);
|
||||||
taskOpen(task, emit);
|
taskOpen(task, emit);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -262,6 +306,7 @@ export default {
|
|||||||
wrapperCol: { span: 20 },
|
wrapperCol: { span: 20 },
|
||||||
...useTaskForm(),
|
...useTaskForm(),
|
||||||
...useStep(),
|
...useStep(),
|
||||||
|
Copyed,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ defineOptions({
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const projectIdStr = route.query.projectId as string;
|
const projectIdStr = route.query.projectId as string;
|
||||||
|
const migrate = route.query.migrate as string;
|
||||||
let projectId = Number(projectIdStr);
|
let projectId = Number(projectIdStr);
|
||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
@@ -116,7 +117,11 @@ onMounted(async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await loadProjectDetail();
|
await loadProjectDetail();
|
||||||
crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
|
|
||||||
|
if (migrate === "true") {
|
||||||
|
openTransferDialog();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
|
|||||||
@@ -46,21 +46,11 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane v-if="settingStore.sysPublic.passkeyEnabled && settingStore.isPlus" key="passkey" :tab="t('authentication.passkeyTab')">
|
|
||||||
<template v-if="formState.loginType === 'passkey'">
|
|
||||||
<div v-if="!passkeySupported" class="text-red-500 text-sm mt-2 text-center mb-10">
|
|
||||||
{{ t("authentication.passkeyNotSupported") }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button v-if="formState.loginType !== 'passkey'" type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
|
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
|
||||||
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
|
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-else type="primary" size="large" html-type="button" :loading="loading" class="login-button" :disabled="!passkeySupported" @click="handlePasskeyLogin">
|
|
||||||
{{ t("authentication.passkeyLogin") }}
|
|
||||||
</a-button>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<div class="mt-2 flex justify-between items-center">
|
<div class="mt-2 flex justify-between items-center">
|
||||||
@@ -73,7 +63,6 @@
|
|||||||
{{ t("authentication.forgotPassword") }}
|
{{ t("authentication.forgotPassword") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link v-if="hasRegisterTypeEnabled() && !queryBindCode" class="register" :to="{ name: 'register' }">
|
<router-link v-if="hasRegisterTypeEnabled() && !queryBindCode" class="register" :to="{ name: 'register' }">
|
||||||
{{ t("authentication.registerLink") }}
|
{{ t("authentication.registerLink") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -81,7 +70,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="!queryBindCode && settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="w-full">
|
<div v-if="!queryBindCode && (settingStore.sysPublic.oauthEnabled || settingStore.sysPublic.passkeyEnabled) && settingStore.isPlus" class="w-full">
|
||||||
<oauth-footer :oauth-only="isOauthOnly"></oauth-footer>
|
<oauth-footer :oauth-only="isOauthOnly"></oauth-footer>
|
||||||
</div>
|
</div>
|
||||||
</a-form>
|
</a-form>
|
||||||
@@ -195,57 +184,6 @@ const twoFactor = reactive({
|
|||||||
verifyCode: "",
|
verifyCode: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const passkeySupported = ref(false);
|
|
||||||
const passkeyEnabled = ref(false);
|
|
||||||
|
|
||||||
const checkPasskeySupport = () => {
|
|
||||||
passkeySupported.value = false;
|
|
||||||
if (typeof window !== "undefined" && "credentials" in navigator && "PublicKeyCredential" in window) {
|
|
||||||
passkeySupported.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePasskeyLogin = async () => {
|
|
||||||
if (!passkeySupported.value) {
|
|
||||||
notification.error({ message: t("authentication.passkeyNotSupported") });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
try {
|
|
||||||
const optionsResponse: any = await request({
|
|
||||||
url: "/passkey/generateAuthentication",
|
|
||||||
method: "post",
|
|
||||||
});
|
|
||||||
const options = optionsResponse;
|
|
||||||
|
|
||||||
const credential = await (navigator.credentials as any).get({
|
|
||||||
publicKey: {
|
|
||||||
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
|
|
||||||
rpId: options.rpId,
|
|
||||||
allowCredentials: options.allowCredentials || [],
|
|
||||||
timeout: options.timeout || 60000,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!credential) {
|
|
||||||
throw new Error("Passkey认证失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginRes: any = await UserApi.loginByPasskey({
|
|
||||||
credential,
|
|
||||||
challenge: options.challenge,
|
|
||||||
});
|
|
||||||
|
|
||||||
await userStore.onLoginSuccess(loginRes);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error("Passkey登录失败:", e);
|
|
||||||
notification.error({ message: e.message || "Passkey登录失败" });
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFinish = async () => {
|
const handleFinish = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
@@ -294,9 +232,7 @@ const isOauthOnly = computed(() => {
|
|||||||
return sysPublicSettings.oauthOnly && settingStore.isPlus && sysPublicSettings.oauthEnabled;
|
return sysPublicSettings.oauthOnly && settingStore.isPlus && sysPublicSettings.oauthEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {});
|
||||||
checkPasskeySupport();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="passkeyEnabled && isPlus" class="oauth-icon-button" :class="{ pointer: passkeySupported }" @click="handlePasskeyLogin">
|
||||||
|
<div><fs-icon icon="ion:finger-print-outline" :class="{ 'text-blue-600': passkeySupported, 'text-gray-400': !passkeySupported }" class="text-40" /></div>
|
||||||
|
<div class="ellipsis title" :title="t('authentication.passkeyLogin')" :class="{ 'text-gray-400': !passkeySupported }">{{ t("authentication.passkeyLogin") }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
import { useI18n } from "/@/locales";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import { notification } from "ant-design-vue";
|
||||||
|
import { request } from "/src/api/service";
|
||||||
|
import * as UserApi from "/src/store/user/api.user";
|
||||||
|
import { useUserStore } from "/src/store/user";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const passkeySupported = ref(false);
|
||||||
|
|
||||||
|
const passkeyEnabled = computed(() => settingStore.sysPublic.passkeyEnabled);
|
||||||
|
const isPlus = computed(() => settingStore.isPlus);
|
||||||
|
|
||||||
|
const checkPasskeySupport = () => {
|
||||||
|
passkeySupported.value = false;
|
||||||
|
if (typeof window !== "undefined" && "credentials" in navigator && "PublicKeyCredential" in window) {
|
||||||
|
passkeySupported.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasskeyLogin = async () => {
|
||||||
|
if (!passkeySupported.value) {
|
||||||
|
notification.error({ message: t("authentication.passkeyNotSupported") });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const optionsResponse: any = await request({
|
||||||
|
url: "/passkey/generateAuthentication",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
const options = optionsResponse;
|
||||||
|
|
||||||
|
console.log("passkey authentication options:", options, JSON.stringify(options));
|
||||||
|
const credential = await (navigator.credentials as any).get({
|
||||||
|
publicKey: {
|
||||||
|
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
|
||||||
|
rpId: options.rpId,
|
||||||
|
allowCredentials: options.allowCredentials || [],
|
||||||
|
timeout: options.timeout || 60000,
|
||||||
|
authenticatorSelection: {
|
||||||
|
residentKey: "required",
|
||||||
|
requireResidentKey: true,
|
||||||
|
userVerification: "preferred",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("passkey authentication credential:", credential, JSON.stringify(credential));
|
||||||
|
if (!credential) {
|
||||||
|
throw new Error("Passkey认证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginRes: any = await UserApi.loginByPasskey({
|
||||||
|
credential,
|
||||||
|
challenge: options.challenge,
|
||||||
|
});
|
||||||
|
|
||||||
|
await userStore.onLoginSuccess(loginRes);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error("Passkey登录失败:", e);
|
||||||
|
notification.error({ message: e.message || "Passkey登录失败" });
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkPasskeySupport();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
<div class="oauth-title">
|
<div class="oauth-title">
|
||||||
<div class="oauth-title-text">{{ computedTitle }}</div>
|
<div class="oauth-title-text">{{ computedTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center items-center gap-4">
|
<div class="flex justify-center items-center gap-4 flex-wrap md:flex-nowrap">
|
||||||
|
<passkey-login></passkey-login>
|
||||||
<template v-for="item in oauthProviderList" :key="item.type">
|
<template v-for="item in oauthProviderList" :key="item.type">
|
||||||
<div v-if="item.addonId" class="oauth-icon-button pointer" @click="goOauthLogin(item.name)">
|
<div v-if="item.addonId" class="oauth-icon-button pointer" @click="goOauthLogin(item.name)">
|
||||||
<div><fs-icon :icon="item.icon" class="text-blue-600 text-40" /></div>
|
<div><fs-icon :icon="item.icon" class="text-blue-600 text-40" /></div>
|
||||||
@@ -19,6 +20,7 @@ import * as api from "./api";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
import PasskeyLogin from "../login/passkey-login.vue";
|
||||||
|
|
||||||
const oauthProviderList = ref([]);
|
const oauthProviderList = ref([]);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ onMounted(() => {
|
|||||||
await settingStore.doBindUrl();
|
await settingStore.doBindUrl();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "更新成功",
|
message: "更新成功",
|
||||||
description: "专业版/商业版已激活",
|
description: "Certd专业版/商业版已激活",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="helper">SaaS模式:每个用户管理自己的流水线和授权资源,独立使用。</div>
|
<div class="helper">SaaS模式:每个用户管理自己的流水线和授权资源,独立使用。</div>
|
||||||
<div class="helper">企业模式:通过项目合作管理流水线证书和授权资源,所有用户视为企业内部员工。</div>
|
<div class="helper">企业模式:通过项目合作管理流水线证书和授权资源,所有用户视为企业内部员工。</div>
|
||||||
|
<div class="helper"><a @click="adminModeIntroOpen = true">更多管理模式介绍</a></div>
|
||||||
<div class="helper text-red-500">建议在开始使用时固定一个合适的模式,之后就不要随意切换了。</div>
|
<div class="helper text-red-500">建议在开始使用时固定一个合适的模式,之后就不要随意切换了。</div>
|
||||||
|
<div v-if="formState.public.adminMode === 'enterprise'" class="helper">设置为企业模式之后,之前创建的个人数据不会显示,您可以选择<a @click="goCurrentProject"> 将个人数据迁移到项目</a></div>
|
||||||
<div v-if="settingsStore.isComm" class="helper text-red-500">商业版不建议设置为企业模式,除非你确定要转成企业内部使用</div>
|
<div v-if="settingsStore.isComm" class="helper text-red-500">商业版不建议设置为企业模式,除非你确定要转成企业内部使用</div>
|
||||||
<div><a @click="adminModeIntroOpen = true"> 更多管理模式介绍</a></div>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
|
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
|
||||||
@@ -33,6 +34,7 @@ import { useI18n } from "/src/locales";
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import { useProjectStore } from "/@/store/project";
|
import { useProjectStore } from "/@/store/project";
|
||||||
import AdminModeIntro from "/@/views/sys/enterprise/project/intro.vue";
|
import AdminModeIntro from "/@/views/sys/enterprise/project/intro.vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -82,5 +84,15 @@ const onFinish = async (form: any) => {
|
|||||||
saveLoading.value = false;
|
saveLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const goCurrentProject = () => {
|
||||||
|
router.push({
|
||||||
|
path: "/certd/project/detail",
|
||||||
|
query: {
|
||||||
|
migrate: "true",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less"></style>
|
<style lang="less"></style>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<pre class="helper pre">{{ t("certd.sys.setting.passkeyEnabledHelper", [bindDomain]) }}</pre>
|
<pre class="helper pre">{{ t("certd.sys.setting.passkeyEnabledHelper", [bindDomain]) }}</pre>
|
||||||
<div v-if="!bindDomainIsSame" class="text-red-500 text-sm mt-2">
|
<div v-if="!bindDomainIsSame" class="text-red-500 text-sm mt-2">
|
||||||
{{ t("certd.sys.setting.passkeyHostnameNotSame") }}
|
{{ t("certd.sys.setting.passkeyHostnameNotSame") }} <a-button class="ml-2" size="small" type="primary" @click="settingsStore.openBindUrlModal()">{{ t("certd.sys.setting.bindUrl") }}</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
|
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default (req: any) => {
|
|||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
port: 3008,
|
port: 3008,
|
||||||
fs: devServerFs,
|
fs: devServerFs,
|
||||||
allowedHosts: ["localhost", "127.0.0.1", "yfy.docmirror.cn", "docmirror.top", "*"],
|
allowedHosts: ["localhost", "127.0.0.1", "yfy.docmirror.cn", "docmirror.top", "*", "local.dev"],
|
||||||
proxy: {
|
proxy: {
|
||||||
// with options
|
// with options
|
||||||
"/api": {
|
"/api": {
|
||||||
@@ -100,7 +100,7 @@ export default (req: any) => {
|
|||||||
"/certd/api": {
|
"/certd/api": {
|
||||||
//配套后端 https://github.com/fast-crud/fs-server-js
|
//配套后端 https://github.com/fast-crud/fs-server-js
|
||||||
target: "http://127.0.0.1:7001/api",
|
target: "http://127.0.0.1:7001/api",
|
||||||
rewrite: path => path.replace(/^\/certd\/api/, ""),
|
rewrite: (path: any) => path.replace(/^\/certd\/api/, ""),
|
||||||
//忽略证书
|
//忽略证书
|
||||||
// agent: new https.Agent({ rejectUnauthorized: false }),
|
// agent: new https.Agent({ rejectUnauthorized: false }),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,47 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到火山云tos自定义域名证书 ([af6deb9](https://github.com/certd/certd/commit/af6deb99cd24a69a189b1fdd1df51c8f7816dcda))
|
||||||
|
* 支持部署证书到火山引擎vod ([f91d591](https://github.com/certd/certd/commit/f91d591b03c50166d9fa352ba11c62d963869aa5))
|
||||||
|
|
||||||
|
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
|
||||||
|
* 修复批量执行按钮无效的bug ([49703f0](https://github.com/certd/certd/commit/49703f08e55b303851086d9f36aca562d7999be6))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 ([ffd2e81](https://github.com/certd/certd/commit/ffd2e8149e3a06bf3eec456ff85dbed793af9e90))
|
||||||
|
* 新增阿里云证书清理插件 ([4b7eeaa](https://github.com/certd/certd/commit/4b7eeaa6e0a14d2e461c7c473a920a0966b1fe8e))
|
||||||
|
|
||||||
|
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
|
||||||
|
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
|
||||||
|
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
|
||||||
|
|
||||||
|
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
|
||||||
|
|
||||||
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
showRunStrategy: false
|
||||||
|
default:
|
||||||
|
strategy:
|
||||||
|
runStrategy: 0
|
||||||
|
name: AliyunDeleteExpiringCert
|
||||||
|
title: 阿里云-删除即将过期证书
|
||||||
|
icon: ant-design:aliyun-outlined
|
||||||
|
group: aliyun
|
||||||
|
desc: 仅删除未使用的证书
|
||||||
|
needPlus: true
|
||||||
|
input:
|
||||||
|
accessId:
|
||||||
|
title: Access提供者
|
||||||
|
helper: access 授权
|
||||||
|
component:
|
||||||
|
name: access-selector
|
||||||
|
type: aliyun
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
endpoint:
|
||||||
|
title: 地域
|
||||||
|
helper: 阿里云CAS证书服务地域
|
||||||
|
component:
|
||||||
|
name: a-select
|
||||||
|
options:
|
||||||
|
- value: cas.aliyuncs.com
|
||||||
|
label: 中国大陆
|
||||||
|
- value: cas.ap-southeast-1.aliyuncs.com
|
||||||
|
label: 新加坡
|
||||||
|
required: true
|
||||||
|
value: cas.aliyuncs.com
|
||||||
|
order: 0
|
||||||
|
maxCount:
|
||||||
|
title: 最大删除数量
|
||||||
|
helper: 单次运行最大删除数量
|
||||||
|
value: 100
|
||||||
|
component:
|
||||||
|
name: a-input-number
|
||||||
|
vModel: value
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
expiringDays:
|
||||||
|
title: 即将过期天数
|
||||||
|
helper: 仅删除有效期小于此天数的证书,0表示完全过期时才删除
|
||||||
|
value: 0
|
||||||
|
component:
|
||||||
|
name: a-input-number
|
||||||
|
vModel: value
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
checkTimeout:
|
||||||
|
title: 检查超时时间
|
||||||
|
helper: 检查删除任务结果超时时间,单位分钟
|
||||||
|
value: 10
|
||||||
|
component:
|
||||||
|
name: a-input-number
|
||||||
|
vModel: value
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
output: {}
|
||||||
|
pluginType: deploy
|
||||||
|
type: builtIn
|
||||||
|
scriptFilePath: /plugins/plugin-aliyun/plugin/delete-expiring-cert/index.js
|
||||||
@@ -427,6 +427,7 @@ input:
|
|||||||
helper: 兼容Windows Server各个版本
|
helper: 兼容Windows Server各个版本
|
||||||
renewDays:
|
renewDays:
|
||||||
title: 更新天数
|
title: 更新天数
|
||||||
|
value: 20
|
||||||
component:
|
component:
|
||||||
name: a-input-number
|
name: a-input-number
|
||||||
vModel: value
|
vModel: value
|
||||||
|
|||||||
@@ -56,6 +56,18 @@ input:
|
|||||||
type: aliyun
|
type: aliyun
|
||||||
required: true
|
required: true
|
||||||
order: 0
|
order: 0
|
||||||
|
orderType:
|
||||||
|
title: 订单类型
|
||||||
|
value: CPACK
|
||||||
|
component:
|
||||||
|
name: a-select
|
||||||
|
vModel: value
|
||||||
|
options:
|
||||||
|
- label: 资源虚拟订单(一般选这个)
|
||||||
|
value: CPACK
|
||||||
|
- label: 售卖订单
|
||||||
|
value: BUY
|
||||||
|
order: 0
|
||||||
orderId:
|
orderId:
|
||||||
title: 证书订单ID
|
title: 证书订单ID
|
||||||
component:
|
component:
|
||||||
@@ -68,6 +80,7 @@ input:
|
|||||||
search: false
|
search: false
|
||||||
pager: true
|
pager: true
|
||||||
multi: true
|
multi: true
|
||||||
|
pageSize: 50
|
||||||
watches:
|
watches:
|
||||||
- certDomains
|
- certDomains
|
||||||
- accessId
|
- accessId
|
||||||
@@ -113,6 +126,7 @@ input:
|
|||||||
helper: 兼容Windows Server各个版本
|
helper: 兼容Windows Server各个版本
|
||||||
renewDays:
|
renewDays:
|
||||||
title: 更新天数
|
title: 更新天数
|
||||||
|
value: 20
|
||||||
component:
|
component:
|
||||||
name: a-input-number
|
name: a-input-number
|
||||||
vModel: value
|
vModel: value
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ input:
|
|||||||
helper: 兼容Windows Server各个版本
|
helper: 兼容Windows Server各个版本
|
||||||
renewDays:
|
renewDays:
|
||||||
title: 更新天数
|
title: 更新天数
|
||||||
|
value: 20
|
||||||
component:
|
component:
|
||||||
name: a-input-number
|
name: a-input-number
|
||||||
vModel: value
|
vModel: value
|
||||||
|
|||||||
@@ -96,6 +96,19 @@ input:
|
|||||||
选择要部署证书的监听器
|
选择要部署证书的监听器
|
||||||
需要在监听器中选择证书中心,进行跨服务访问授权
|
需要在监听器中选择证书中心,进行跨服务访问授权
|
||||||
order: 0
|
order: 0
|
||||||
|
certType:
|
||||||
|
title: 证书部署类型
|
||||||
|
helper: 选择部署默认证书还是扩展证书
|
||||||
|
component:
|
||||||
|
name: a-select
|
||||||
|
options:
|
||||||
|
- label: 默认证书
|
||||||
|
value: default
|
||||||
|
- label: 扩展证书
|
||||||
|
value: extension
|
||||||
|
value: default
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
output: {}
|
output: {}
|
||||||
pluginType: deploy
|
pluginType: deploy
|
||||||
type: builtIn
|
type: builtIn
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
showRunStrategy: false
|
||||||
|
default:
|
||||||
|
strategy:
|
||||||
|
runStrategy: 1
|
||||||
|
name: VolcengineDeployToTOS
|
||||||
|
title: 火山引擎-部署证书至TOS自定义域名
|
||||||
|
icon: svg:icon-volcengine
|
||||||
|
group: volcengine
|
||||||
|
desc: 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件
|
||||||
|
input:
|
||||||
|
cert:
|
||||||
|
title: 域名证书
|
||||||
|
helper: 请选择前置任务输出的域名证书
|
||||||
|
component:
|
||||||
|
name: output-selector
|
||||||
|
from:
|
||||||
|
- ':cert:'
|
||||||
|
- VolcengineUploadToCertCenter
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
certDomains:
|
||||||
|
title: 当前证书域名
|
||||||
|
component:
|
||||||
|
name: cert-domains-getter
|
||||||
|
mergeScript: |2-
|
||||||
|
|
||||||
|
return {
|
||||||
|
component:{
|
||||||
|
inputKey: ctx.compute(({form})=>{
|
||||||
|
return form.cert
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template: false
|
||||||
|
required: false
|
||||||
|
order: 0
|
||||||
|
accessId:
|
||||||
|
title: Access授权
|
||||||
|
helper: 火山引擎AccessKeyId、AccessKeySecret
|
||||||
|
component:
|
||||||
|
name: access-selector
|
||||||
|
type: volcengine
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
region:
|
||||||
|
title: 地域
|
||||||
|
helper: TOS服务所在地域
|
||||||
|
component:
|
||||||
|
name: a-select
|
||||||
|
options:
|
||||||
|
- label: 华北2(北京)
|
||||||
|
value: cn-beijing
|
||||||
|
- label: 华东2(上海)
|
||||||
|
value: cn-shanghai
|
||||||
|
- label: 华南1(广州)
|
||||||
|
value: cn-guangzhou
|
||||||
|
- label: 中国香港
|
||||||
|
value: cn-hongkong
|
||||||
|
- label: 亚太东南(柔佛)
|
||||||
|
value: ap-southeast-1
|
||||||
|
- label: 亚太东南(雅加达)
|
||||||
|
value: ap-southeast-3
|
||||||
|
value: cn-beijing
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
bucket:
|
||||||
|
title: Bucket
|
||||||
|
helper: 存储桶名称
|
||||||
|
component:
|
||||||
|
name: remote-auto-complete
|
||||||
|
vModel: value
|
||||||
|
type: plugin
|
||||||
|
action: onGetBucketList
|
||||||
|
search: false
|
||||||
|
pager: false
|
||||||
|
watches:
|
||||||
|
- accessId
|
||||||
|
- region
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
|
domainName:
|
||||||
|
title: TOS自定义域名
|
||||||
|
component:
|
||||||
|
name: remote-select
|
||||||
|
vModel: value
|
||||||
|
mode: tags
|
||||||
|
type: plugin
|
||||||
|
action: onGetDomainList
|
||||||
|
search: false
|
||||||
|
pager: false
|
||||||
|
multi: true
|
||||||
|
watches:
|
||||||
|
- certDomains
|
||||||
|
- accessId
|
||||||
|
- certDomains
|
||||||
|
- accessId
|
||||||
|
- region
|
||||||
|
- bucket
|
||||||
|
required: true
|
||||||
|
mergeScript: |2-
|
||||||
|
|
||||||
|
return {
|
||||||
|
component:{
|
||||||
|
form: ctx.compute(({form})=>{
|
||||||
|
return form
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
helper: 你在火山引擎上配置的TOS自定义域名,比如:example.com
|
||||||
|
order: 0
|
||||||
|
output: {}
|
||||||
|
pluginType: deploy
|
||||||
|
type: builtIn
|
||||||
|
scriptFilePath: /plugins/plugin-volcengine/plugins/plugin-deploy-to-tos.js
|
||||||
@@ -6,8 +6,7 @@ name: VolcengineDeployToVOD
|
|||||||
title: 火山引擎-部署证书至VOD
|
title: 火山引擎-部署证书至VOD
|
||||||
icon: svg:icon-volcengine
|
icon: svg:icon-volcengine
|
||||||
group: volcengine
|
group: volcengine
|
||||||
desc: 部署至火山引擎视频点播(暂不可用)
|
desc: 部署至火山引擎视频点播
|
||||||
deprecated: 暂时缺少部署ssl接口
|
|
||||||
input:
|
input:
|
||||||
cert:
|
cert:
|
||||||
title: 域名证书
|
title: 域名证书
|
||||||
@@ -44,12 +43,64 @@ input:
|
|||||||
type: volcengine
|
type: volcengine
|
||||||
required: true
|
required: true
|
||||||
order: 0
|
order: 0
|
||||||
|
regionId:
|
||||||
|
title: 区域
|
||||||
|
helper: 选择火山引擎区域
|
||||||
|
component:
|
||||||
|
name: select
|
||||||
|
options:
|
||||||
|
- value: cn-north-1
|
||||||
|
label: 华北1(北京)
|
||||||
|
- value: ap-southeast-1
|
||||||
|
label: 东南亚1(新加坡)
|
||||||
|
default: cn-north-1
|
||||||
|
required: true
|
||||||
|
order: 0
|
||||||
spaceName:
|
spaceName:
|
||||||
title: 空间名称
|
title: 空间名称
|
||||||
|
component:
|
||||||
|
name: remote-select
|
||||||
|
vModel: value
|
||||||
|
mode: default
|
||||||
|
type: plugin
|
||||||
|
action: onGetSpaceList
|
||||||
|
search: false
|
||||||
|
pager: false
|
||||||
|
multi: false
|
||||||
|
watches:
|
||||||
|
- certDomains
|
||||||
|
- accessId
|
||||||
|
- accessId
|
||||||
|
- regionId
|
||||||
|
required: true
|
||||||
|
mergeScript: |2-
|
||||||
|
|
||||||
|
return {
|
||||||
|
component:{
|
||||||
|
form: ctx.compute(({form})=>{
|
||||||
|
return form
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
helper: 选择要部署证书的点播空间
|
||||||
|
order: 0
|
||||||
|
domainType:
|
||||||
|
title: 域名类型
|
||||||
|
helper: 选择域名类型
|
||||||
|
component:
|
||||||
|
name: a-select
|
||||||
|
vModel: value
|
||||||
|
options:
|
||||||
|
- value: play
|
||||||
|
label: 点播加速域名
|
||||||
|
- value: image
|
||||||
|
label: 封面加速域名
|
||||||
|
value: play
|
||||||
required: true
|
required: true
|
||||||
order: 0
|
order: 0
|
||||||
domainList:
|
domainList:
|
||||||
title: 点播域名
|
title: 域名
|
||||||
component:
|
component:
|
||||||
name: remote-select
|
name: remote-select
|
||||||
vModel: value
|
vModel: value
|
||||||
@@ -65,6 +116,7 @@ input:
|
|||||||
- certDomains
|
- certDomains
|
||||||
- accessId
|
- accessId
|
||||||
- spaceName
|
- spaceName
|
||||||
|
- domainType
|
||||||
required: true
|
required: true
|
||||||
mergeScript: |2-
|
mergeScript: |2-
|
||||||
|
|
||||||
@@ -77,7 +129,7 @@ input:
|
|||||||
}
|
}
|
||||||
|
|
||||||
helper: |-
|
helper: |-
|
||||||
选择要部署证书的点播域名
|
选择要部署证书的域名
|
||||||
需要先在域名管理页面进行证书中心访问授权(即点击去配置SSL证书)
|
需要先在域名管理页面进行证书中心访问授权(即点击去配置SSL证书)
|
||||||
order: 0
|
order: 0
|
||||||
output: {}
|
output: {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-server",
|
"name": "@certd/ui-server",
|
||||||
"version": "1.39.3",
|
"version": "1.39.7",
|
||||||
"description": "fast-server base midway",
|
"description": "fast-server base midway",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -50,20 +50,20 @@
|
|||||||
"@aws-sdk/client-route-53": "^3.964.0",
|
"@aws-sdk/client-route-53": "^3.964.0",
|
||||||
"@aws-sdk/client-s3": "^3.964.0",
|
"@aws-sdk/client-s3": "^3.964.0",
|
||||||
"@aws-sdk/client-sts": "^3.990.0",
|
"@aws-sdk/client-sts": "^3.990.0",
|
||||||
"@certd/acme-client": "^1.39.3",
|
"@certd/acme-client": "^1.39.7",
|
||||||
"@certd/basic": "^1.39.3",
|
"@certd/basic": "^1.39.7",
|
||||||
"@certd/commercial-core": "^1.39.3",
|
"@certd/commercial-core": "^1.39.7",
|
||||||
"@certd/cv4pve-api-javascript": "^8.4.2",
|
"@certd/cv4pve-api-javascript": "^8.4.2",
|
||||||
"@certd/jdcloud": "^1.39.3",
|
"@certd/jdcloud": "^1.39.7",
|
||||||
"@certd/lib-huawei": "^1.39.3",
|
"@certd/lib-huawei": "^1.39.7",
|
||||||
"@certd/lib-k8s": "^1.39.3",
|
"@certd/lib-k8s": "^1.39.7",
|
||||||
"@certd/lib-server": "^1.39.3",
|
"@certd/lib-server": "^1.39.7",
|
||||||
"@certd/midway-flyway-js": "^1.39.3",
|
"@certd/midway-flyway-js": "^1.39.7",
|
||||||
"@certd/pipeline": "^1.39.3",
|
"@certd/pipeline": "^1.39.7",
|
||||||
"@certd/plugin-cert": "^1.39.3",
|
"@certd/plugin-cert": "^1.39.7",
|
||||||
"@certd/plugin-lib": "^1.39.3",
|
"@certd/plugin-lib": "^1.39.7",
|
||||||
"@certd/plugin-plus": "^1.39.3",
|
"@certd/plugin-plus": "^1.39.7",
|
||||||
"@certd/plus-core": "^1.39.3",
|
"@certd/plus-core": "^1.39.7",
|
||||||
"@google-cloud/publicca": "^1.3.0",
|
"@google-cloud/publicca": "^1.3.0",
|
||||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
|
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
|
||||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.185",
|
"@huaweicloud/huaweicloud-sdk-core": "^3.1.185",
|
||||||
@@ -87,6 +87,7 @@
|
|||||||
"@simplewebauthn/server": "^13.2.3",
|
"@simplewebauthn/server": "^13.2.3",
|
||||||
"@ucloud-sdks/ucloud-sdk-js": "^0.2.4",
|
"@ucloud-sdks/ucloud-sdk-js": "^0.2.4",
|
||||||
"@volcengine/openapi": "^1.28.1",
|
"@volcengine/openapi": "^1.28.1",
|
||||||
|
"@volcengine/tos-sdk": "^2.9.1",
|
||||||
"ali-oss": "^6.21.0",
|
"ali-oss": "^6.21.0",
|
||||||
"alipay-sdk": "^4.13.0",
|
"alipay-sdk": "^4.13.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import * as commercial from '@certd/commercial-core';
|
|||||||
import * as upload from '@midwayjs/upload';
|
import * as upload from '@midwayjs/upload';
|
||||||
import { setLogger } from '@certd/acme-client';
|
import { setLogger } from '@certd/acme-client';
|
||||||
import {HiddenMiddleware} from "./middleware/hidden.js";
|
import {HiddenMiddleware} from "./middleware/hidden.js";
|
||||||
import * as swagger from '@midwayjs/swagger';
|
// import * as swagger from '@midwayjs/swagger';
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
// process.env.UV_THREADPOOL_SIZE = 2
|
// process.env.UV_THREADPOOL_SIZE = 2
|
||||||
process.on('uncaughtException', error => {
|
process.on('uncaughtException', error => {
|
||||||
@@ -62,10 +62,10 @@ process.on('uncaughtException', error => {
|
|||||||
upload,
|
upload,
|
||||||
libServer,
|
libServer,
|
||||||
commercial,
|
commercial,
|
||||||
{
|
// {
|
||||||
component: swagger,
|
// component: swagger,
|
||||||
enabledEnvironment: ['local']
|
// enabledEnvironment: ['local']
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
component: info,
|
component: info,
|
||||||
enabledEnvironment: ['local'],
|
enabledEnvironment: ['local'],
|
||||||
|
|||||||
@@ -95,9 +95,7 @@ export class LoginController extends BaseController {
|
|||||||
this.ctx
|
this.ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.ok({
|
return this.ok(options);
|
||||||
...options,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/loginByPasskey', { description: Constants.per.guest })
|
@Post('/loginByPasskey', { description: Constants.per.guest })
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ export class MinePasskeyController extends BaseController {
|
|||||||
|
|
||||||
return this.ok({
|
return this.ok({
|
||||||
...options,
|
...options,
|
||||||
userId
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user