Token导航 LogoToken导航TokenDH.com
前端设计敏感数据github未标认证来源可访问许可证需确认审计异常

directus-development-workflowdirectus 开发工作流程

Agent Skill

directus-development-workflow 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

12,648

周安装

686

GitHub Stars

公开资料未说明

下载量

5,577
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:directus-development-workflow(directus 开发工作流程)
来源仓库:https://github.com/gumpen-app/directapp
仓库路径:skills/directus-development-workflow
安装命令:
npx skills add https://github.com/gumpen-app/directapp --skill 'Directus Development Workflow'
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/gumpen-app/directapp --skill 'Directus Development Workflow'

简介

directus-development-workflow skill 提供专业的 Directus 项目脚手架与 CI/CD 部署流水线配置指南。

  • 适用于构建可扩展、可测试的多环境 Directus 应用,涵盖 Docker 容器化与自动化测试策略。
  • 使用时需统一 TypeScript 配置与依赖管理,建议采用 monorepo 结构分离前端扩展与后端逻辑。
  • 安装前请规划好环境变量管理与密钥存储方案(如 .env 文件或 secrets manager),确保生产安全合规。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Directus Development Workflow

Overview

This skill provides comprehensive guidance for setting up and maintaining professional Directus development workflows. Master project scaffolding, TypeScript configuration, testing strategies, continuous integration/deployment, Docker containerization, multi-environment management, and development best practices for building scalable Directus applications.

When to Use This Skill

  • Setting up new Directus projects
  • Configuring TypeScript for type safety
  • Implementing testing strategies
  • Setting up CI/CD pipelines
  • Containerizing with Docker
  • Managing multiple environments
  • Implementing database migrations
  • Setting up development tools
  • Optimizing build processes
  • Deploying to production

Project Setup

Step 1: Initialize Directus Project

# Create project directory
mkdir my-directus-project && cd my-directus-project

# Initialize package.json
npm init -y

# Install Directus
npm install directus

# Initialize Directus
npx directus init

# Project structure
my-directus-project/
├── .env                     # Environment variables
├── .gitignore               # Git ignore rules
├── docker-compose.yml       # Docker configuration
├── package.json             # Dependencies
├── tsconfig.json            # TypeScript config
├── uploads/                 # File uploads directory
├── extensions/              # Custom extensions
│   ├── endpoints/
│   ├── hooks/
│   ├── interfaces/
│   ├── displays/
│   ├── layouts/
│   ├── modules/
│   ├── operations/
│   └── panels/
├── migrations/              # Database migrations
├── seeders/                 # Database seeders
├── tests/                   # Test files
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── scripts/                 # Utility scripts
├── docs/                    # Documentation
└── .github/                 # GitHub workflows
    └── workflows/

Step 2: Environment Configuration

# .env.example
# Database
DB_CLIENT="pg"
DB_HOST="localhost"
DB_PORT="5432"
DB_DATABASE="directus"
DB_USER="directus"
DB_PASSWORD="directus"

# Security
KEY="your-random-secret-key"
SECRET="your-random-secret"

# Admin
ADMIN_EMAIL="admin@example.com"
ADMIN_PASSWORD="admin"

# Server
PUBLIC_URL="http://localhost:8055"
PORT=8055

# Storage
STORAGE_LOCATIONS="local,s3"
STORAGE_LOCAL_DRIVER="local"
STORAGE_LOCAL_ROOT="./uploads"

# S3 Storage (optional)
STORAGE_S3_DRIVER="s3"
STORAGE_S3_KEY="your-s3-key"
STORAGE_S3_SECRET="your-s3-secret"
STORAGE_S3_BUCKET="your-bucket"
STORAGE_S3_REGION="us-east-1"

# Email
EMAIL_TRANSPORT="sendgrid"
EMAIL_SENDGRID_API_KEY="your-sendgrid-key"
EMAIL_FROM="no-reply@example.com"

# Cache
CACHE_ENABLED="true"
CACHE_STORE="redis"
CACHE_REDIS="redis://localhost:6379"
CACHE_AUTO_PURGE="true"

# Rate Limiting
RATE_LIMITER_ENABLED="true"
RATE_LIMITER_STORE="redis"
RATE_LIMITER_POINTS="100"
RATE_LIMITER_DURATION="60"

