feat(iam): 角色权限管理 + 权限缓存 + 指标 + 鉴权中间件增强 + nextstep 文档
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { getRedis } from "../../config/redis.js";
|
||||
import { cacheMetrics } from "../observability/metrics.js";
|
||||
|
||||
const PERMISSION_CACHE_TTL_SECONDS = 300; // 5 分钟
|
||||
|
||||
@@ -22,14 +23,20 @@ export class PermissionCacheService {
|
||||
async getPermissions(userId: string): Promise<string[] | null> {
|
||||
const redis = getRedis();
|
||||
const raw = await redis.get(PermissionCacheService.buildKey(userId));
|
||||
if (!raw) return null;
|
||||
if (!raw) {
|
||||
cacheMetrics.recordMiss();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (Array.isArray(parsed) && parsed.every((p) => typeof p === "string")) {
|
||||
cacheMetrics.recordHit();
|
||||
return parsed as string[];
|
||||
}
|
||||
cacheMetrics.recordMiss();
|
||||
return null;
|
||||
} catch {
|
||||
cacheMetrics.recordMiss();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -44,8 +51,9 @@ export class PermissionCacheService {
|
||||
);
|
||||
}
|
||||
|
||||
async invalidate(userId: string): Promise<void> {
|
||||
async invalidate(userId: string, reason = "manual"): Promise<void> {
|
||||
const redis = getRedis();
|
||||
await redis.del(PermissionCacheService.buildKey(userId));
|
||||
cacheMetrics.recordInvalidation(reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,56 @@ registry.registerMetric(
|
||||
// 这些指标无需业务代码埋点,prom-client 自动采集
|
||||
promClient.collectDefaultMetrics({ register: registry });
|
||||
|
||||
// Redis 权限缓存指标(I3 裁决:DB 驱动 + Redis 缓存可观测性)
|
||||
registry.registerMetric(
|
||||
new promClient.Counter({
|
||||
name: "iam_permission_cache_hits_total",
|
||||
help: "Total number of permission cache hits (Redis)",
|
||||
}),
|
||||
);
|
||||
|
||||
registry.registerMetric(
|
||||
new promClient.Counter({
|
||||
name: "iam_permission_cache_misses_total",
|
||||
help: "Total number of permission cache misses (Redis)",
|
||||
}),
|
||||
);
|
||||
|
||||
registry.registerMetric(
|
||||
new promClient.Counter({
|
||||
name: "iam_permission_cache_invalidations_total",
|
||||
help: "Total number of permission cache invalidations (Redis)",
|
||||
labelNames: ["reason"],
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* 缓存指标访问器:供 PermissionCacheService 使用.
|
||||
* 避免在业务代码中直接操作 registry,统一通过此门面.
|
||||
*/
|
||||
export const cacheMetrics = {
|
||||
recordHit(): void {
|
||||
const metric = registry.getSingleMetric("iam_permission_cache_hits_total");
|
||||
if (metric && "inc" in metric) {
|
||||
(metric as promClient.Counter).inc();
|
||||
}
|
||||
},
|
||||
recordMiss(): void {
|
||||
const metric = registry.getSingleMetric(
|
||||
"iam_permission_cache_misses_total",
|
||||
);
|
||||
if (metric && "inc" in metric) {
|
||||
(metric as promClient.Counter).inc();
|
||||
}
|
||||
},
|
||||
recordInvalidation(reason: string): void {
|
||||
const metric = registry.getSingleMetric(
|
||||
"iam_permission_cache_invalidations_total",
|
||||
);
|
||||
if (metric && "inc" in metric) {
|
||||
(metric as promClient.Counter).inc({ reason });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export { registry as metricsRegistry };
|
||||
|
||||
Reference in New Issue
Block a user