mirror of
https://github.com/certd/certd.git
synced 2026-08-01 02:17:45 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0e0bb51ce | |||
| 1dc5a19c45 | |||
| 04aa9041e8 | |||
| 0d86c84b28 | |||
| 6605669113 | |||
| 4b8747b5da | |||
| 2ba4132c50 | |||
| 49dc2796cd | |||
| 1e07d69932 | |||
| aabf73a736 | |||
| c9be2293ab |
@@ -0,0 +1,133 @@
|
|||||||
|
```markdown
|
||||||
|
# certd Development Patterns
|
||||||
|
|
||||||
|
> Auto-generated skill from repository analysis
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill teaches the core development patterns and workflows for the `certd` TypeScript codebase. It covers coding conventions, file organization, commit patterns, and detailed step-by-step instructions for extending user preferences—a common feature workflow. The repository is structured for modularity, with clear separation between UI and backend logic, and emphasizes maintainable, convention-driven development.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
### File Naming
|
||||||
|
|
||||||
|
- **CamelCase** is used for file names.
|
||||||
|
- Example: `userPreferences.ts`, `preferencesDrawer.vue`
|
||||||
|
|
||||||
|
### Import Style
|
||||||
|
|
||||||
|
- **Absolute imports** are preferred.
|
||||||
|
- Example:
|
||||||
|
```typescript
|
||||||
|
import { getUserPreferences } from 'packages/ui/certd-client/src/vben/layouts/widgets/preferences/api';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Style
|
||||||
|
|
||||||
|
- **Named exports** are used throughout the codebase.
|
||||||
|
- Example:
|
||||||
|
```typescript
|
||||||
|
export function getUserPreferences() { ... }
|
||||||
|
export const PREFERENCE_KEYS = [ ... ];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Patterns
|
||||||
|
|
||||||
|
- **Conventional commits** are used, with the `feat` prefix for new features.
|
||||||
|
- Example:
|
||||||
|
```
|
||||||
|
feat: add account sync to preferences
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### Extend Preferences Feature
|
||||||
|
|
||||||
|
**Trigger:** When someone wants to add or enhance a user preference feature (e.g., import/export, sync to account).
|
||||||
|
**Command:** `/extend-preferences`
|
||||||
|
|
||||||
|
Follow these steps to extend user preferences functionality:
|
||||||
|
|
||||||
|
1. **Update localization files**
|
||||||
|
Add or modify strings in:
|
||||||
|
- `packages/ui/certd-client/src/locales/langs/en-US/preferences.ts`
|
||||||
|
- `packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts`
|
||||||
|
```typescript
|
||||||
|
// en-US/preferences.ts
|
||||||
|
export default {
|
||||||
|
sync: "Sync Preferences",
|
||||||
|
import: "Import Preferences",
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Modify or add Vue components for preferences UI**
|
||||||
|
Update or create components such as:
|
||||||
|
- `preferences-drawer.vue`
|
||||||
|
- `account-sync.ts`
|
||||||
|
```vue
|
||||||
|
<!-- preferences-drawer.vue -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button @click="syncPreferences">{{ $t('preferences.sync') }}</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update or add supporting icon definitions**
|
||||||
|
Edit:
|
||||||
|
- `packages/ui/certd-client/src/vben/icons/lucide.ts`
|
||||||
|
```typescript
|
||||||
|
export const SyncIcon = { /* icon definition */ };
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Implement or update store logic for settings**
|
||||||
|
Update:
|
||||||
|
- `packages/ui/certd-client/src/store/settings/index.tsx`
|
||||||
|
```typescript
|
||||||
|
export function syncPreferencesToAccount() { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add or update backend API/controller for user preferences**
|
||||||
|
Edit or add:
|
||||||
|
- `packages/ui/certd-client/src/vben/layouts/widgets/preferences/api.ts`
|
||||||
|
- `packages/ui/certd-server/src/controller/user/mine/user-preferences.ts`
|
||||||
|
```typescript
|
||||||
|
// user-preferences.ts
|
||||||
|
export async function updateUserPreferences(req, res) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Write or update backend tests for new preference logic**
|
||||||
|
Add or update:
|
||||||
|
- `packages/ui/certd-server/src/controller/user/mine/user-preferences.test.ts`
|
||||||
|
```typescript
|
||||||
|
test('should sync preferences', async () => { ... });
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Update backend models if necessary**
|
||||||
|
Edit:
|
||||||
|
- `packages/ui/certd-server/src/modules/mine/service/models.ts`
|
||||||
|
```typescript
|
||||||
|
export interface UserPreferences { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Patterns
|
||||||
|
|
||||||
|
- **Test files** follow the `*.test.*` naming convention.
|
||||||
|
- Example: `user-preferences.test.ts`
|
||||||
|
- **Testing framework** is not explicitly detected, but tests are written in TypeScript and likely use a standard Node.js testing library (e.g., Jest or Mocha).
|
||||||
|
- **Test Example:**
|
||||||
|
```typescript
|
||||||
|
test('should update preferences', async () => {
|
||||||
|
// Arrange
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|----------------------|--------------------------------------------------------------|
|
||||||
|
| /extend-preferences | Guide to extend or enhance user preference functionality |
|
||||||
|
```
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Certd"
|
||||||
|
short_description: "Repo-specific patterns and workflows for certd"
|
||||||
|
default_prompt: "Use the certd repo skill to follow existing architecture, testing, and workflow conventions."
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: true
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
{
|
||||||
|
"version": "1.3",
|
||||||
|
"schemaVersion": "1.0",
|
||||||
|
"generatedBy": "ecc-tools",
|
||||||
|
"generatedAt": "2026-07-30T02:07:44.046Z",
|
||||||
|
"repo": "https://github.com/certd/certd",
|
||||||
|
"referenceSetReadiness": {
|
||||||
|
"score": 0,
|
||||||
|
"present": 0,
|
||||||
|
"total": 7,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "deep-analyzer-corpus",
|
||||||
|
"label": "Deep analyzer corpus",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add analyzer fixture, golden, benchmark, or reference-set files that can catch analyzer regressions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rag-evaluator",
|
||||||
|
"label": "RAG/evaluator comparison",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add retrieval or evaluator reference-set comparison fixtures with expected ranking behavior."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pr-salvage",
|
||||||
|
"label": "PR salvage/review corpus",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add stale-PR, review-thread, reopen-flow, or salvage reference cases for queue cleanup automation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "discussion-triage",
|
||||||
|
"label": "Discussion triage corpus",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add public discussion triage fixtures, golden cases, or reference sets for informational, answered, and no-response classifications."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "harness-compatibility",
|
||||||
|
"label": "Harness compatibility",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add cross-harness, adapter-compliance, or harness-audit evidence for Claude, Codex, OpenCode, Zed, dmux, and agent surfaces."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "security-evidence",
|
||||||
|
"label": "Security evidence",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Attach security evidence such as SBOMs, SARIF, audit reports, or AgentShield evidence packs."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ci-failure-mode",
|
||||||
|
"label": "CI failure-mode evidence",
|
||||||
|
"status": "missing",
|
||||||
|
"evidence": [],
|
||||||
|
"recommendation": "Add captured CI failure logs, dry-run fixtures, or troubleshooting docs for common workflow failure modes."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"requested": "core",
|
||||||
|
"recommended": "core",
|
||||||
|
"effective": "core",
|
||||||
|
"requestedAlias": "core",
|
||||||
|
"recommendedAlias": "core",
|
||||||
|
"effectiveAlias": "core"
|
||||||
|
},
|
||||||
|
"requestedProfile": "core",
|
||||||
|
"profile": "core",
|
||||||
|
"recommendedProfile": "core",
|
||||||
|
"effectiveProfile": "core",
|
||||||
|
"tier": "free",
|
||||||
|
"requestedComponents": [
|
||||||
|
"repo-baseline"
|
||||||
|
],
|
||||||
|
"selectedComponents": [
|
||||||
|
"repo-baseline"
|
||||||
|
],
|
||||||
|
"requestedAddComponents": [],
|
||||||
|
"requestedRemoveComponents": [],
|
||||||
|
"blockedRemovalComponents": [],
|
||||||
|
"tierFilteredComponents": [],
|
||||||
|
"requestedRootPackages": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"selectedRootPackages": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"requestedPackages": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"requestedAddPackages": [],
|
||||||
|
"requestedRemovePackages": [],
|
||||||
|
"selectedPackages": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"blockedRemovalPackages": [],
|
||||||
|
"tierFilteredRootPackages": [],
|
||||||
|
"tierFilteredPackages": [],
|
||||||
|
"conflictingPackages": [],
|
||||||
|
"dependencyGraph": {
|
||||||
|
"runtime-core": []
|
||||||
|
},
|
||||||
|
"resolutionOrder": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"requestedModules": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"selectedModules": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"modules": [
|
||||||
|
"runtime-core"
|
||||||
|
],
|
||||||
|
"managedFiles": [
|
||||||
|
".claude/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/agents/openai.yaml",
|
||||||
|
".claude/identity.json",
|
||||||
|
".codex/config.toml",
|
||||||
|
".codex/AGENTS.md",
|
||||||
|
".codex/agents/explorer.toml",
|
||||||
|
".codex/agents/reviewer.toml",
|
||||||
|
".codex/agents/docs-researcher.toml",
|
||||||
|
".claude/homunculus/instincts/inherited/certd-instincts.yaml"
|
||||||
|
],
|
||||||
|
"packageFiles": {
|
||||||
|
"runtime-core": [
|
||||||
|
".claude/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/agents/openai.yaml",
|
||||||
|
".claude/identity.json",
|
||||||
|
".codex/config.toml",
|
||||||
|
".codex/AGENTS.md",
|
||||||
|
".codex/agents/explorer.toml",
|
||||||
|
".codex/agents/reviewer.toml",
|
||||||
|
".codex/agents/docs-researcher.toml",
|
||||||
|
".claude/homunculus/instincts/inherited/certd-instincts.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"moduleFiles": {
|
||||||
|
"runtime-core": [
|
||||||
|
".claude/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/SKILL.md",
|
||||||
|
".agents/skills/certd/agents/openai.yaml",
|
||||||
|
".claude/identity.json",
|
||||||
|
".codex/config.toml",
|
||||||
|
".codex/AGENTS.md",
|
||||||
|
".codex/agents/explorer.toml",
|
||||||
|
".codex/agents/reviewer.toml",
|
||||||
|
".codex/agents/docs-researcher.toml",
|
||||||
|
".claude/homunculus/instincts/inherited/certd-instincts.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".claude/skills/certd/SKILL.md",
|
||||||
|
"description": "Repository-specific Claude Code skill generated from git history."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".agents/skills/certd/SKILL.md",
|
||||||
|
"description": "Codex-facing copy of the generated repository skill."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".agents/skills/certd/agents/openai.yaml",
|
||||||
|
"description": "Codex skill metadata so the repo skill appears cleanly in the skill interface."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".claude/identity.json",
|
||||||
|
"description": "Suggested identity.json baseline derived from repository conventions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".codex/config.toml",
|
||||||
|
"description": "Repo-local Codex MCP and multi-agent baseline aligned with ECC defaults."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".codex/AGENTS.md",
|
||||||
|
"description": "Codex usage guide that points at the generated repo skill and workflow bundle."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".codex/agents/explorer.toml",
|
||||||
|
"description": "Read-only explorer role config for Codex multi-agent work."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".codex/agents/reviewer.toml",
|
||||||
|
"description": "Read-only reviewer role config focused on correctness and security."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".codex/agents/docs-researcher.toml",
|
||||||
|
"description": "Read-only docs researcher role config for API verification."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleId": "runtime-core",
|
||||||
|
"path": ".claude/homunculus/instincts/inherited/certd-instincts.yaml",
|
||||||
|
"description": "Continuous-learning instincts derived from repository patterns."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"workflows": [],
|
||||||
|
"adapters": {
|
||||||
|
"claudeCode": {
|
||||||
|
"skillPath": ".claude/skills/certd/SKILL.md",
|
||||||
|
"identityPath": ".claude/identity.json",
|
||||||
|
"commandPaths": []
|
||||||
|
},
|
||||||
|
"codex": {
|
||||||
|
"configPath": ".codex/config.toml",
|
||||||
|
"agentsGuidePath": ".codex/AGENTS.md",
|
||||||
|
"skillPath": ".agents/skills/certd/SKILL.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
# Instincts generated from https://github.com/certd/certd
|
||||||
|
# Generated: 2026-07-30T02:08:05.402Z
|
||||||
|
# Version: 2.0
|
||||||
|
# NOTE: This file supplements (does not replace) any existing curated instincts.
|
||||||
|
# High-confidence manually curated instincts should be preserved alongside these.
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-commit-conventional
|
||||||
|
trigger: "when writing a commit message"
|
||||||
|
confidence: 0.85
|
||||||
|
domain: git
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Commit Conventional
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use conventional commit format with prefixes: feat
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- 2 commits analyzed
|
||||||
|
- Detected conventional commit pattern
|
||||||
|
- Examples: feat: 偏好设置支持从剪切板导入, feat: 偏好设置支持保存到账号并在登录后自动同步
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-commit-length
|
||||||
|
trigger: "when writing a commit message"
|
||||||
|
confidence: 0.6
|
||||||
|
domain: git
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Commit Length
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Keep commit messages concise (~22 characters)
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Average commit message length: 22 chars
|
||||||
|
- Based on 2 commits
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-naming-files
|
||||||
|
trigger: "when creating a new file"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Naming Files
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use camelCase naming convention
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Analyzed file naming patterns in repository
|
||||||
|
- Dominant pattern: camelCase
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-export-style
|
||||||
|
trigger: "when exporting from a module"
|
||||||
|
confidence: 0.7
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Export Style
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Prefer named exports
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Export pattern analysis
|
||||||
|
- Dominant style: named
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-arch-feature-based
|
||||||
|
trigger: "when adding a new feature"
|
||||||
|
confidence: 0.85
|
||||||
|
domain: architecture
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Arch Feature Based
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Create a new folder in src/features/ with all related code colocated
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Feature-based module organization detected
|
||||||
|
- Structure: src/features/[feature-name]/
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-workflow-extend-preferences-feature
|
||||||
|
trigger: "when doing extend preferences feature"
|
||||||
|
confidence: 0.6
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: https://github.com/certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Workflow Extend Preferences Feature
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Follow the extend-preferences-feature workflow:
|
||||||
|
1. Update localization files for new preference-related strings
|
||||||
|
2. Modify or add Vue components for preferences UI
|
||||||
|
3. Update or add supporting icon definitions
|
||||||
|
4. Implement or update store logic for settings
|
||||||
|
5. Add or update backend API/controller for user preferences
|
||||||
|
6. Write or update backend tests for new preference logic
|
||||||
|
7. Update backend models if necessary
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow detected from commit patterns
|
||||||
|
- Frequency: ~2x per month
|
||||||
|
- Files: packages/ui/certd-client/src/locales/langs/en-US/preferences.ts, packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts, packages/ui/certd-client/src/vben/layouts/widgets/preferences/preferences-drawer.vue
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-file-naming
|
||||||
|
trigger: "When creating a new file in the codebase"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct File Naming
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Name the file using camelCase
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.namingConventions.files
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-function-naming
|
||||||
|
trigger: "When defining a new function"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Function Naming
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use camelCase for function names
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.namingConventions.functions
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-class-naming
|
||||||
|
trigger: "When defining a new class"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Class Naming
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use PascalCase for class names
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.namingConventions.classes
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-constant-naming
|
||||||
|
trigger: "When declaring a constant"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Constant Naming
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use SCREAMING_SNAKE_CASE for constant names
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.namingConventions.constants
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-import-style
|
||||||
|
trigger: "When importing modules"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Import Style
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use absolute import paths
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.importStyle
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-export-style
|
||||||
|
trigger: "When exporting from a module"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Export Style
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use named exports
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in codeStyle.exportStyle
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-try-catch
|
||||||
|
trigger: "When handling errors in code"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: code-style
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Try Catch
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use try-catch blocks for error handling
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in errorHandling.style
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-test-location
|
||||||
|
trigger: "When adding or updating tests"
|
||||||
|
confidence: 0.7
|
||||||
|
domain: testing
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Test Location
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Place tests in both source and test-specific folders as appropriate (mixed location)
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in architecture.folderStructure.testLocation
|
||||||
|
- Seen in files like packages/ui/certd-server/src/controller/user/mine/user-preferences.test.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-backend-test-pattern
|
||||||
|
trigger: "When adding backend logic for user preferences"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: testing
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Backend Test Pattern
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Create or update a corresponding .test.ts file in the same directory
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Seen in packages/ui/certd-server/src/controller/user/mine/user-preferences.test.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-conventional-commits
|
||||||
|
trigger: "When writing a commit message"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: git
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Conventional Commits
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Use the conventional commit format with a type prefix (e.g., feat: ...)
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in commits.type
|
||||||
|
- Examples: feat: 偏好设置支持从剪切板导入
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-commit-length
|
||||||
|
trigger: "When writing a commit message"
|
||||||
|
confidence: 0.7
|
||||||
|
domain: git
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Commit Length
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Keep the commit message concise, around 22 characters on average
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Pattern in commits.averageLength
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-localization
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Localization
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Update localization files for new preference-related strings
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- Files: packages/ui/certd-client/src/locales/langs/en-US/preferences.ts, packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-ui
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Ui
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Modify or add Vue components for the preferences UI
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- Files: packages/ui/certd-client/src/vben/layouts/widgets/preferences/preferences-drawer.vue
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-icons
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Icons
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Update or add supporting icon definitions
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- File: packages/ui/certd-client/src/vben/icons/lucide.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-store
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Store
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Implement or update store logic for settings
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- File: packages/ui/certd-client/src/store/settings/index.tsx
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-backend-api
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Backend Api
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Add or update backend API/controller for user preferences
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- Files: packages/ui/certd-server/src/controller/user/mine/user-preferences.ts, packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-backend-test
|
||||||
|
trigger: "When extending or adding a user preference feature"
|
||||||
|
confidence: 0.9
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Backend Test
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Write or update backend tests for new preference logic
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- File: packages/ui/certd-server/src/controller/user/mine/user-preferences.test.ts
|
||||||
|
|
||||||
|
---
|
||||||
|
id: certd-instinct-extend-preferences-models
|
||||||
|
trigger: "When extending or adding a user preference feature and new data is needed"
|
||||||
|
confidence: 0.8
|
||||||
|
domain: workflow
|
||||||
|
source: repo-analysis
|
||||||
|
source_repo: certd/certd
|
||||||
|
---
|
||||||
|
|
||||||
|
# Certd Instinct Extend Preferences Models
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
Update backend models if necessary
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
|
||||||
|
- Workflow: extend-preferences-feature
|
||||||
|
- File: packages/ui/certd-server/src/modules/mine/service/models.ts
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0",
|
||||||
|
"technicalLevel": "technical",
|
||||||
|
"preferredStyle": {
|
||||||
|
"verbosity": "detailed",
|
||||||
|
"codeComments": true,
|
||||||
|
"explanations": true
|
||||||
|
},
|
||||||
|
"domains": [
|
||||||
|
"typescript"
|
||||||
|
],
|
||||||
|
"suggestedBy": "ecc-tools-repo-analysis",
|
||||||
|
"createdAt": "2026-07-30T02:08:05.402Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
```markdown
|
||||||
|
# certd Development Patterns
|
||||||
|
|
||||||
|
> Auto-generated skill from repository analysis
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill teaches the core development patterns and workflows for the `certd` TypeScript codebase. It covers coding conventions, file organization, commit patterns, and detailed step-by-step instructions for extending user preferences—a common feature workflow. The repository is structured for modularity, with clear separation between UI and backend logic, and emphasizes maintainable, convention-driven development.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
### File Naming
|
||||||
|
|
||||||
|
- **CamelCase** is used for file names.
|
||||||
|
- Example: `userPreferences.ts`, `preferencesDrawer.vue`
|
||||||
|
|
||||||
|
### Import Style
|
||||||
|
|
||||||
|
- **Absolute imports** are preferred.
|
||||||
|
- Example:
|
||||||
|
```typescript
|
||||||
|
import { getUserPreferences } from 'packages/ui/certd-client/src/vben/layouts/widgets/preferences/api';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Style
|
||||||
|
|
||||||
|
- **Named exports** are used throughout the codebase.
|
||||||
|
- Example:
|
||||||
|
```typescript
|
||||||
|
export function getUserPreferences() { ... }
|
||||||
|
export const PREFERENCE_KEYS = [ ... ];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Patterns
|
||||||
|
|
||||||
|
- **Conventional commits** are used, with the `feat` prefix for new features.
|
||||||
|
- Example:
|
||||||
|
```
|
||||||
|
feat: add account sync to preferences
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### Extend Preferences Feature
|
||||||
|
|
||||||
|
**Trigger:** When someone wants to add or enhance a user preference feature (e.g., import/export, sync to account).
|
||||||
|
**Command:** `/extend-preferences`
|
||||||
|
|
||||||
|
Follow these steps to extend user preferences functionality:
|
||||||
|
|
||||||
|
1. **Update localization files**
|
||||||
|
Add or modify strings in:
|
||||||
|
- `packages/ui/certd-client/src/locales/langs/en-US/preferences.ts`
|
||||||
|
- `packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts`
|
||||||
|
```typescript
|
||||||
|
// en-US/preferences.ts
|
||||||
|
export default {
|
||||||
|
sync: "Sync Preferences",
|
||||||
|
import: "Import Preferences",
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Modify or add Vue components for preferences UI**
|
||||||
|
Update or create components such as:
|
||||||
|
- `preferences-drawer.vue`
|
||||||
|
- `account-sync.ts`
|
||||||
|
```vue
|
||||||
|
<!-- preferences-drawer.vue -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button @click="syncPreferences">{{ $t('preferences.sync') }}</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update or add supporting icon definitions**
|
||||||
|
Edit:
|
||||||
|
- `packages/ui/certd-client/src/vben/icons/lucide.ts`
|
||||||
|
```typescript
|
||||||
|
export const SyncIcon = { /* icon definition */ };
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Implement or update store logic for settings**
|
||||||
|
Update:
|
||||||
|
- `packages/ui/certd-client/src/store/settings/index.tsx`
|
||||||
|
```typescript
|
||||||
|
export function syncPreferencesToAccount() { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add or update backend API/controller for user preferences**
|
||||||
|
Edit or add:
|
||||||
|
- `packages/ui/certd-client/src/vben/layouts/widgets/preferences/api.ts`
|
||||||
|
- `packages/ui/certd-server/src/controller/user/mine/user-preferences.ts`
|
||||||
|
```typescript
|
||||||
|
// user-preferences.ts
|
||||||
|
export async function updateUserPreferences(req, res) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Write or update backend tests for new preference logic**
|
||||||
|
Add or update:
|
||||||
|
- `packages/ui/certd-server/src/controller/user/mine/user-preferences.test.ts`
|
||||||
|
```typescript
|
||||||
|
test('should sync preferences', async () => { ... });
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Update backend models if necessary**
|
||||||
|
Edit:
|
||||||
|
- `packages/ui/certd-server/src/modules/mine/service/models.ts`
|
||||||
|
```typescript
|
||||||
|
export interface UserPreferences { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Patterns
|
||||||
|
|
||||||
|
- **Test files** follow the `*.test.*` naming convention.
|
||||||
|
- Example: `user-preferences.test.ts`
|
||||||
|
- **Testing framework** is not explicitly detected, but tests are written in TypeScript and likely use a standard Node.js testing library (e.g., Jest or Mocha).
|
||||||
|
- **Test Example:**
|
||||||
|
```typescript
|
||||||
|
test('should update preferences', async () => {
|
||||||
|
// Arrange
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|----------------------|--------------------------------------------------------------|
|
||||||
|
| /extend-preferences | Guide to extend or enhance user preference functionality |
|
||||||
|
```
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# ECC for Codex CLI
|
||||||
|
|
||||||
|
This supplements the root `AGENTS.md` with a repo-local ECC baseline.
|
||||||
|
|
||||||
|
## Repo Skill
|
||||||
|
|
||||||
|
- Repo-generated Codex skill: `.agents/skills/certd/SKILL.md`
|
||||||
|
- Claude-facing companion skill: `.claude/skills/certd/SKILL.md`
|
||||||
|
- Keep user-specific credentials and private MCPs in `~/.codex/config.toml`, not in this repo.
|
||||||
|
|
||||||
|
## MCP Baseline
|
||||||
|
|
||||||
|
Treat `.codex/config.toml` as the default ECC-safe baseline for work in this repository.
|
||||||
|
The generated baseline enables GitHub, Context7, Exa, Memory, Playwright, and Sequential Thinking.
|
||||||
|
|
||||||
|
## Multi-Agent Support
|
||||||
|
|
||||||
|
- Explorer: read-only evidence gathering
|
||||||
|
- Reviewer: correctness, security, and regression review
|
||||||
|
- Docs researcher: API and release-note verification
|
||||||
|
|
||||||
|
## Workflow Files
|
||||||
|
|
||||||
|
- No dedicated workflow command files were generated for this repo.
|
||||||
|
|
||||||
|
Use these workflow files as reusable task scaffolds when the detected repository workflows recur.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
model = "gpt-5.4"
|
||||||
|
model_reasoning_effort = "medium"
|
||||||
|
sandbox_mode = "read-only"
|
||||||
|
|
||||||
|
developer_instructions = """
|
||||||
|
Verify APIs, framework behavior, and release-note claims against primary documentation before changes land.
|
||||||
|
Cite the exact docs or file paths that support each claim.
|
||||||
|
Do not invent undocumented behavior.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
model = "gpt-5.4"
|
||||||
|
model_reasoning_effort = "medium"
|
||||||
|
sandbox_mode = "read-only"
|
||||||
|
|
||||||
|
developer_instructions = """
|
||||||
|
Stay in exploration mode.
|
||||||
|
Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them.
|
||||||
|
Prefer targeted search and file reads over broad scans.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
model = "gpt-5.4"
|
||||||
|
model_reasoning_effort = "high"
|
||||||
|
sandbox_mode = "read-only"
|
||||||
|
|
||||||
|
developer_instructions = """
|
||||||
|
Review like an owner.
|
||||||
|
Prioritize correctness, security, behavioral regressions, and missing tests.
|
||||||
|
Lead with concrete findings and avoid style-only feedback unless it hides a real bug.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#:schema https://developers.openai.com/codex/config-schema.json
|
||||||
|
|
||||||
|
# ECC Tools generated Codex baseline
|
||||||
|
approval_policy = "on-request"
|
||||||
|
sandbox_mode = "workspace-write"
|
||||||
|
web_search = "live"
|
||||||
|
|
||||||
|
[mcp_servers.github]
|
||||||
|
command = "npx"
|
||||||
|
args = ["-y", "@modelcontextprotocol/server-github"]
|
||||||
|
|
||||||
|
[mcp_servers.context7]
|
||||||
|
command = "npx"
|
||||||
|
args = ["-y", "@upstash/context7-mcp@latest"]
|
||||||
|
|
||||||
|
[mcp_servers.exa]
|
||||||
|
url = "https://mcp.exa.ai/mcp"
|
||||||
|
|
||||||
|
[mcp_servers.memory]
|
||||||
|
command = "npx"
|
||||||
|
args = ["-y", "@modelcontextprotocol/server-memory"]
|
||||||
|
|
||||||
|
[mcp_servers.playwright]
|
||||||
|
command = "npx"
|
||||||
|
args = ["-y", "@playwright/mcp@latest", "--extension"]
|
||||||
|
|
||||||
|
[mcp_servers.sequential-thinking]
|
||||||
|
command = "npx"
|
||||||
|
args = ["-y", "@modelcontextprotocol/server-sequential-thinking"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
multi_agent = true
|
||||||
|
|
||||||
|
[agents]
|
||||||
|
max_threads = 6
|
||||||
|
max_depth = 1
|
||||||
|
|
||||||
|
[agents.explorer]
|
||||||
|
description = "Read-only codebase explorer for gathering evidence before changes are proposed."
|
||||||
|
config_file = "agents/explorer.toml"
|
||||||
|
|
||||||
|
[agents.reviewer]
|
||||||
|
description = "PR reviewer focused on correctness, security, and missing tests."
|
||||||
|
config_file = "agents/reviewer.toml"
|
||||||
|
|
||||||
|
[agents.docs_researcher]
|
||||||
|
description = "Documentation specialist that verifies APIs, framework behavior, and release notes."
|
||||||
|
config_file = "agents/docs-researcher.toml"
|
||||||
@@ -568,18 +568,11 @@ export class RuntimeDepsService {
|
|||||||
const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env: tryEnv });
|
const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, env: tryEnv });
|
||||||
if (result.code === 0) {
|
if (result.code === 0) {
|
||||||
this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl: tryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) });
|
this.writeInstallState(statePath, { installedAt: new Date().toISOString(), registryUrl: tryUrl, dependenciesHash, nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath) });
|
||||||
log.info(`${result.stdout?.slice(-2000) || "无npm安装日志输出"}`);
|
|
||||||
log.info("第三方依赖安装完成");
|
log.info("第三方依赖安装完成");
|
||||||
return { registryUrl: tryUrl, packageJsonPath };
|
return { registryUrl: tryUrl, packageJsonPath };
|
||||||
}
|
}
|
||||||
const errOutput = (result.stderr || "").trim();
|
lastError = result.stderr || result.stdout || "unknown error";
|
||||||
const outOutput = (result.stdout || "").trim();
|
log.warn?.(`镜像 ${tryUrl || "默认"} 安装失败${urlsToTry.length > 1 ? ",尝试下一个镜像..." : ""}`);
|
||||||
lastError = errOutput || outOutput || "unknown error";
|
|
||||||
log.info(`镜像 ${tryUrl || "默认"} 安装失败,退出码: ${result.code}${urlsToTry.length > 1 ? ",尝试下一个镜像..." : ""}`);
|
|
||||||
log.info(` pnpm stderr: ${(errOutput || "无npm安装日志输出").slice(-2000)}`);
|
|
||||||
if (outOutput) {
|
|
||||||
log.info(` pnpm stdout: ${outOutput.slice(-2000)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.writeInstallState(statePath, {
|
this.writeInstallState(statePath, {
|
||||||
...currentState,
|
...currentState,
|
||||||
|
|||||||
@@ -93,21 +93,19 @@ export class Flyway {
|
|||||||
await queryRunner.commitTransaction();
|
await queryRunner.commitTransaction();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error(err);
|
this.logger.error(err);
|
||||||
this.errorTip(err);
|
|
||||||
await this.storeSqlExecLog(file.script, filepath, false, queryRunner);
|
await this.storeSqlExecLog(file.script, filepath, false, queryRunner);
|
||||||
await queryRunner.rollbackTransaction();
|
await queryRunner.rollbackTransaction();
|
||||||
|
|
||||||
|
if (err.code === "SQLITE_IOERR_WRITE") {
|
||||||
|
this.logger.warn("SQLite数据库写入失败,可能您的操作系统版本太低,请将「certd:latest」镜像改为「certd:slim」即可。(如需指定版本可以修改成「certd:[version]-slim」)", file.script);
|
||||||
|
}
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.info("[ midfly ] end-------------");
|
this.logger.info("[ midfly ] end-------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
private errorTip(err: any) {
|
|
||||||
if (err.code === "SQLITE_IOERR_WRITE") {
|
|
||||||
this.logger.warn("SQLite数据库写入失败,可能您的操作系统版本太低,请将「certd:latest」镜像改为「certd:slim」即可。(如需指定版本可以修改成「certd:[version]-slim」)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async storeSqlExecLog(filename: string, filepath: string, success: boolean, queryRunner: QueryRunner) {
|
private async storeSqlExecLog(filename: string, filepath: string, success: boolean, queryRunner: QueryRunner) {
|
||||||
const hash = await this.getFileHash(filepath);
|
const hash = await this.getFileHash(filepath);
|
||||||
//先删除
|
//先删除
|
||||||
@@ -267,7 +265,6 @@ export class Flyway {
|
|||||||
await queryRunner.query(sql);
|
await queryRunner.query(sql);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error("exec sql error : ", err.message, err);
|
this.logger.error("exec sql error : ", err.message, err);
|
||||||
this.errorTip(err);
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export default {
|
|||||||
projectUserManager: "Project User Management",
|
projectUserManager: "Project User Management",
|
||||||
myProjectManager: "My Projects",
|
myProjectManager: "My Projects",
|
||||||
myProjectDetail: "Project Detail",
|
myProjectDetail: "Project Detail",
|
||||||
projectDetail: "Project Detail",
|
|
||||||
projectJoin: "Join Project",
|
projectJoin: "Join Project",
|
||||||
currentProject: "Current Project",
|
currentProject: "Current Project",
|
||||||
projectMemberManager: "Project Member",
|
projectMemberManager: "Project Member",
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export default {
|
|||||||
enterpriseSetting: "企业设置",
|
enterpriseSetting: "企业设置",
|
||||||
myProjectManager: "我的项目",
|
myProjectManager: "我的项目",
|
||||||
myProjectDetail: "项目详情",
|
myProjectDetail: "项目详情",
|
||||||
projectDetail: "项目详情",
|
|
||||||
projectJoin: "加入项目",
|
projectJoin: "加入项目",
|
||||||
currentProject: "当前项目",
|
currentProject: "当前项目",
|
||||||
projectMemberManager: "项目成员管理",
|
projectMemberManager: "项目成员管理",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, ref, watch, inject, onMounted, Ref } from "vue";
|
import { defineComponent, reactive, ref, watch, inject, onMounted } from "vue";
|
||||||
import CertAccessModal from "./access/index.vue";
|
import CertAccessModal from "./access/index.vue";
|
||||||
import { createAccessApi } from "../api";
|
import { createAccessApi } from "../api";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
@@ -64,9 +64,9 @@ export default defineComponent({
|
|||||||
setup(props, ctx) {
|
setup(props, ctx) {
|
||||||
const api = createAccessApi(props.from);
|
const api = createAccessApi(props.from);
|
||||||
|
|
||||||
const target:Ref<any> = ref({});
|
const target = ref({});
|
||||||
const selectedId = ref();
|
const selectedId = ref();
|
||||||
async function refreshTarget(value:any) {
|
async function refreshTarget(value) {
|
||||||
selectedId.value = value;
|
selectedId.value = value;
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
target.value = await api.GetSimpleInfo(value);
|
target.value = await api.GetSimpleInfo(value);
|
||||||
@@ -83,7 +83,7 @@ export default defineComponent({
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
|
|
||||||
async function emitValue(value:any) {
|
async function emitValue(value) {
|
||||||
const userId = userStore.userInfo.id;
|
const userId = userStore.userInfo.id;
|
||||||
const isEnterprice = projectStore.isEnterprise;
|
const isEnterprice = projectStore.isEnterprise;
|
||||||
if (pipeline?.value) {
|
if (pipeline?.value) {
|
||||||
@@ -132,7 +132,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const providerDefine = ref({});
|
const providerDefine = ref({});
|
||||||
|
|
||||||
async function refreshProviderDefine(type:any) {
|
async function refreshProviderDefine(type) {
|
||||||
providerDefine.value = await api.GetProviderDefine(type);
|
providerDefine.value = await api.GetProviderDefine(type);
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
|||||||
this.clearLastStatus();
|
this.clearLastStatus();
|
||||||
|
|
||||||
if (this.successNotify) {
|
if (this.successNotify) {
|
||||||
await this.sendSuccessNotify(cert);
|
await this.sendSuccessNotify();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("申请证书失败");
|
throw new Error("申请证书失败");
|
||||||
@@ -165,14 +165,12 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
|||||||
nextUpdateDays: leftDays - maxDays,
|
nextUpdateDays: leftDays - maxDays,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async sendSuccessNotify(certReader: CertReader) {
|
async sendSuccessNotify() {
|
||||||
this.logger.info("发送证书申请成功通知");
|
this.logger.info("发送证书申请成功通知");
|
||||||
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
|
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
|
||||||
const body: NotificationBody = {
|
const body: NotificationBody = {
|
||||||
title: `证书申请成功【${this.pipeline.title}】`,
|
title: `证书申请成功【${this.pipeline.title}】`,
|
||||||
content: `域名:${this.domains.join(",")}\n
|
content: `域名:${this.domains.join(",")}`,
|
||||||
证书有效期:${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")}\n
|
|
||||||
`,
|
|
||||||
url: url,
|
url: url,
|
||||||
notificationType: "certApplySuccess",
|
notificationType: "certApplySuccess",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import { BaotaAccess } from "../access.js";
|
import { BaotaAccess } from "../access.js";
|
||||||
import { HttpClient, HttpRequestConfig, ILogger } from "@certd/basic";
|
import { HttpClient, HttpRequestConfig } from "@certd/basic";
|
||||||
import * as querystring from "node:querystring";
|
import * as querystring from "node:querystring";
|
||||||
|
|
||||||
export class BaotaClient {
|
export class BaotaClient {
|
||||||
access: BaotaAccess;
|
access: BaotaAccess;
|
||||||
http: HttpClient;
|
http: HttpClient;
|
||||||
logger: ILogger
|
|
||||||
|
|
||||||
constructor(access: BaotaAccess, http: HttpClient) {
|
constructor(access: BaotaAccess, http: HttpClient) {
|
||||||
this.access = access;
|
this.access = access;
|
||||||
this.http = http;
|
this.http = http;
|
||||||
this.logger = access.ctx.logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//将以上 java代码 翻译成nodejs 代码
|
//将以上 java代码 翻译成nodejs 代码
|
||||||
|
|||||||
+18
-29
@@ -88,7 +88,7 @@ export class BaotaDeployWebSiteCert extends AbstractTaskPlugin {
|
|||||||
})
|
})
|
||||||
siteName!: string | string[];
|
siteName!: string | string[];
|
||||||
|
|
||||||
async onInstance() { }
|
async onInstance() {}
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
const { cert, accessId } = this;
|
const { cert, accessId } = this;
|
||||||
const access = await this.getAccess(accessId);
|
const access = await this.getAccess(accessId);
|
||||||
@@ -104,37 +104,26 @@ export class BaotaDeployWebSiteCert extends AbstractTaskPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lockKey = `baota-lock-${accessId}`;
|
const lockKey = `baota-lock-${accessId}`;
|
||||||
if (this.isDockerSite) {
|
|
||||||
this.logger.info(`当前已勾选docker站点(如果部署失败,请确认站点:${siteNames}, 是否全部为docker站点)`);
|
|
||||||
}
|
|
||||||
for (const site of siteNames) {
|
for (const site of siteNames) {
|
||||||
// 加锁,防止并发部署证书, 宝塔并发部署会导致nginx的conf错乱
|
// 加锁,防止并发部署证书, 宝塔并发部署会导致nginx的conf错乱
|
||||||
await this.ctx.utils.locker.execute(lockKey, async () => {
|
await this.ctx.utils.locker.execute(lockKey, async () => {
|
||||||
try {
|
this.logger.info(`为站点:${site}设置证书,目前支持宝塔网站站点、docker站点`);
|
||||||
if (this.isDockerSite) {
|
if (this.isDockerSite) {
|
||||||
this.logger.info(`为Docker站点:${site} 设置证书`);
|
const res = await client.doRequest("/mod/docker/com/set_ssl", "", {
|
||||||
const res = await client.doRequest("/mod/docker/com/set_ssl", "", {
|
site_name: site,
|
||||||
site_name: site,
|
key: cert.key,
|
||||||
key: cert.key,
|
csr: cert.crt,
|
||||||
csr: cert.crt,
|
});
|
||||||
});
|
this.logger.info(res?.msg);
|
||||||
this.logger.info(res?.msg);
|
} else {
|
||||||
} else {
|
const res = await client.doRequest("/site", "SetSSL", {
|
||||||
this.logger.info(`为非Docker站点:${site} 设置证书`);
|
type: 0,
|
||||||
const res = await client.doRequest("/site", "SetSSL", {
|
siteName: site,
|
||||||
type: 0,
|
key: cert.key,
|
||||||
siteName: site,
|
csr: cert.crt,
|
||||||
key: cert.key,
|
});
|
||||||
csr: cert.crt,
|
this.logger.info(res?.msg);
|
||||||
});
|
|
||||||
this.logger.info(res?.msg);
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e?.message?.includes("没有服务器配置文件")) {
|
|
||||||
this.logger.error(e.message);
|
|
||||||
this.logger.warn(`首先请确认站点 ${site} 是否存在,如果存在,请到宝塔上手动保存一下该站点证书试试`);
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ import { TencentAccess, TencentSslClient } from "../../../plugin-lib/tencent/ind
|
|||||||
title: "腾讯云-删除即将过期证书",
|
title: "腾讯云-删除即将过期证书",
|
||||||
icon: "svg:icon-tencentcloud",
|
icon: "svg:icon-tencentcloud",
|
||||||
group: pluginGroups.tencent.key,
|
group: pluginGroups.tencent.key,
|
||||||
desc: "仅删除即将过期且未使用的证书",
|
desc: "仅删除未使用的证书",
|
||||||
dependPlugins: {
|
dependPlugins: {
|
||||||
"access:tencent": "*",
|
"access:tencent": "*",
|
||||||
},
|
},
|
||||||
|
|||||||
-111
@@ -1,111 +0,0 @@
|
|||||||
/// <reference types="mocha" />
|
|
||||||
|
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { DeployCertToTencentCLB } from "./index.js";
|
|
||||||
|
|
||||||
describe("DeployCertToTencentCLB", () => {
|
|
||||||
it("uses remote single-select inputs for CLB and HTTPS listener", () => {
|
|
||||||
const input = (DeployCertToTencentCLB as any).define.input;
|
|
||||||
|
|
||||||
assert.equal(input.loadBalancerId.component.name, "remote-select");
|
|
||||||
assert.equal(input.loadBalancerId.component.single, true);
|
|
||||||
assert.equal(input.loadBalancerId.component.action, "onGetCLBList");
|
|
||||||
assert.equal(input.loadBalancerId.required, true);
|
|
||||||
|
|
||||||
assert.equal(input.listenerId.component.name, "remote-select");
|
|
||||||
assert.equal(input.listenerId.component.single, true);
|
|
||||||
assert.equal(input.listenerId.component.action, "onGetListenerList");
|
|
||||||
assert.deepEqual(input.listenerId.component.watches, ["certDomains", "accessId", "region", "loadBalancerId"]);
|
|
||||||
assert.equal(input.listenerId.required, true);
|
|
||||||
|
|
||||||
assert.equal(input.domain.component.name, "remote-select");
|
|
||||||
assert.equal(input.domain.component.single, false);
|
|
||||||
assert.equal(input.domain.component.action, "onGetDomainList");
|
|
||||||
assert.deepEqual(input.domain.component.watches, ["certDomains", "accessId", "region", "loadBalancerId", "listenerId"]);
|
|
||||||
assert.equal(input.domain.required, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("maps CLB API results to remote-select options", async () => {
|
|
||||||
const plugin = new DeployCertToTencentCLB();
|
|
||||||
plugin.accessId = "access-1";
|
|
||||||
plugin.logger = { info: () => undefined } as any;
|
|
||||||
(plugin as any).getClient = async () => ({});
|
|
||||||
(plugin as any).getCLBList = async () => [
|
|
||||||
{
|
|
||||||
LoadBalancerId: "lb-1",
|
|
||||||
LoadBalancerName: "业务负载均衡",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const options = await plugin.onGetCLBList({});
|
|
||||||
|
|
||||||
assert.deepEqual(options, [
|
|
||||||
{
|
|
||||||
value: "lb-1",
|
|
||||||
label: "业务负载均衡<lb-1>",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("maps HTTPS listener API results after selecting a CLB", async () => {
|
|
||||||
const plugin = new DeployCertToTencentCLB();
|
|
||||||
plugin.accessId = "access-1";
|
|
||||||
plugin.loadBalancerId = "lb-1";
|
|
||||||
plugin.logger = { info: () => undefined } as any;
|
|
||||||
(plugin as any).getClient = async () => ({});
|
|
||||||
(plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: any) => {
|
|
||||||
assert.equal(loadBalancerId, "lb-1");
|
|
||||||
assert.equal(listenerIds, null);
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
ListenerId: "listener-1",
|
|
||||||
ListenerName: "HTTPS监听器",
|
|
||||||
Port: 443,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = await plugin.onGetListenerList({});
|
|
||||||
|
|
||||||
assert.deepEqual(options, [
|
|
||||||
{
|
|
||||||
value: "listener-1",
|
|
||||||
label: "HTTPS监听器:443<listener-1>",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("maps SNI domains from the selected listener rules", async () => {
|
|
||||||
const plugin = new DeployCertToTencentCLB();
|
|
||||||
plugin.accessId = "access-1";
|
|
||||||
plugin.loadBalancerId = "lb-1";
|
|
||||||
plugin.listenerId = "listener-1";
|
|
||||||
plugin.logger = { info: () => undefined } as any;
|
|
||||||
(plugin as any).getClient = async () => ({});
|
|
||||||
(plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: string[]) => {
|
|
||||||
assert.equal(loadBalancerId, "lb-1");
|
|
||||||
assert.deepEqual(listenerIds, ["listener-1"]);
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
ListenerId: "listener-1",
|
|
||||||
Rules: [{ Domain: "www.example.com" }, { Domain: "api.example.com" }],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = await plugin.onGetDomainList({});
|
|
||||||
|
|
||||||
assert.deepEqual(options, [
|
|
||||||
{
|
|
||||||
value: "www.example.com",
|
|
||||||
label: "www.example.com",
|
|
||||||
domain: "www.example.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "api.example.com",
|
|
||||||
label: "api.example.com",
|
|
||||||
domain: "api.example.com",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+20
-108
@@ -1,8 +1,7 @@
|
|||||||
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { TencentAccess } from "../../../plugin-lib/tencent/index.js";
|
import { TencentAccess } from "../../../plugin-lib/tencent/index.js";
|
||||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||||
import { createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToTencentCLB",
|
name: "DeployCertToTencentCLB",
|
||||||
title: "腾讯云-部署到CLB",
|
title: "腾讯云-部署到CLB",
|
||||||
@@ -74,44 +73,29 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
|
|||||||
})
|
})
|
||||||
region!: string;
|
region!: string;
|
||||||
|
|
||||||
@TaskInput(
|
@TaskInput({
|
||||||
createRemoteSelectInputDefine({
|
title: "负载均衡ID",
|
||||||
title: "负载均衡ID",
|
required: true,
|
||||||
helper: "请选择要部署证书的负载均衡",
|
})
|
||||||
action: DeployCertToTencentCLB.prototype.onGetCLBList.name,
|
|
||||||
watches: ["region"],
|
|
||||||
single: true,
|
|
||||||
pager: false,
|
|
||||||
search: false,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
loadBalancerId!: string;
|
loadBalancerId!: string;
|
||||||
|
|
||||||
@TaskInput(
|
@TaskInput({
|
||||||
createRemoteSelectInputDefine({
|
title: "监听器ID",
|
||||||
title: "监听器ID",
|
required: true,
|
||||||
helper: "请选择要部署证书的HTTPS监听器",
|
})
|
||||||
action: DeployCertToTencentCLB.prototype.onGetListenerList.name,
|
|
||||||
watches: ["region", "loadBalancerId"],
|
|
||||||
single: true,
|
|
||||||
pager: false,
|
|
||||||
search: false,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
listenerId!: string;
|
listenerId!: string;
|
||||||
|
|
||||||
@TaskInput(
|
@TaskInput({
|
||||||
createRemoteSelectInputDefine({
|
title: "域名",
|
||||||
title: "域名",
|
required: false,
|
||||||
helper: "如果开启了SNI,请选择要部署证书的域名;未开启SNI时可以留空",
|
component: {
|
||||||
action: DeployCertToTencentCLB.prototype.onGetDomainList.name,
|
name: "a-select",
|
||||||
watches: ["region", "loadBalancerId", "listenerId"],
|
vModel: "value",
|
||||||
required: false,
|
open: false,
|
||||||
single: false,
|
mode: "tags",
|
||||||
pager: false,
|
},
|
||||||
search: false,
|
helper: "如果开启了sni,则此项必须填写,未开启,则不要填写",
|
||||||
})
|
})
|
||||||
)
|
|
||||||
domain!: string | string[];
|
domain!: string | string[];
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
@@ -288,27 +272,6 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
|
|||||||
return ret.LoadBalancerSet;
|
return ret.LoadBalancerSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onGetCLBList(data: PageSearch) {
|
|
||||||
if (!this.accessId) {
|
|
||||||
throw new Error("请选择Access提供者");
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = await this.getClient();
|
|
||||||
const list = await this.getCLBList(client);
|
|
||||||
if (!list || list.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.map((item: any) => {
|
|
||||||
const loadBalancerId = item.LoadBalancerId;
|
|
||||||
const loadBalancerName = item.LoadBalancerName || loadBalancerId;
|
|
||||||
return {
|
|
||||||
value: loadBalancerId,
|
|
||||||
label: `${loadBalancerName}<${loadBalancerId}>`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getListenerList(client: any, balancerId: any, listenerIds: any) {
|
async getListenerList(client: any, balancerId: any, listenerIds: any) {
|
||||||
// HTTPS
|
// HTTPS
|
||||||
const params = {
|
const params = {
|
||||||
@@ -321,57 +284,6 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
|
|||||||
return ret.Listeners;
|
return ret.Listeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onGetListenerList(data: PageSearch) {
|
|
||||||
if (!this.accessId) {
|
|
||||||
throw new Error("请选择Access提供者");
|
|
||||||
}
|
|
||||||
if (!this.loadBalancerId) {
|
|
||||||
throw new Error("请先选择负载均衡");
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = await this.getClient();
|
|
||||||
const list = await this.getListenerList(client, this.loadBalancerId, null);
|
|
||||||
if (!list || list.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.map((item: any) => {
|
|
||||||
const listenerId = item.ListenerId;
|
|
||||||
const listenerName = item.ListenerName || "HTTPS监听器";
|
|
||||||
const port = item.Port ? `:${item.Port}` : "";
|
|
||||||
return {
|
|
||||||
value: listenerId,
|
|
||||||
label: `${listenerName}${port}<${listenerId}>`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async onGetDomainList(data: PageSearch) {
|
|
||||||
if (!this.accessId) {
|
|
||||||
throw new Error("请选择Access提供者");
|
|
||||||
}
|
|
||||||
if (!this.loadBalancerId) {
|
|
||||||
throw new Error("请先选择负载均衡");
|
|
||||||
}
|
|
||||||
if (!this.listenerId) {
|
|
||||||
throw new Error("请先选择监听器");
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = await this.getClient();
|
|
||||||
const listeners = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
|
|
||||||
const listener = listeners?.[0];
|
|
||||||
const domains = listener?.Rules?.map((rule: any) => rule.Domain).filter(Boolean) || [];
|
|
||||||
const uniqueDomains = [...new Set(domains)];
|
|
||||||
|
|
||||||
return uniqueDomains.map(domain => {
|
|
||||||
return {
|
|
||||||
value: domain,
|
|
||||||
label: domain,
|
|
||||||
domain,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkRet(ret: any) {
|
checkRet(ret: any) {
|
||||||
if (!ret || ret.Error) {
|
if (!ret || ret.Error) {
|
||||||
throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);
|
throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);
|
||||||
|
|||||||
Reference in New Issue
Block a user