# Extensions
EXTENSIONS_AUTO_RELOAD="true"

# Telemetry
TELEMETRY="false"

# AI Integration (custom)
OPENAI_API_KEY="your-openai-key"
ANTHROPIC_API_KEY="your-anthropic-key"
PINECONE_API_KEY="your-pinecone-key"

Step 3: TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "types": ["node", "jest"],
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@extensions/*": ["extensions/*"],
      "@utils/*": ["src/utils/*"],
      "@services/*": ["src/services/*"],
      "@types/*": ["src/types/*"]
    }
  },
  "include": [
    "src/**/*",
    "extensions/**/*",
    "tests/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist",
    "uploads"
  ]
}

Step 4: Extension Development Setup

# Create extension scaffolding script
cat > scripts/create-extension.sh << 'EOF'
#!/bin/bash

echo "Select extension type:"
echo "1) Endpoint"
echo "2) Hook"
echo "3) Panel"
echo "4) Interface"
echo "5) Display"
echo "6) Layout"
echo "7) Module"
echo "8) Operation"

read -p "Enter choice [1-8]: " choice

read -p "Enter extension name: " name

case $choice in
  1) type="endpoint" ;;
  2) type="hook" ;;
  3) type="panel" ;;
  4) type="interface" ;;
  5) type="display" ;;
  6) type="layout" ;;
  7) type="module" ;;
  8) type="operation" ;;
  *) echo "Invalid choice"; exit 1 ;;
esac

npx create-directus-extension@latest \
  --type=$type \
  --name=$name \
  --language=typescript

echo "Extension created at extensions/$type-$name"
EOF

chmod +x scripts/create-extension.sh

Docker Configuration

Development Docker Setup

# docker-compose.yml
version: '3.8'

services:
  database:
    image: postgis/postgis:15-alpine
    container_name: directus_database
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: directus
      POSTGRES_PASSWORD: directus
      POSTGRES_DB: directus
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U directus"]
      interval: 10s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    container_name: directus_cache
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  directus:
    build:
      context: .
      dockerfile: Dockerfile.dev
    container_name: directus_app
    ports:
      - "8055:8055"
    volumes:
      - ./uploads:/directus/uploads
      - ./extensions:/directus/extensions
      - ./migrations:/directus/migrations
      - ./.env:/directus/.env
    environment:
      DB_CLIENT: pg
      DB_HOST: database
      DB_PORT: 5432
      DB_DATABASE: directus
      DB_USER: directus
      DB_PASSWORD: directus
      CACHE_ENABLED: "true"
      CACHE_STORE: redis
      CACHE_REDIS: redis://cache:6379
      EXTENSIONS_AUTO_RELOAD: "true"
    depends_on:
      database:
        condition: service_healthy
      cache:
        condition: service_healthy
    command: >
      sh -c "
        npx directus database install &&
        npx directus database migrate:latest &&
        npx directus start
      "

  # Development tools
  adminer:
    image: adminer
    container_name: directus_adminer
    ports:
      - "8080:8080"
    environment:
      ADMINER_DEFAULT_SERVER: database
    depends_on:
      - database

  mailhog:
    image: mailhog/mailhog
    container_name: directus_mailhog
    ports:
      - "1025:1025"  # SMTP server
      - "8025:8025"  # Web UI

volumes:
  postgres_data:
  redis_data:

networks:
  default:
    name: directus_network

Production Docker Setup

# Dockerfile
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY tsconfig.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY extensions ./extensions
COPY src ./src

# Build TypeScript
RUN npm run build

# Production stage
FROM node:18-alpine

WORKDIR /directus

# Install Directus
RUN npm install -g directus

# Copy built extensions
COPY --from=builder /app/dist/extensions ./extensions
COPY --from=builder /app/package*.json ./

# Install production dependencies
RUN npm ci --only=production

# Create uploads directory
RUN mkdir -p uploads

# Set up healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e "require('http').get('http://localhost:8055/server/health', (r) => {r.statusCode === 200 ? process.exit(0) : process.exit(1)})"

# Expose port
EXPOSE 8055

# Start Directus
CMD ["npx", "directus", "start"]

Testing Strategy

