@meyicloud/meyi-insight-server 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/source.controller.d.ts +2 -1
- package/dist/controllers/source.controller.d.ts.map +1 -1
- package/dist/controllers/source.controller.js +24 -5
- package/dist/controllers/source.controller.js.map +1 -1
- package/dist/database/schema.d.ts +160 -0
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +17 -0
- package/dist/database/schema.js.map +1 -1
- package/dist/features/sla/domain/sla.evaluator.d.ts +8 -0
- package/dist/features/sla/domain/sla.evaluator.d.ts.map +1 -0
- package/dist/features/sla/domain/sla.evaluator.js +72 -0
- package/dist/features/sla/domain/sla.evaluator.js.map +1 -0
- package/dist/features/sla/domain/sla.resolver.d.ts +16 -0
- package/dist/features/sla/domain/sla.resolver.d.ts.map +1 -0
- package/dist/features/sla/domain/sla.resolver.js +28 -0
- package/dist/features/sla/domain/sla.resolver.js.map +1 -0
- package/dist/features/sla/domain/sla.types.d.ts +59 -0
- package/dist/features/sla/domain/sla.types.d.ts.map +1 -0
- package/dist/features/sla/domain/sla.types.js +6 -0
- package/dist/features/sla/domain/sla.types.js.map +1 -0
- package/dist/features/sla/index.d.ts +7 -0
- package/dist/features/sla/index.d.ts.map +1 -0
- package/dist/features/sla/index.js +7 -0
- package/dist/features/sla/index.js.map +1 -0
- package/dist/features/sla/sla-policy.model.d.ts +19 -0
- package/dist/features/sla/sla-policy.model.d.ts.map +1 -0
- package/dist/features/sla/sla-policy.model.js +44 -0
- package/dist/features/sla/sla-policy.model.js.map +1 -0
- package/dist/features/sla/sla-policy.service.d.ts +29 -0
- package/dist/features/sla/sla-policy.service.d.ts.map +1 -0
- package/dist/features/sla/sla-policy.service.js +67 -0
- package/dist/features/sla/sla-policy.service.js.map +1 -0
- package/dist/features/sla/sla.controller.d.ts +9 -0
- package/dist/features/sla/sla.controller.d.ts.map +1 -0
- package/dist/features/sla/sla.controller.js +52 -0
- package/dist/features/sla/sla.controller.js.map +1 -0
- package/dist/features/sla/sla.routes.d.ts +4 -0
- package/dist/features/sla/sla.routes.d.ts.map +1 -0
- package/dist/features/sla/sla.routes.js +12 -0
- package/dist/features/sla/sla.routes.js.map +1 -0
- package/dist/features/sla/sla.schema.d.ts +8 -0
- package/dist/features/sla/sla.schema.d.ts.map +1 -0
- package/dist/features/sla/sla.schema.js +18 -0
- package/dist/features/sla/sla.schema.js.map +1 -0
- package/dist/features/sources/cloudtrail/controller.d.ts.map +1 -1
- package/dist/features/sources/cloudtrail/controller.js +35 -14
- package/dist/features/sources/cloudtrail/controller.js.map +1 -1
- package/dist/features/sources/cloudtrail/reconciler.d.ts +8 -0
- package/dist/features/sources/cloudtrail/reconciler.d.ts.map +1 -0
- package/dist/features/sources/cloudtrail/reconciler.js +111 -0
- package/dist/features/sources/cloudtrail/reconciler.js.map +1 -0
- package/dist/features/sources/cloudtrail/template.d.ts.map +1 -1
- package/dist/features/sources/cloudtrail/template.js +29 -5
- package/dist/features/sources/cloudtrail/template.js.map +1 -1
- package/dist/migrations/run.d.ts.map +1 -1
- package/dist/migrations/run.js +26 -0
- package/dist/migrations/run.js.map +1 -1
- package/dist/models/incident.model.d.ts.map +1 -1
- package/dist/models/incident.model.js +1 -0
- package/dist/models/incident.model.js.map +1 -1
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/source.routes.js +1 -1
- package/dist/routes/source.routes.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/dist/service.d.ts +7 -4
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +7 -3
- package/dist/service.js.map +1 -1
- package/dist/services/incident.service.d.ts +7 -4
- package/dist/services/incident.service.d.ts.map +1 -1
- package/dist/services/incident.service.js +15 -19
- package/dist/services/incident.service.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SlaPolicy, SlaPolicyRecord, SlaPolicyResolver } from "./sla.types.js";
|
|
2
|
+
/** Fallback limits when an account has no explicit policy (matches historic behaviour). */
|
|
3
|
+
export declare const DEFAULT_SLA_CRITICAL: {
|
|
4
|
+
readonly ackMinutes: 10;
|
|
5
|
+
readonly resolveMinutes: 60;
|
|
6
|
+
};
|
|
7
|
+
export declare const DEFAULT_SLA_STANDARD: {
|
|
8
|
+
readonly ackMinutes: 30;
|
|
9
|
+
readonly resolveMinutes: 120;
|
|
10
|
+
};
|
|
11
|
+
export declare function severityOf(tags?: Record<string, string> | null): string;
|
|
12
|
+
export declare function defaultSlaPolicy(tags?: Record<string, string> | null): SlaPolicy;
|
|
13
|
+
export declare function policyFromRecord(record: Pick<SlaPolicyRecord, "enabled" | "ackMinutes" | "resolveMinutes">): SlaPolicy;
|
|
14
|
+
/** Builds a resolver over a set of account overrides. An account policy applies to every severity. */
|
|
15
|
+
export declare function createSlaResolver(records: SlaPolicyRecord[]): SlaPolicyResolver;
|
|
16
|
+
//# sourceMappingURL=sla.resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.resolver.d.ts","sourceRoot":"","sources":["../../../../src/features/sla/domain/sla.resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB;;;CAAkD,CAAC;AACpF,eAAO,MAAM,oBAAoB;;;CAAmD,CAAC;AAErF,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,MAAM,CAEvE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAIhF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,GAAG,gBAAgB,CAAC,GAAG,SAAS,CAOtH;AAED,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,iBAAiB,CAM/E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Fallback limits when an account has no explicit policy (matches historic behaviour). */
|
|
2
|
+
export const DEFAULT_SLA_CRITICAL = { ackMinutes: 10, resolveMinutes: 60 };
|
|
3
|
+
export const DEFAULT_SLA_STANDARD = { ackMinutes: 30, resolveMinutes: 120 };
|
|
4
|
+
export function severityOf(tags) {
|
|
5
|
+
return String(tags?.severity || tags?.priority || "").toLowerCase();
|
|
6
|
+
}
|
|
7
|
+
export function defaultSlaPolicy(tags) {
|
|
8
|
+
const s = severityOf(tags);
|
|
9
|
+
const limits = s === "critical" || s === "p1" ? DEFAULT_SLA_CRITICAL : DEFAULT_SLA_STANDARD;
|
|
10
|
+
return { enabled: true, ...limits, source: "default" };
|
|
11
|
+
}
|
|
12
|
+
export function policyFromRecord(record) {
|
|
13
|
+
return {
|
|
14
|
+
enabled: record.enabled,
|
|
15
|
+
ackMinutes: record.ackMinutes,
|
|
16
|
+
resolveMinutes: record.resolveMinutes,
|
|
17
|
+
source: "account",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Builds a resolver over a set of account overrides. An account policy applies to every severity. */
|
|
21
|
+
export function createSlaResolver(records) {
|
|
22
|
+
const byAccount = new Map(records.map((r) => [r.accountId, r]));
|
|
23
|
+
return (accountId, tags) => {
|
|
24
|
+
const record = accountId ? byAccount.get(accountId) : undefined;
|
|
25
|
+
return record ? policyFromRecord(record) : defaultSlaPolicy(tags);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=sla.resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.resolver.js","sourceRoot":"","sources":["../../../../src/features/sla/domain/sla.resolver.ts"],"names":[],"mappings":"AAEA,2FAA2F;AAC3F,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAW,CAAC;AACpF,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,GAAG,EAAW,CAAC;AAErF,MAAM,UAAU,UAAU,CAAC,IAAoC;IAC7D,OAAO,MAAM,CAAC,IAAI,EAAE,QAAQ,IAAI,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAoC;IACnE,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAC;IAC5F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAA0E;IACzG,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,MAAM,EAAE,SAAS;KAClB,CAAC;AACJ,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,iBAAiB,CAAC,OAA0B;IAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,OAAO,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Where an effective SLA policy came from. */
|
|
2
|
+
export type SlaPolicySource = "account" | "default";
|
|
3
|
+
export interface SlaPolicy {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
ackMinutes: number;
|
|
6
|
+
resolveMinutes: number;
|
|
7
|
+
source: SlaPolicySource;
|
|
8
|
+
}
|
|
9
|
+
/** Persisted per-account override (one row per tenant + AWS account). */
|
|
10
|
+
export interface SlaPolicyRecord {
|
|
11
|
+
tenantId: string;
|
|
12
|
+
accountId: string;
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
ackMinutes: number;
|
|
15
|
+
resolveMinutes: number;
|
|
16
|
+
updatedBy: string | null;
|
|
17
|
+
updatedAt: Date | string | null;
|
|
18
|
+
}
|
|
19
|
+
export type SlaPhaseState = "met" | "met_late" | "pending" | "overdue" | "disabled";
|
|
20
|
+
export interface SlaPhase {
|
|
21
|
+
state: SlaPhaseState;
|
|
22
|
+
limitMinutes: number;
|
|
23
|
+
/** Minutes from creation until the phase completed (or until now while pending). */
|
|
24
|
+
elapsedMinutes: number | null;
|
|
25
|
+
/** Minutes left while pending; negative once overdue. Null when not pending. */
|
|
26
|
+
remainingMinutes: number | null;
|
|
27
|
+
}
|
|
28
|
+
export interface SlaEvaluation {
|
|
29
|
+
policy: SlaPolicy;
|
|
30
|
+
ack: SlaPhase;
|
|
31
|
+
resolve: SlaPhase;
|
|
32
|
+
/** True when either phase is overdue or was completed late. */
|
|
33
|
+
breached: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Minimal incident shape the evaluator needs. */
|
|
36
|
+
export interface SlaIncidentInput {
|
|
37
|
+
status: string;
|
|
38
|
+
createdAt: Date | string | null | undefined;
|
|
39
|
+
/** When the incident left "open" (acknowledged). */
|
|
40
|
+
ackedAt?: Date | string | null;
|
|
41
|
+
resolvedAt?: Date | string | null;
|
|
42
|
+
accountId?: string | null;
|
|
43
|
+
tags?: Record<string, string> | null;
|
|
44
|
+
}
|
|
45
|
+
/** Resolves the effective policy for an incident's account. Built once per request. */
|
|
46
|
+
export type SlaPolicyResolver = (accountId: string | null | undefined, tags?: Record<string, string> | null) => SlaPolicy;
|
|
47
|
+
/**
|
|
48
|
+
* Port used by incident code so it doesn't depend on persistence details.
|
|
49
|
+
* `tenantId` undefined yields a resolver that only returns defaults.
|
|
50
|
+
*/
|
|
51
|
+
export interface SlaPolicyProvider {
|
|
52
|
+
loadResolver(tenantId?: string): Promise<SlaPolicyResolver>;
|
|
53
|
+
}
|
|
54
|
+
export declare const SLA_LIMITS: {
|
|
55
|
+
readonly minMinutes: 1;
|
|
56
|
+
/** 30 days. */
|
|
57
|
+
readonly maxMinutes: 43200;
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=sla.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.types.d.ts","sourceRoot":"","sources":["../../../../src/features/sla/domain/sla.types.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEpF,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,QAAQ,CAAC;IACd,OAAO,EAAE,QAAQ,CAAC;IAClB,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C,oDAAoD;IACpD,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACtC;AAED,uFAAuF;AACvF,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAAK,SAAS,CAAC;AAE1H;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC7D;AAED,eAAO,MAAM,UAAU;;IAErB,eAAe;;CAEP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.types.js","sourceRoot":"","sources":["../../../../src/features/sla/domain/sla.types.ts"],"names":[],"mappings":"AA8DA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU,EAAE,CAAC;IACb,eAAe;IACf,UAAU,EAAE,MAAM;CACV,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./domain/sla.types.js";
|
|
2
|
+
export * from "./domain/sla.resolver.js";
|
|
3
|
+
export * from "./domain/sla.evaluator.js";
|
|
4
|
+
export { SlaPolicyModel } from "./sla-policy.model.js";
|
|
5
|
+
export { SlaPolicyService, SlaError, type AccountSlaPolicy } from "./sla-policy.service.js";
|
|
6
|
+
export { createSlaRouter } from "./sla.routes.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/sla/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./domain/sla.types.js";
|
|
2
|
+
export * from "./domain/sla.resolver.js";
|
|
3
|
+
export * from "./domain/sla.evaluator.js";
|
|
4
|
+
export { SlaPolicyModel } from "./sla-policy.model.js";
|
|
5
|
+
export { SlaPolicyService, SlaError } from "./sla-policy.service.js";
|
|
6
|
+
export { createSlaRouter } from "./sla.routes.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/features/sla/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAyB,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SlaPolicyRecord } from "./domain/sla.types.js";
|
|
2
|
+
/** Persistence only: every query is scoped by tenant_id. No business rules here. */
|
|
3
|
+
export declare class SlaPolicyModel {
|
|
4
|
+
private db;
|
|
5
|
+
private tbl;
|
|
6
|
+
constructor(db: any, tbl: any);
|
|
7
|
+
listByTenant(tenantId: string): Promise<SlaPolicyRecord[]>;
|
|
8
|
+
findByAccount(tenantId: string, accountId: string): Promise<SlaPolicyRecord | null>;
|
|
9
|
+
upsert(input: {
|
|
10
|
+
tenantId: string;
|
|
11
|
+
accountId: string;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
ackMinutes: number;
|
|
14
|
+
resolveMinutes: number;
|
|
15
|
+
updatedBy: string | null;
|
|
16
|
+
}): Promise<SlaPolicyRecord>;
|
|
17
|
+
delete(tenantId: string, accountId: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=sla-policy.model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla-policy.model.d.ts","sourceRoot":"","sources":["../../../src/features/sla/sla-policy.model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,oFAAoF;AACpF,qBAAa,cAAc;IAEvB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,GAAG;gBADH,EAAE,EAAE,GAAG,EACP,GAAG,EAAE,GAAG;IAGZ,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAI1D,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IASnF,MAAM,CAAC,KAAK,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,GAAG,OAAO,CAAC,eAAe,CAAC;IAkBtB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKjE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm";
|
|
2
|
+
/** Persistence only: every query is scoped by tenant_id. No business rules here. */
|
|
3
|
+
export class SlaPolicyModel {
|
|
4
|
+
db;
|
|
5
|
+
tbl;
|
|
6
|
+
constructor(db, tbl) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.tbl = tbl;
|
|
9
|
+
}
|
|
10
|
+
async listByTenant(tenantId) {
|
|
11
|
+
return this.db.select().from(this.tbl).where(eq(this.tbl.tenantId, tenantId));
|
|
12
|
+
}
|
|
13
|
+
async findByAccount(tenantId, accountId) {
|
|
14
|
+
const [row] = await this.db
|
|
15
|
+
.select()
|
|
16
|
+
.from(this.tbl)
|
|
17
|
+
.where(and(eq(this.tbl.tenantId, tenantId), eq(this.tbl.accountId, accountId)))
|
|
18
|
+
.limit(1);
|
|
19
|
+
return row ?? null;
|
|
20
|
+
}
|
|
21
|
+
async upsert(input) {
|
|
22
|
+
const [row] = await this.db
|
|
23
|
+
.insert(this.tbl)
|
|
24
|
+
.values(input)
|
|
25
|
+
.onConflictDoUpdate({
|
|
26
|
+
target: [this.tbl.tenantId, this.tbl.accountId],
|
|
27
|
+
set: {
|
|
28
|
+
enabled: input.enabled,
|
|
29
|
+
ackMinutes: input.ackMinutes,
|
|
30
|
+
resolveMinutes: input.resolveMinutes,
|
|
31
|
+
updatedBy: input.updatedBy,
|
|
32
|
+
updatedAt: new Date(),
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
.returning();
|
|
36
|
+
return row;
|
|
37
|
+
}
|
|
38
|
+
async delete(tenantId, accountId) {
|
|
39
|
+
await this.db
|
|
40
|
+
.delete(this.tbl)
|
|
41
|
+
.where(and(eq(this.tbl.tenantId, tenantId), eq(this.tbl.accountId, accountId)));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=sla-policy.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla-policy.model.js","sourceRoot":"","sources":["../../../src/features/sla/sla-policy.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAGtC,oFAAoF;AACpF,MAAM,OAAO,cAAc;IAEf;IACA;IAFV,YACU,EAAO,EACP,GAAQ;QADR,OAAE,GAAF,EAAE,CAAK;QACP,QAAG,GAAH,GAAG,CAAK;IACf,CAAC;IAEJ,KAAK,CAAC,YAAY,CAAC,QAAgB;QACjC,OAAO,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,SAAiB;QACrD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE;aACxB,MAAM,EAAE;aACR,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;aACd,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;aAC9E,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,OAAO,GAAG,IAAI,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAOZ;QACC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE;aACxB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAChB,MAAM,CAAC,KAAK,CAAC;aACb,kBAAkB,CAAC;YAClB,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YAC/C,GAAG,EAAE;gBACH,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB;SACF,CAAC;aACD,SAAS,EAAE,CAAC;QACf,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,SAAiB;QAC9C,MAAM,IAAI,CAAC,EAAE;aACV,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAChB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ConnectorService } from "../../services/connector.service.js";
|
|
2
|
+
import type { SlaPolicyModel } from "./sla-policy.model.js";
|
|
3
|
+
import type { SlaPolicy, SlaPolicyProvider, SlaPolicyResolver } from "./domain/sla.types.js";
|
|
4
|
+
export declare class SlaError extends Error {
|
|
5
|
+
readonly status: 400 | 404;
|
|
6
|
+
readonly details?: unknown;
|
|
7
|
+
constructor(status: 400 | 404, message: string, details?: unknown);
|
|
8
|
+
}
|
|
9
|
+
export interface AccountSlaPolicy extends SlaPolicy {
|
|
10
|
+
accountId: string;
|
|
11
|
+
updatedBy: string | null;
|
|
12
|
+
updatedAt: Date | string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare class SlaPolicyService implements SlaPolicyProvider {
|
|
15
|
+
private model;
|
|
16
|
+
private connectors;
|
|
17
|
+
constructor(model: SlaPolicyModel, connectors: ConnectorService);
|
|
18
|
+
/** The account must be one of this tenant's connectors; prevents writing policies for foreign accounts. */
|
|
19
|
+
private assertConnector;
|
|
20
|
+
private toAccountPolicy;
|
|
21
|
+
list(tenantId: string): Promise<AccountSlaPolicy[]>;
|
|
22
|
+
get(tenantId: string, accountId: string): Promise<AccountSlaPolicy>;
|
|
23
|
+
upsert(tenantId: string, accountId: string, input: unknown, actor: string | null): Promise<AccountSlaPolicy>;
|
|
24
|
+
/** Removes the override so the account falls back to defaults. */
|
|
25
|
+
reset(tenantId: string, accountId: string): Promise<AccountSlaPolicy>;
|
|
26
|
+
/** One query per request; used by incident dashboards, analytics and detail. */
|
|
27
|
+
loadResolver(tenantId?: string): Promise<SlaPolicyResolver>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=sla-policy.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla-policy.service.d.ts","sourceRoot":"","sources":["../../../src/features/sla/sla-policy.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAmB,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE9G,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG;IAE1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;gBAFjB,MAAM,EAAE,GAAG,GAAG,GAAG,EAC1B,OAAO,EAAE,MAAM,EACN,OAAO,CAAC,EAAE,OAAO;CAI7B;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,qBAAa,gBAAiB,YAAW,iBAAiB;IAEtD,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,UAAU;gBADV,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,gBAAgB;IAGtC,2GAA2G;YAC7F,eAAe;IAK7B,OAAO,CAAC,eAAe;IAKjB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAKnD,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKnE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAWlH,kEAAkE;IAC5D,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAM3E,gFAAgF;IAC1E,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;CASlE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { SlaPolicyInputSchema } from "./sla.schema.js";
|
|
2
|
+
import { createSlaResolver, defaultSlaPolicy, policyFromRecord } from "./domain/sla.resolver.js";
|
|
3
|
+
export class SlaError extends Error {
|
|
4
|
+
status;
|
|
5
|
+
details;
|
|
6
|
+
constructor(status, message, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.details = details;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class SlaPolicyService {
|
|
13
|
+
model;
|
|
14
|
+
connectors;
|
|
15
|
+
constructor(model, connectors) {
|
|
16
|
+
this.model = model;
|
|
17
|
+
this.connectors = connectors;
|
|
18
|
+
}
|
|
19
|
+
/** The account must be one of this tenant's connectors; prevents writing policies for foreign accounts. */
|
|
20
|
+
async assertConnector(tenantId, accountId) {
|
|
21
|
+
const connector = await this.connectors.findByAccountId(tenantId, accountId);
|
|
22
|
+
if (!connector)
|
|
23
|
+
throw new SlaError(404, "Connector not found");
|
|
24
|
+
}
|
|
25
|
+
toAccountPolicy(accountId, record) {
|
|
26
|
+
if (!record)
|
|
27
|
+
return { ...defaultSlaPolicy(), accountId, updatedBy: null, updatedAt: null };
|
|
28
|
+
return { ...policyFromRecord(record), accountId, updatedBy: record.updatedBy, updatedAt: record.updatedAt };
|
|
29
|
+
}
|
|
30
|
+
async list(tenantId) {
|
|
31
|
+
const records = await this.model.listByTenant(tenantId);
|
|
32
|
+
return records.map((r) => this.toAccountPolicy(r.accountId, r));
|
|
33
|
+
}
|
|
34
|
+
async get(tenantId, accountId) {
|
|
35
|
+
await this.assertConnector(tenantId, accountId);
|
|
36
|
+
return this.toAccountPolicy(accountId, await this.model.findByAccount(tenantId, accountId));
|
|
37
|
+
}
|
|
38
|
+
async upsert(tenantId, accountId, input, actor) {
|
|
39
|
+
const parsed = SlaPolicyInputSchema.safeParse(input);
|
|
40
|
+
if (!parsed.success) {
|
|
41
|
+
throw new SlaError(400, parsed.error.issues[0]?.message ?? "Invalid SLA policy", parsed.error.issues);
|
|
42
|
+
}
|
|
43
|
+
await this.assertConnector(tenantId, accountId);
|
|
44
|
+
const data = parsed.data;
|
|
45
|
+
const record = await this.model.upsert({ tenantId, accountId, ...data, updatedBy: actor });
|
|
46
|
+
return this.toAccountPolicy(accountId, record);
|
|
47
|
+
}
|
|
48
|
+
/** Removes the override so the account falls back to defaults. */
|
|
49
|
+
async reset(tenantId, accountId) {
|
|
50
|
+
await this.assertConnector(tenantId, accountId);
|
|
51
|
+
await this.model.delete(tenantId, accountId);
|
|
52
|
+
return this.toAccountPolicy(accountId, null);
|
|
53
|
+
}
|
|
54
|
+
/** One query per request; used by incident dashboards, analytics and detail. */
|
|
55
|
+
async loadResolver(tenantId) {
|
|
56
|
+
if (!tenantId)
|
|
57
|
+
return createSlaResolver([]);
|
|
58
|
+
try {
|
|
59
|
+
return createSlaResolver(await this.model.listByTenant(tenantId));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Never break incident views because the policy table is unavailable; fall back to defaults.
|
|
63
|
+
return createSlaResolver([]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=sla-policy.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla-policy.service.js","sourceRoot":"","sources":["../../../src/features/sla/sla-policy.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import { type SlaPolicyService } from "./sla-policy.service.js";
|
|
3
|
+
export declare function createSlaController(service: SlaPolicyService): {
|
|
4
|
+
list(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
5
|
+
get(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
6
|
+
upsert(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
reset(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=sla.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.controller.d.ts","sourceRoot":"","sources":["../../../src/features/sla/sla.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAY,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAe1E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB;cAEzC,OAAO,OAAO,QAAQ;aAOvB,OAAO,OAAO,QAAQ;gBAOnB,OAAO,OAAO,QAAQ;eAQvB,OAAO,OAAO,QAAQ;EAQ1C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolveTenantId } from "../../tenancy.js";
|
|
2
|
+
import { SlaError } from "./sla-policy.service.js";
|
|
3
|
+
function actorLabel(req) {
|
|
4
|
+
const u = req.user;
|
|
5
|
+
if (!u)
|
|
6
|
+
return null;
|
|
7
|
+
return u.email || u.full_name || u.fullName || u.name || u.id || null;
|
|
8
|
+
}
|
|
9
|
+
function fail(res, e) {
|
|
10
|
+
if (e instanceof SlaError) {
|
|
11
|
+
return res.status(e.status).json({ ok: false, error: e.message, details: e.details });
|
|
12
|
+
}
|
|
13
|
+
return res.status(500).json({ ok: false, error: e?.message ?? "Internal error" });
|
|
14
|
+
}
|
|
15
|
+
export function createSlaController(service) {
|
|
16
|
+
return {
|
|
17
|
+
async list(req, res) {
|
|
18
|
+
try {
|
|
19
|
+
return res.json({ ok: true, policies: await service.list(resolveTenantId(req)) });
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return fail(res, e);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async get(req, res) {
|
|
26
|
+
try {
|
|
27
|
+
return res.json({ ok: true, policy: await service.get(resolveTenantId(req), req.params.accountId) });
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return fail(res, e);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
async upsert(req, res) {
|
|
34
|
+
try {
|
|
35
|
+
const policy = await service.upsert(resolveTenantId(req), req.params.accountId, req.body, actorLabel(req));
|
|
36
|
+
return res.json({ ok: true, policy });
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
return fail(res, e);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
async reset(req, res) {
|
|
43
|
+
try {
|
|
44
|
+
return res.json({ ok: true, policy: await service.reset(resolveTenantId(req), req.params.accountId) });
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return fail(res, e);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=sla.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.controller.js","sourceRoot":"","sources":["../../../src/features/sla/sla.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAyB,MAAM,yBAAyB,CAAC;AAE1E,SAAS,UAAU,CAAC,GAAY;IAC9B,MAAM,CAAC,GAAI,GAAW,CAAC,IAAI,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC;AACxE,CAAC;AAED,SAAS,IAAI,CAAC,GAAa,EAAE,CAAM;IACjC,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,IAAI,gBAAgB,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAyB;IAC3D,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAY,EAAE,GAAa;YACpC,IAAI,CAAC;gBACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;YACpF,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAY,EAAE,GAAa;YACnC,IAAI,CAAC;gBACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACvG,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAY,EAAE,GAAa;YACtC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3G,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,GAAY,EAAE,GAAa;YACrC,IAAI,CAAC;gBACH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACzG,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.routes.d.ts","sourceRoot":"","sources":["../../../src/features/sla/sla.routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAUjE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { createSlaController } from "./sla.controller.js";
|
|
3
|
+
export function createSlaRouter(service) {
|
|
4
|
+
const router = Router();
|
|
5
|
+
const ctrl = createSlaController(service);
|
|
6
|
+
router.get("/policies", ctrl.list.bind(ctrl));
|
|
7
|
+
router.get("/policies/:accountId", ctrl.get.bind(ctrl));
|
|
8
|
+
router.put("/policies/:accountId", ctrl.upsert.bind(ctrl));
|
|
9
|
+
router.delete("/policies/:accountId", ctrl.reset.bind(ctrl));
|
|
10
|
+
return router;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=sla.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.routes.js","sourceRoot":"","sources":["../../../src/features/sla/sla.routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,MAAM,CAAC,sBAAsB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAE7D,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const SlaPolicyInputSchema: z.ZodObject<{
|
|
3
|
+
enabled: z.ZodBoolean;
|
|
4
|
+
ackMinutes: z.ZodNumber;
|
|
5
|
+
resolveMinutes: z.ZodNumber;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export type SlaPolicyInput = z.infer<typeof SlaPolicyInputSchema>;
|
|
8
|
+
//# sourceMappingURL=sla.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.schema.d.ts","sourceRoot":"","sources":["../../../src/features/sla/sla.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,eAAO,MAAM,oBAAoB;;;;iBAS7B,CAAC;AAEL,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SLA_LIMITS } from "./domain/sla.types.js";
|
|
3
|
+
const minutes = z
|
|
4
|
+
.number()
|
|
5
|
+
.int("Must be a whole number of minutes")
|
|
6
|
+
.min(SLA_LIMITS.minMinutes, `Must be at least ${SLA_LIMITS.minMinutes} minute`)
|
|
7
|
+
.max(SLA_LIMITS.maxMinutes, `Must be at most ${SLA_LIMITS.maxMinutes} minutes (30 days)`);
|
|
8
|
+
export const SlaPolicyInputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
enabled: z.boolean(),
|
|
11
|
+
ackMinutes: minutes,
|
|
12
|
+
resolveMinutes: minutes,
|
|
13
|
+
})
|
|
14
|
+
.refine((v) => v.resolveMinutes >= v.ackMinutes, {
|
|
15
|
+
message: "Resolve limit must be greater than or equal to the acknowledge limit",
|
|
16
|
+
path: ["resolveMinutes"],
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=sla.schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sla.schema.js","sourceRoot":"","sources":["../../../src/features/sla/sla.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,OAAO,GAAG,CAAC;KACd,MAAM,EAAE;KACR,GAAG,CAAC,mCAAmC,CAAC;KACxC,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,oBAAoB,UAAU,CAAC,UAAU,SAAS,CAAC;KAC9E,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,mBAAmB,UAAU,CAAC,UAAU,oBAAoB,CAAC,CAAC;AAE5F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB,UAAU,EAAE,OAAO;IACnB,cAAc,EAAE,OAAO;CACxB,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,UAAU,EAAE;IAC/C,OAAO,EAAE,sEAAsE;IAC/E,IAAI,EAAE,CAAC,gBAAgB,CAAC;CACzB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/features/sources/cloudtrail/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/features/sources/cloudtrail/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AA6F/E,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,aAAa,EAC5B,gBAAgB,CAAC,EAAE,gBAAgB;IAGjC;;;OAGG;kBACiB,OAAO,OAAO,QAAQ;IAkC1C;;;;;;;;;OASG;0BACyB,OAAO,OAAO,QAAQ;IA6LlD;;;OAGG;+BAC8B,OAAO,OAAO,QAAQ;IAiCvD;;;OAGG;gCAC+B,OAAO,OAAO,QAAQ;IAoExD;;;;OAIG;0BACyB,OAAO,OAAO,QAAQ;EAmDrD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildCloudTrailCentralStackTemplate, ALL_AWS_ACTIVE_REGIONS } from "./template.js";
|
|
2
2
|
import { resolveTenantId } from "../../../tenancy.js";
|
|
3
|
+
import { reconcileCloudTrailDeployment } from "./reconciler.js";
|
|
3
4
|
function normalizeSourceType(value) {
|
|
4
5
|
const normalized = String(value || "")
|
|
5
6
|
.toLowerCase()
|
|
@@ -80,12 +81,16 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
80
81
|
*/
|
|
81
82
|
async getSetup(req, res) {
|
|
82
83
|
try {
|
|
83
|
-
|
|
84
|
+
let source = await sourceService.getById(req.params.id);
|
|
84
85
|
if (!source)
|
|
85
86
|
return res.status(404).json({ ok: false, error: "Source not found" });
|
|
86
87
|
if (!isCloudTrailType(source.type)) {
|
|
87
88
|
return res.status(400).json({ ok: false, error: "Source type must be cloudtrail" });
|
|
88
89
|
}
|
|
90
|
+
const tenantId = resolveTenantId(req);
|
|
91
|
+
if (String(source.awsDeploymentStatus || "").toLowerCase() === "pending") {
|
|
92
|
+
source = await reconcileCloudTrailDeployment(source, sourceService, connectorService, tenantId);
|
|
93
|
+
}
|
|
89
94
|
return res.json({
|
|
90
95
|
ok: true,
|
|
91
96
|
source: {
|
|
@@ -119,13 +124,16 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
119
124
|
*/
|
|
120
125
|
async getConsoleDeploy(req, res) {
|
|
121
126
|
try {
|
|
122
|
-
|
|
127
|
+
let source = await sourceService.getById(req.params.id);
|
|
123
128
|
if (!source)
|
|
124
129
|
return res.status(404).json({ ok: false, error: "Source not found" });
|
|
125
130
|
if (!isCloudTrailType(source.type)) {
|
|
126
131
|
return res.status(400).json({ ok: false, error: "Source type must be cloudtrail" });
|
|
127
132
|
}
|
|
128
133
|
const tenantId = resolveTenantId(req);
|
|
134
|
+
if (String(source.awsDeploymentStatus || "").toLowerCase() === "pending") {
|
|
135
|
+
source = await reconcileCloudTrailDeployment(source, sourceService, connectorService, tenantId);
|
|
136
|
+
}
|
|
129
137
|
const region = String(req.query.region || source.awsRegion || "ap-south-1").trim() || "ap-south-1";
|
|
130
138
|
const stackName = sanitizeAwsName(req.query.stackName || source.awsStackName, `meyi-cloudtrail-${source.id.slice(0, 8)}`, 128);
|
|
131
139
|
// createNewTrail: query param > stored value > default true
|
|
@@ -218,7 +226,7 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
218
226
|
webhookUrl = `${urlObj.toString().replace(/\/$/, "")}${webhookPath}`;
|
|
219
227
|
}
|
|
220
228
|
const deploymentStatusPath = `/api/v1/insight/sources/${source.id}/aws/cloudtrail/deployment-status`;
|
|
221
|
-
const deploymentStatusUrl = `${baseUrl}${deploymentStatusPath}
|
|
229
|
+
const deploymentStatusUrl = `${baseUrl}${deploymentStatusPath}`;
|
|
222
230
|
const templatePath = `/api/v1/insight/sources/${source.id}/aws/cloudtrail/central-stack-template.yml`;
|
|
223
231
|
const templateUrl = `${baseUrl}${templatePath}?token=${encodeURIComponent(source.webhookToken)}`;
|
|
224
232
|
const templateDownloadUrl = templateUrl;
|
|
@@ -324,7 +332,16 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
324
332
|
if (!isCloudTrailType(source.type)) {
|
|
325
333
|
return res.status(400).json({ ok: false, error: "Source type must be cloudtrail" });
|
|
326
334
|
}
|
|
327
|
-
|
|
335
|
+
let token = String(req.query.token || req.body?.token || "").trim();
|
|
336
|
+
if (!token && req.headers.authorization) {
|
|
337
|
+
const auth = String(req.headers.authorization).trim();
|
|
338
|
+
if (auth.toLowerCase().startsWith("bearer ")) {
|
|
339
|
+
token = auth.slice(7).trim();
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
token = auth;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
328
345
|
if (!token || token !== String(source.webhookToken || "")) {
|
|
329
346
|
return res.status(403).json({ ok: false, error: "Invalid deployment status token" });
|
|
330
347
|
}
|
|
@@ -332,27 +349,31 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
332
349
|
if (req.body?.source === "aws.cloudformation" && req.body?.detail) {
|
|
333
350
|
const detail = req.body.detail;
|
|
334
351
|
const statusDetails = detail["status-details"] || {};
|
|
352
|
+
const statusVal = statusDetails["detailed-status"] || statusDetails["status"] || detail["status"];
|
|
335
353
|
parsedBody = {
|
|
336
|
-
operationStatus:
|
|
337
|
-
reason: statusDetails["status-reason"],
|
|
338
|
-
stackId: detail["stack-
|
|
339
|
-
stackName: (detail["stack-
|
|
354
|
+
operationStatus: statusVal,
|
|
355
|
+
reason: statusDetails["status-reason"] || detail["status-reason"],
|
|
356
|
+
stackId: detail["stack-id"] || detail["stack-set-arn"] || req.body.resources?.[0],
|
|
357
|
+
stackName: (detail["stack-id"] || detail["stack-set-arn"] || req.body.resources?.[0]) ? (detail["stack-id"] || detail["stack-set-arn"] || req.body.resources?.[0]).split("/")[1] : undefined
|
|
340
358
|
};
|
|
341
359
|
}
|
|
342
|
-
// Map CloudFormation StackSet status to internal status
|
|
360
|
+
// Map CloudFormation Stack / StackSet / StackInstance status to internal status
|
|
343
361
|
const rawStatus = String(parsedBody?.operationStatus || parsedBody?.status || "").trim().toLowerCase();
|
|
344
362
|
let requestedStatus = rawStatus;
|
|
345
|
-
if (rawStatus === "succeeded" || rawStatus === "create_complete" || rawStatus === "update_complete" || rawStatus === "deployed")
|
|
363
|
+
if (rawStatus === "succeeded" || rawStatus === "create_complete" || rawStatus === "update_complete" || rawStatus === "deployed" || rawStatus === "current") {
|
|
346
364
|
requestedStatus = "deployed";
|
|
347
|
-
|
|
365
|
+
}
|
|
366
|
+
else if (rawStatus.includes("failed") || rawStatus.includes("rollback")) {
|
|
348
367
|
requestedStatus = "failed";
|
|
349
|
-
|
|
368
|
+
}
|
|
369
|
+
else if (rawStatus === "delete_complete" || rawStatus === "deleted") {
|
|
350
370
|
requestedStatus = "deleted";
|
|
371
|
+
}
|
|
351
372
|
const allowed = new Set(["not_started", "pending", "deployed", "failed", "deleted"]);
|
|
352
373
|
const status = allowed.has(requestedStatus) ? requestedStatus : "pending";
|
|
353
374
|
const updated = await sourceService.updateCloudTrailDeployment(source.id, {
|
|
354
375
|
awsDeploymentStatus: status,
|
|
355
|
-
awsDeploymentStatusReason: String(parsedBody?.reason || "").trim() || null,
|
|
376
|
+
awsDeploymentStatusReason: String(parsedBody?.reason || "").trim() || (status === "deployed" ? "Deployment completed in AWS" : null),
|
|
356
377
|
awsStackId: String(parsedBody?.stackId || "").trim() || null,
|
|
357
378
|
awsStackName: String(parsedBody?.stackName || source.awsStackName || "").trim() || null,
|
|
358
379
|
});
|
|
@@ -387,7 +408,7 @@ export function createCloudTrailSourceController(sourceService, connectorService
|
|
|
387
408
|
}
|
|
388
409
|
const baseUrl = buildPublicBaseUrl(req);
|
|
389
410
|
let webhookUrl = `${baseUrl}/api/v1/insight/webhook/${source.webhookToken}`;
|
|
390
|
-
const deploymentStatusUrl = `${baseUrl}/api/v1/insight/sources/${source.id}/aws/cloudtrail/deployment-status
|
|
411
|
+
const deploymentStatusUrl = `${baseUrl}/api/v1/insight/sources/${source.id}/aws/cloudtrail/deployment-status`;
|
|
391
412
|
// Read from query params first (if provided by frontend during download), then fallback to database
|
|
392
413
|
const reqCreateNewTrail = req.query.createNewTrail !== undefined
|
|
393
414
|
? String(req.query.createNewTrail).toLowerCase() !== "false"
|