基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/RunnerQuan/SAFE-Agent --skill vendix-backend-middleware命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
Find doctors with Healthgrades - search providers, read reviews, and check credentials
Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks.
基于RFM模型和回归算法的客户生命周期价值(LTV)预测分析工具,支持电商和零售业务的客户价值预测。使用时需要客户交易数据、订单历史或消费记录,自动进行RFM特征工程、回归建模和价值预测。
| name | vendix-backend-middleware |
| description | Middleware configuration. |
| metadata | {"scope":["root"],"auto_invoke":"Configuring middleware"} |
Middleware Pattern - Resolución de dominios dinámicos, contexto multi-tenant y middlewares personalizados.
El DomainResolverMiddleware es responsable de:
tienda.tiendavendix.com)metadata: scope: [root] auto_invoke: "Configuring middleware"
File: common/middleware/domain-resolver.middleware.ts
import { Injectable, NestMiddleware, NotFoundException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { PrismaService } from '@/prisma/services/prisma.service';
import LRU from 'lru-cache';
interface DomainInfo {
store_id: number;
organization_id: number;
domain_type: 'store' | 'organization' | 'vendix_core';
}
@Injectable()
export class DomainResolverMiddleware implements NestMiddleware {
private cache = new LRU<string, DomainInfo>({
max: 500, // Max 500 cached domains
ttl: 1000 * 60 * 5, // 5 minutes TTL
});
constructor(private readonly prisma: PrismaService) {}
async use(req: Request, res: Response, next: NextFunction) {
const host = req.headers.host; // tienda.tiendavendix.com
// Check cache first
const cached = this.cache.get(host);
if (cached) {
req['store_id'] = cached.store_id;
req['organization_id'] = cached.organization_id;
req['domain_type'] = cached.domain_type;
return next();
}
// Resolve domain
const domain_info = await this.resolveDomain(host);
if (!domain_info) {
throw new NotFoundException('Domain not found');
}
// Cache result
this.cache.set(host, domain_info);
// Inject into request
req['store_id'] = domain_info.store_id;
req['organization_id'] = domain_info.organization_id;
req['domain_type'] = domain_info.domain_type;
next();
}
private async resolveDomain(host: string): Promise<DomainInfo | null> {
// Extract subdomain
const subdomain = this.extractSubdomain(host);
// Check for vendix core domain
if (this.isVendixCoreDomain(host)) {
return {
store_id: null,
organization_id: null,
domain_type: 'vendix_core',
};
}
// Try to resolve as store domain
const store = await this.prisma.stores.findFirst({
where: { domain_name: subdomain, is_active: true },
select: {
id: true,
organization_id: true,
},
});
if (store) {
return {
store_id: store.id,
organization_id: store.organization_id,
domain_type: 'store',
};
}
// Try to resolve as organization domain
const organization = await this.prisma.organizations.findFirst({
where: { domain_name: subdomain, is_active: true },
select: {
id: true,
},
});
if (organization) {
return {
store_id: null,
organization_id: organization.id,
domain_type: 'organization',
};
}
return null;
}
private extractSubdomain(host: string): string {
// tienda.tiendavendix.com -> tienda
const parts = host.split('.');
return parts[0];
}
private isVendixCoreDomain(host: string): boolean {
// Check if it's the main vendix domain
const core_domains = [
'vendix.com',
'tiendavendix.com',
'localhost',
];
return core_domains.some(domain => host.includes(domain));
}
}
File: app.module.ts
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { DomainResolverMiddleware } from './common/middleware/domain-resolver.middleware';
@Module({
// ... module configuration
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(DomainResolverMiddleware)
.forRoutes('*'); // Apply to ALL routes
}
}
File: common/context/request-context.service.ts
import { Injectable, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
constructor(@Inject(REQUEST) private readonly request: Request) {}
get user_id(): number {
return this.request['user']?.id;
}
get organization_id(): number {
return this.request['organization_id'];
}
get store_id(): number {
return this.request['store_id'];
}
get domain_type(): string {
return .[];
}
(): [] {
.[]?. || [];
}
(): {
..();
}
(): {
..() ||
..();
}
}
Key Features:
File: common/middleware/logging.middleware.ts
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggingMiddleware implements NestMiddleware {
private logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl, ip } = req;
const user_agent = req.get('user-agent') || '';
const start = Date.now();
res.on('finish', () => {
const { statusCode } = res;
const content_length = res.get('content-length');
const response_time = Date.now() - start;
this.logger.log(
,
);
});
();
}
}
File: common/middleware/rate-limit.middleware.ts
import { Injectable, NestMiddleware, HttpException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import LRU from 'lru-cache';
@Injectable()
export class RateLimitMiddleware implements NestMiddleware {
private rate_limits = new LRU<string, { count: number; reset_time: number }>({
max: 1000,
ttl: 1000 * 60, // 1 minute
});
private readonly LIMIT = 100; // 100 requests per minute
use(req: Request, res: Response, next: NextFunction) {
const key = this.getKey(req);
const now = .();
limit_data = ..(key);
(!limit_data || now > limit_data.) {
..(key, {
: ,
: now + ,
});
();
}
(limit_data. >= .) {
(, );
}
limit_data.++;
();
}
(: ): {
user_id = req[]?. || ;
ip = req.;
;
}
}
File: common/middleware/cors.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class CorsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const allowed_origins = [
'https://vendix.com',
'https://*.vendix.com',
];
const origin = req.headers.origin;
if (allowed_origins.some(allowed => origin?.match(allowed))) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === ) {
res.();
}
();
}
}
File: app.module.ts
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
CorsMiddleware,
LoggingMiddleware,
RateLimitMiddleware,
DomainResolverMiddleware, // Must be last
)
.forRoutes('*')
.apply(
// Specific middlewares for specific routes
)
.exclude(
// Exclude certain routes
'health',
'public/(.*)',
)
.forRoutes('api');
}
}
Order Matters:
@Controller('products')
export class ProductsController {
constructor(
private readonly context: RequestContextService,
private readonly prisma: EcommercePrismaService,
) {}
@Get()
async findAll() {
// Context automatically injected from middleware
return this.prisma.products.findMany({
where: {
store_id: this.context.store_id,
organization_id: this.context.organization_id,
},
});
}
}
@Controller('admin')
export class AdminController {
constructor(private readonly context: RequestContextService) {}
@Get('settings')
async getSettings() {
if (this.context.is_super_admin) {
// Super admin can see all settings
return this.getSystemSettings();
}
if (this.context.is_owner) {
// Owner can see organization settings
return this.getOrganizationSettings(this.context.organization_id);
}
// Regular user
return this.getStoreSettings(this.context.store_id);
}
}
vendix.com
tiendavendix.com
localhost
Context:
organization_id: nullstore_id: nulldomain_type: 'vendix_core'Used for:
mycompany.tiendavendix.com
Context:
organization_id: 123store_id: nulldomain_type: 'organization'Used for:
myshop.tiendavendix.com
Context:
organization_id: 123store_id: 456domain_type: 'store'Used for:
private cache = new LRU<string, DomainInfo>({
max: 500, // Cache up to 500 domains
ttl: 1000 * 60 * 5, // 5 minutes TTL
});
Benefits:
// When a store domain is updated
async updateStoreDomain(store_id: number, new_domain: string) {
await this.prisma.stores.update({
where: { id: store_id },
data: { domain_name: new_domain },
});
// Invalidate cache for this domain
this.cache.del(new_domain);
}
| File | Purpose |
|---|---|
common/middleware/domain-resolver.middleware.ts | Domain resolution and caching |
common/context/request-context.service.ts | Request-scoped context service |
app.module.ts | Middleware registration |
common/middleware/logging.middleware.ts | HTTP logging |
common/middleware/rate-limit.middleware.ts | Rate limiting |
common/middleware/cors.middleware.ts | CORS handling |
vendix-backend-auth - JWT authentication setupvendix-backend-domain - Domain architecture patternsvendix-backend-prisma - Prisma service patternsvendix-naming-conventions - Naming conventions (CRITICAL)