Unit Testing with Vitest

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import path from 'path';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'dist/',
        '*.config.ts',
        'tests/',
      ],
    },
    setupFiles: ['./tests/setup.ts'],
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@extensions': path.resolve(__dirname, './extensions'),
      '@utils': path.resolve(__dirname, './src/utils'),
      '@services': path.resolve(__dirname, './src/services'),
    },
  },
});
// tests/setup.ts
import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
import { createTestDatabase, dropTestDatabase } from './helpers/database';
import { mockServices } from './mocks/services';

beforeAll(async () => {
  await createTestDatabase();
  global.testServices = mockServices();
});

afterAll(async () => {
  await dropTestDatabase();
});

beforeEach(() => {
  // Reset mocks
  vi.clearAllMocks();
});

afterEach(() => {
  // Clean up test data
});

Integration Testing

// tests/integration/api.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import { createDirectus, rest, authentication, createItems, readItems } from '@directus/sdk';

describe('API Integration Tests', () => {
  let client: any;

  beforeAll(async () => {
    client = createDirectus('http://localhost:8055')
      .with(authentication())
      .with(rest());

    await client.login('admin@example.com', 'admin');
  });

  describe('Collections API', () => {
    it('should create and read items', async () => {
      // Create item
      const created = await client.request(
        createItems('articles', {
          title: 'Test Article',
          content: 'Test content',
          status: 'published',
        })
      );

      expect(created).toHaveProperty('id');

      // Read items
      const items = await client.request(
        readItems('articles', {
          filter: {
            id: { _eq: created.id },
          },
        })
      );

      expect(items).toHaveLength(1);
      expect(items[0].title).toBe('Test Article');
    });
  });

  describe('Custom Endpoints', () => {
    it('should handle custom analytics endpoint', async () => {
      const response = await fetch('http://localhost:8055/custom/analytics', {
        headers: {
          Authorization: `Bearer ${client.token}`,
        },
      });

      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data).toHaveProperty('data');
      expect(data.data).toHaveProperty('daily');
    });
  });
});

End-to-End Testing with Playwright

// tests/e2e/admin-panel.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Directus Admin Panel', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('http://localhost:8055/admin');

    // Login
    await page.fill('input[type="email"]', 'admin@example.com');
    await page.fill('input[type="password"]', 'admin');
    await page.click('button[type="submit"]');

    await page.waitForURL('**/content');
  });

  test('should create new article', async ({ page }) => {
    // Navigate to articles
    await page.click('text=Articles');

    // Create new item
    await page.click('button:has-text("Create Item")');

    // Fill form
    await page.fill('input[name="title"]', 'Test Article from E2E');
    await page.fill('textarea[name="content"]', 'This is test content');

    // Save
    await page.click('button:has-text("Save")');

    // Verify
    await expect(page.locator('text=Item created')).toBeVisible();
  });

  test('should use custom panel', async ({ page }) => {
    // Navigate to insights
    await page.click('text=Insights');

    // Check custom panel
    await expect(page.locator('.analytics-panel')).toBeVisible();

    // Verify data loads
    await expect(page.locator('.metric-card')).toHaveCount(3);
  });
});

CI/CD Pipeline

GitHub Actions Workflow

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  release:
    types: [created]

env:
  NODE_VERSION: '18'
  DOCKER_REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # Linting and Type Checking
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Type check
        run: npm run type-check

  # Unit and Integration Tests
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_USER: directus
          POSTGRES_PASSWORD: directus
          POSTGRES_DB: directus_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 6379:6379

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run migrations
        env:
          DB_CLIENT: pg
          DB_HOST: localhost
          DB_PORT: 5432
          DB_DATABASE: directus_test
          DB_USER: directus
          DB_PASSWORD: directus
        run: |
          npx directus database install
          npx directus database migrate:latest

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json
          flags: unittests
          name: codecov-umbrella

  # E2E Tests
  e2e:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: |
          npm ci
          npx playwright install --with-deps

      - name: Start services
        run: docker-compose up -d

      - name: Wait for services
        run: |
          timeout 60 sh -c 'until curl -f http://localhost:8055/server/health; do sleep 1; done'

      - name: Run E2E tests
        run: npm run test:e2e

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/

  # Security Scanning
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Snyk Security Scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

      - name: Run npm audit
        run: npm audit --audit-level=high

  # Build and Push Docker Image
  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    if: github.event_name == 'push' || github.event_name == 'release'
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.DOCKER_REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

  # Deploy to Staging
  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - name: Deploy to Kubernetes
        run: |
          echo "Deploying to staging..."
          # kubectl apply -f k8s/staging/

      - name: Run smoke tests
        run: |
          curl -f https://staging.example.com/server/health

  # Deploy to Production
  deploy-production:
    needs: build
    runs-on: ubuntu-latest
    if: github.event_name == 'release'
    environment:
      name: production
      url: https://example.com
    steps:
      - name: Deploy to Kubernetes
        run: |
          echo "Deploying to production..."
          # kubectl apply -f k8s/production/

      - name: Run smoke tests
        run: |
          curl -f https://example.com/server/health

      - name: Notify deployment
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          text: 'Production deployment completed!'
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Database Migration Management

Migration Scripts

// migrations/001_create_custom_tables.ts
import { Knex } from 'knex';

export async function up(knex: Knex): Promise<void> {
  // Create custom analytics table
  await knex.schema.createTable('custom_analytics', (table) => {
    table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
    table.string('event_type', 50).notNullable();
    table.string('event_category', 50);
    table.jsonb('event_data');
    table.uuid('user_id').references('id').inTable('directus_users');
    table.timestamp('created_at').defaultTo(knex.fn.now());
    table.index(['event_type', 'created_at']);
    table.index('user_id');
  });

  // Create custom settings table
  await knex.schema.createTable('custom_settings', (table) => {
    table.string('key', 100).primary();
    table.jsonb('value').notNullable();
    table.string('type', 20).defaultTo('string');
    table.text('description');
    table.timestamps(true, true);
  });
}

export async function down(knex: Knex): Promise<void> {
  await knex.schema.dropTableIfExists('custom_settings');
  await knex.schema.dropTableIfExists('custom_analytics');
}

Migration Runner Script

// scripts/migrate.ts
import { Knex } from 'knex';
import { config } from 'dotenv';
import path from 'path';

config();

const knexConfig: Knex.Config = {
  client: process.env.DB_CLIENT || 'pg',
  connection: {
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT || '5432'),
    database: process.env.DB_DATABASE,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
  },
  migrations: {
    directory: path.join(__dirname, '../migrations'),
    extension: 'ts',
    tableName: 'knex_migrations',
  },
};

const knex = require('knex')(knexConfig);

async function runMigrations() {
  try {
    console.log('Running migrations...');

    const [batch, migrations] = await knex.migrate.latest();

    if (migrations.length === 0) {
      console.log('Database is already up to date');
    } else {
      console.log(`Batch ${batch} run: ${migrations.length} migrations`);
      migrations.forEach(migration => {
        console.log(`  - ${migration}`);
      });
    }

    process.exit(0);
  } catch (error) {
    console.error('Migration failed:', error);
    process.exit(1);
  }
}

async function rollbackMigrations() {
  try {
    console.log('Rolling back migrations...');

    const [batch, migrations] = await knex.migrate.rollback();

    if (migrations.length === 0) {
      console.log('No migrations to rollback');
    } else {
      console.log(`Batch ${batch} rolled back: ${migrations.length} migrations`);
      migrations.forEach(migration => {
        console.log(`  - ${migration}`);
      });
    }

    process.exit(0);
  } catch (error) {
    console.error('Rollback failed:', error);
    process.exit(1);
  }
}

// Parse command line arguments
const command = process.argv[2];

switch (command) {
  case 'up':
  case 'latest':
    runMigrations();
    break;
  case 'down':
  case 'rollback':
    rollbackMigrations();
    break;
  default:
    console.log('Usage: npm run migrate [up|down]');
    process.exit(1);
}

Development Tools Setup

VS Code Configuration

// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.organizeImports": true
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue"
  ],
  "files.exclude": {
    "node_modules": true,
    "dist": true,
    ".turbo": true,
    "uploads": true
  },
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/uploads": true
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "directus.api.url": "http://localhost:8055",
  "directus.api.staticToken": "${env:DIRECTUS_STATIC_TOKEN}"
}

VS Code Launch Configuration

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Directus",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/node_modules/.bin/directus",
      "args": ["start"],
      "env": {
        "NODE_ENV": "development",
        "EXTENSIONS_AUTO_RELOAD": "true"
      },
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Extension",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/extensions/${input:extensionName}/src/index.ts",
      "preLaunchTask": "npm: build:extensions",
      "outFiles": ["${workspaceFolder}/extensions/${input:extensionName}/dist/**/*.js"],
      "env": {
        "NODE_ENV": "development"
      }
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Tests",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/node_modules/.bin/vitest",
      "args": ["--run", "${file}"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ],
  "inputs": [
    {
      "id": "extensionName",
      "type": "promptString",
      "description": "Enter the extension name to debug"
    }
  ]
}

Performance Monitoring

Application Performance Monitoring

// src/monitoring/apm.ts
import * as Sentry from '@sentry/node';
import { ProfilingIntegration } from '@sentry/profiling-node';
import { performance } from 'perf_hooks';

export class PerformanceMonitor {
  private metrics: Map<string, number[]> = new Map();

  constructor() {
    // Initialize Sentry
    if (process.env.SENTRY_DSN) {
      Sentry.init({
        dsn: process.env.SENTRY_DSN,
        integrations: [
          new ProfilingIntegration(),
        ],
        tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
        profilesSampleRate: 1.0,
      });
    }
  }

  startTimer(operation: string): () => void {
    const start = performance.now();

    return () => {
      const duration = performance.now() - start;
      this.recordMetric(operation, duration);

      // Log slow operations
      if (duration > 1000) {
        console.warn(`Slow operation: ${operation} took ${duration.toFixed(2)}ms`);
      }
    };
  }

  recordMetric(name: string, value: number): void {
    if (!this.metrics.has(name)) {
      this.metrics.set(name, []);
    }

    const values = this.metrics.get(name)!;
    values.push(value);

    // Keep only last 100 values
    if (values.length > 100) {
      values.shift();
    }
  }

  getStats(name: string): {
    avg: number;
    min: number;
    max: number;
    p95: number;
  } | null {
    const values = this.metrics.get(name);
    if (!values || values.length === 0) return null;

    const sorted = [...values].sort((a, b) => a - b);
    const sum = sorted.reduce((a, b) => a + b, 0);

    return {
      avg: sum / sorted.length,
      min: sorted[0],
      max: sorted[sorted.length - 1],
      p95: sorted[Math.floor(sorted.length * 0.95)],
    };
  }

  async measureDatabaseQuery<T>(
    queryName: string,
    query: () => Promise<T>
  ): Promise<T> {
    const transaction = Sentry.startTransaction({
      op: 'db.query',
      name: queryName,
    });

    const endTimer = this.startTimer(`db.${queryName}`);

    try {
      const result = await query();
      transaction.setStatus('ok');
      return result;
    } catch (error) {
      transaction.setStatus('internal_error');
      throw error;
    } finally {
      endTimer();
      transaction.finish();
    }
  }

  reportMetrics(): void {
    const report: any = {};

    this.metrics.forEach((values, name) => {
      report[name] = this.getStats(name);
    });

    console.log('Performance Report:', JSON.stringify(report, null, 2));

    // Send to monitoring service
    if (process.env.MONITORING_ENDPOINT) {
      fetch(process.env.MONITORING_ENDPOINT, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          timestamp: new Date().toISOString(),
          metrics: report,
        }),
      }).catch(console.error);
    }
  }
}

Deployment Strategies

Kubernetes Deployment

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: directus
  namespace: production
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: directus
  template:
    metadata:
      labels:
        app: directus
    spec:
      containers:
      - name: directus
        image: ghcr.io/myorg/directus:latest
        ports:
        - containerPort: 8055
        env:
        - name: DB_CLIENT
          value: "pg"
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: directus-secrets
              key: db-host
        - name: DB_DATABASE
          value: "directus"
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: directus-secrets
              key: db-user
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: directus-secrets
              key: db-password
        - name: KEY
          valueFrom:
            secretKeyRef:
              name: directus-secrets
              key: app-key
        - name: SECRET
          valueFrom:
            secretKeyRef:
              name: directus-secrets
              key: app-secret
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /server/health
            port: 8055
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /server/ping
            port: 8055
          initialDelaySeconds: 10
          periodSeconds: 5
        volumeMounts:
        - name: uploads
          mountPath: /directus/uploads
        - name: extensions
          mountPath: /directus/extensions
      volumes:
      - name: uploads
        persistentVolumeClaim:
          claimName: directus-uploads
      - name: extensions
        configMap:
          name: directus-extensions

---
apiVersion: v1
kind: Service
metadata:
  name: directus
  namespace: production
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8055
  selector:
    app: directus

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: directus-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: directus
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Terraform Infrastructure

# terraform/main.tf
terraform {
  required_version = ">= 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket = "terraform-state-directus"
    key    = "production/terraform.tfstate"
    region = "us-east-1"
  }
}

provider "aws" {
  region = var.aws_region
}

# RDS Database
resource "aws_db_instance" "directus" {
  identifier     = "directus-production"
  engine         = "postgres"
  engine_version = "15.3"
  instance_class = "db.t3.medium"

  allocated_storage     = 100
  storage_type          = "gp3"
  storage_encrypted     = true

  db_name  = "directus"
  username = var.db_username
  password = var.db_password

  vpc_security_group_ids = [aws_security_group.database.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name

  backup_retention_period = 30
  backup_window          = "03:00-04:00"
  maintenance_window     = "sun:04:00-sun:05:00"

  skip_final_snapshot = false
  final_snapshot_identifier = "directus-final-snapshot-${timestamp()}"

  tags = {
    Name        = "directus-production"
    Environment = "production"
  }
}

# ElastiCache Redis
resource "aws_elasticache_cluster" "directus" {
  cluster_id           = "directus-cache"
  engine              = "redis"
  node_type           = "cache.t3.micro"
  num_cache_nodes     = 1
  parameter_group_name = "default.redis7"
  port                = 6379

  subnet_group_name = aws_elasticache_subnet_group.main.name
  security_group_ids = [aws_security_group.cache.id]

  tags = {
    Name        = "directus-cache"
    Environment = "production"
  }
}

# S3 Bucket for uploads
resource "aws_s3_bucket" "uploads" {
  bucket = "directus-uploads-${var.environment}"

  tags = {
    Name        = "directus-uploads"
    Environment = var.environment
  }
}

resource "aws_s3_bucket_versioning" "uploads" {
  bucket = aws_s3_bucket.uploads.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "uploads" {
  bucket = aws_s3_bucket.uploads.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# ECS Cluster
resource "aws_ecs_cluster" "main" {
  name = "directus-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = {
    Name        = "directus-cluster"
    Environment = var.environment
  }
}

# ECS Task Definition
resource "aws_ecs_task_definition" "directus" {
  family                   = "directus"
  network_mode            = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                     = "1024"
  memory                  = "2048"

  container_definitions = jsonencode([{
    name  = "directus"
    image = "${var.ecr_repository_url}:${var.image_tag}"

    portMappings = [{
      containerPort = 8055
      protocol      = "tcp"
    }]

    environment = [
      {
        name  = "DB_CLIENT"
        value = "pg"
      },
      {
        name  = "DB_HOST"
        value = aws_db_instance.directus.address
      },
      {
        name  = "DB_DATABASE"
        value = "directus"
      },
      {
        name  = "CACHE_ENABLED"
        value = "true"
      },
      {
        name  = "CACHE_STORE"
        value = "redis"
      },
      {
        name  = "CACHE_REDIS"
        value = "redis://${aws_elasticache_cluster.directus.cache_nodes[0].address}:6379"
      },
      {
        name  = "STORAGE_LOCATIONS"
        value = "s3"
      },
      {
        name  = "STORAGE_S3_BUCKET"
        value = aws_s3_bucket.uploads.id
      }
    ]

    secrets = [
      {
        name      = "DB_USER"
        valueFrom = aws_secretsmanager_secret.db_credentials.arn
      },
      {
        name      = "DB_PASSWORD"
        valueFrom = aws_secretsmanager_secret.db_credentials.arn
      },
      {
        name      = "KEY"
        valueFrom = aws_secretsmanager_secret.app_secrets.arn
      },
      {
        name      = "SECRET"
        valueFrom = aws_secretsmanager_secret.app_secrets.arn
      }
    ]

    logConfiguration = {
      logDriver = "awslogs"
      options = {
        "awslogs-group"         = aws_cloudwatch_log_group.directus.name
        "awslogs-region"        = var.aws_region
        "awslogs-stream-prefix" = "directus"
      }
    }
  }])

  tags = {
    Name        = "directus-task"
    Environment = var.environment
  }
}

# Output values
output "database_endpoint" {
  value       = aws_db_instance.directus.endpoint
  description = "RDS database endpoint"
}

output "cache_endpoint" {
  value       = aws_elasticache_cluster.directus.cache_nodes[0].address
  description = "Redis cache endpoint"
}

output "s3_bucket" {
  value       = aws_s3_bucket.uploads.id
  description = "S3 bucket for uploads"
}

Monitoring & Logging

Structured Logging

// src/utils/logger.ts
import winston from 'winston';
import { LoggingWinston } from '@google-cloud/logging-winston';

const logFormat = winston.format.combine(
  winston.format.timestamp(),
  winston.format.errors({ stack: true }),
  winston.format.json()
);

const transports: winston.transport[] = [
  new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  }),
];

// Add cloud logging in production
if (process.env.NODE_ENV === 'production') {
  if (process.env.GOOGLE_CLOUD_PROJECT) {
    transports.push(new LoggingWinston());
  }
}

export const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: logFormat,
  defaultMeta: {
    service: 'directus',
    environment: process.env.NODE_ENV,
  },
  transports,
});

// Request logging middleware
export function requestLogger(req: any, res: any, next: any) {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;

    logger.info('HTTP Request', {
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration,
      ip: req.ip,
      userAgent: req.get('user-agent'),
      userId: req.accountability?.user,
    });
  });

  next();
}

Best Practices

Code Quality Standards

// .eslintrc.js
module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 2022,
    sourceType: 'module',
    project: './tsconfig.json',
  },
  plugins: [
    '@typescript-eslint',
    'import',
    'prettier',
    'security',
  ],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
    'plugin:security/recommended',
    'prettier',
  ],
  rules: {
    '@typescript-eslint/explicit-function-return-type': 'error',
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/strict-boolean-expressions': 'error',
    'import/order': ['error', {
      'groups': ['builtin', 'external', 'internal'],
      'newlines-between': 'always',
      'alphabetize': { 'order': 'asc' },
    }],
    'no-console': ['warn', { allow: ['warn', 'error'] }],
    'security/detect-object-injection': 'warn',
  },
  ignorePatterns: ['dist/', 'node_modules/', 'coverage/'],
};

Security Best Practices

// src/security/security-middleware.ts
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import mongoSanitize from 'express-mongo-sanitize';
import { v4 as uuidv4 } from 'uuid';

export function setupSecurity(app: any): void {
  // Security headers
  app.use(helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
        imgSrc: ["'self'", "data:", "https:"],
      },
    },
  }));

  // Rate limiting
  const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
    message: 'Too many requests from this IP',
    standardHeaders: true,
    legacyHeaders: false,
  });

  app.use('/api/', limiter);

  // Stricter rate limiting for auth endpoints
  const authLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 5,
    skipSuccessfulRequests: true,
  });

  app.use('/auth/', authLimiter);

  // Request sanitization
  app.use(mongoSanitize());

  // Request ID for tracing
  app.use((req: any, res: any, next: any) => {
    req.id = req.headers['x-request-id'] || uuidv4();
    res.setHeader('X-Request-ID', req.id);
    next();
  });

  // CORS configuration
  app.use((req: any, res: any, next: any) => {
    const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'];
    const origin = req.headers.origin;

    if (allowedOrigins.includes(origin)) {
      res.setHeader('Access-Control-Allow-Origin', origin);
    }

    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.setHeader('Access-Control-Allow-Credentials', 'true');

    if (req.method === 'OPTIONS') {
      return res.sendStatus(204);
    }

    next();
  });
}

Success Metrics

  • ✅ Development environment setup < 5 minutes
  • ✅ TypeScript compilation with zero errors
  • ✅ Test coverage > 80%
  • ✅ CI/CD pipeline execution < 10 minutes
  • ✅ Docker build size < 500MB
  • ✅ Zero-downtime deployments
  • ✅ Database migrations rollback capability
  • ✅ Monitoring alerts < 1 minute response time
  • ✅ Security scanning passes all checks
  • ✅ Performance benchmarks meet SLA requirements

Resources

Version History

  • 1.0.0 - Initial release with comprehensive development workflow patterns

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.19%
按下载量换算1,851

Claude

31.63%
按下载量换算1,764

Cursor

17.22%
按下载量换算960

Gemini CLI

9.09%
按下载量换算507

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills