@agentlensai/server 0.9.0 → 0.11.0
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/cloud/auth/api-key-middleware.d.ts +66 -0
- package/dist/cloud/auth/api-key-middleware.d.ts.map +1 -0
- package/dist/cloud/auth/api-key-middleware.js +147 -0
- package/dist/cloud/auth/api-key-middleware.js.map +1 -0
- package/dist/cloud/auth/api-keys.d.ts +90 -0
- package/dist/cloud/auth/api-keys.d.ts.map +1 -0
- package/dist/cloud/auth/api-keys.js +162 -0
- package/dist/cloud/auth/api-keys.js.map +1 -0
- package/dist/cloud/auth/audit-log.d.ts +66 -0
- package/dist/cloud/auth/audit-log.d.ts.map +1 -0
- package/dist/cloud/auth/audit-log.js +92 -0
- package/dist/cloud/auth/audit-log.js.map +1 -0
- package/dist/cloud/auth/auth-service.d.ts +77 -0
- package/dist/cloud/auth/auth-service.d.ts.map +1 -0
- package/dist/cloud/auth/auth-service.js +229 -0
- package/dist/cloud/auth/auth-service.js.map +1 -0
- package/dist/cloud/auth/brute-force.d.ts +36 -0
- package/dist/cloud/auth/brute-force.d.ts.map +1 -0
- package/dist/cloud/auth/brute-force.js +67 -0
- package/dist/cloud/auth/brute-force.js.map +1 -0
- package/dist/cloud/auth/index.d.ts +11 -0
- package/dist/cloud/auth/index.d.ts.map +1 -0
- package/dist/cloud/auth/index.js +11 -0
- package/dist/cloud/auth/index.js.map +1 -0
- package/dist/cloud/auth/jwt.d.ts +34 -0
- package/dist/cloud/auth/jwt.d.ts.map +1 -0
- package/dist/cloud/auth/jwt.js +68 -0
- package/dist/cloud/auth/jwt.js.map +1 -0
- package/dist/cloud/auth/oauth.d.ts +37 -0
- package/dist/cloud/auth/oauth.d.ts.map +1 -0
- package/dist/cloud/auth/oauth.js +120 -0
- package/dist/cloud/auth/oauth.js.map +1 -0
- package/dist/cloud/auth/passwords.d.ts +25 -0
- package/dist/cloud/auth/passwords.d.ts.map +1 -0
- package/dist/cloud/auth/passwords.js +50 -0
- package/dist/cloud/auth/passwords.js.map +1 -0
- package/dist/cloud/auth/rbac.d.ts +51 -0
- package/dist/cloud/auth/rbac.d.ts.map +1 -0
- package/dist/cloud/auth/rbac.js +89 -0
- package/dist/cloud/auth/rbac.js.map +1 -0
- package/dist/cloud/auth/tokens.d.ts +18 -0
- package/dist/cloud/auth/tokens.d.ts.map +1 -0
- package/dist/cloud/auth/tokens.js +29 -0
- package/dist/cloud/auth/tokens.js.map +1 -0
- package/dist/cloud/billing/billing-service.d.ts +44 -0
- package/dist/cloud/billing/billing-service.d.ts.map +1 -0
- package/dist/cloud/billing/billing-service.js +153 -0
- package/dist/cloud/billing/billing-service.js.map +1 -0
- package/dist/cloud/billing/index.d.ts +11 -0
- package/dist/cloud/billing/index.d.ts.map +1 -0
- package/dist/cloud/billing/index.js +11 -0
- package/dist/cloud/billing/index.js.map +1 -0
- package/dist/cloud/billing/invoice-service.d.ts +57 -0
- package/dist/cloud/billing/invoice-service.d.ts.map +1 -0
- package/dist/cloud/billing/invoice-service.js +123 -0
- package/dist/cloud/billing/invoice-service.js.map +1 -0
- package/dist/cloud/billing/plan-management.d.ts +46 -0
- package/dist/cloud/billing/plan-management.d.ts.map +1 -0
- package/dist/cloud/billing/plan-management.js +157 -0
- package/dist/cloud/billing/plan-management.js.map +1 -0
- package/dist/cloud/billing/quota-enforcement.d.ts +53 -0
- package/dist/cloud/billing/quota-enforcement.d.ts.map +1 -0
- package/dist/cloud/billing/quota-enforcement.js +143 -0
- package/dist/cloud/billing/quota-enforcement.js.map +1 -0
- package/dist/cloud/billing/stripe-client.d.ts +142 -0
- package/dist/cloud/billing/stripe-client.d.ts.map +1 -0
- package/dist/cloud/billing/stripe-client.js +169 -0
- package/dist/cloud/billing/stripe-client.js.map +1 -0
- package/dist/cloud/billing/trial-service.d.ts +47 -0
- package/dist/cloud/billing/trial-service.d.ts.map +1 -0
- package/dist/cloud/billing/trial-service.js +104 -0
- package/dist/cloud/billing/trial-service.js.map +1 -0
- package/dist/cloud/billing/usage-metering.d.ts +83 -0
- package/dist/cloud/billing/usage-metering.d.ts.map +1 -0
- package/dist/cloud/billing/usage-metering.js +174 -0
- package/dist/cloud/billing/usage-metering.js.map +1 -0
- package/dist/cloud/ingestion/backpressure.d.ts +107 -0
- package/dist/cloud/ingestion/backpressure.d.ts.map +1 -0
- package/dist/cloud/ingestion/backpressure.js +134 -0
- package/dist/cloud/ingestion/backpressure.js.map +1 -0
- package/dist/cloud/ingestion/batch-writer.d.ts +115 -0
- package/dist/cloud/ingestion/batch-writer.d.ts.map +1 -0
- package/dist/cloud/ingestion/batch-writer.js +319 -0
- package/dist/cloud/ingestion/batch-writer.js.map +1 -0
- package/dist/cloud/ingestion/dlq-manager.d.ts +116 -0
- package/dist/cloud/ingestion/dlq-manager.d.ts.map +1 -0
- package/dist/cloud/ingestion/dlq-manager.js +244 -0
- package/dist/cloud/ingestion/dlq-manager.js.map +1 -0
- package/dist/cloud/ingestion/event-queue.d.ts +105 -0
- package/dist/cloud/ingestion/event-queue.d.ts.map +1 -0
- package/dist/cloud/ingestion/event-queue.js +185 -0
- package/dist/cloud/ingestion/event-queue.js.map +1 -0
- package/dist/cloud/ingestion/gateway.d.ts +68 -0
- package/dist/cloud/ingestion/gateway.d.ts.map +1 -0
- package/dist/cloud/ingestion/gateway.js +198 -0
- package/dist/cloud/ingestion/gateway.js.map +1 -0
- package/dist/cloud/ingestion/index.d.ts +7 -0
- package/dist/cloud/ingestion/index.d.ts.map +1 -0
- package/dist/cloud/ingestion/index.js +7 -0
- package/dist/cloud/ingestion/index.js.map +1 -0
- package/dist/cloud/ingestion/rate-limiter.d.ts +73 -0
- package/dist/cloud/ingestion/rate-limiter.d.ts.map +1 -0
- package/dist/cloud/ingestion/rate-limiter.js +153 -0
- package/dist/cloud/ingestion/rate-limiter.js.map +1 -0
- package/dist/cloud/migrate.d.ts +45 -0
- package/dist/cloud/migrate.d.ts.map +1 -0
- package/dist/cloud/migrate.js +147 -0
- package/dist/cloud/migrate.js.map +1 -0
- package/dist/cloud/migration/export-import.d.ts +56 -0
- package/dist/cloud/migration/export-import.d.ts.map +1 -0
- package/dist/cloud/migration/export-import.js +289 -0
- package/dist/cloud/migration/export-import.js.map +1 -0
- package/dist/cloud/migration/index.d.ts +5 -0
- package/dist/cloud/migration/index.d.ts.map +1 -0
- package/dist/cloud/migration/index.js +5 -0
- package/dist/cloud/migration/index.js.map +1 -0
- package/dist/cloud/org-service.d.ts +68 -0
- package/dist/cloud/org-service.d.ts.map +1 -0
- package/dist/cloud/org-service.js +169 -0
- package/dist/cloud/org-service.js.map +1 -0
- package/dist/cloud/partition-maintenance.d.ts +29 -0
- package/dist/cloud/partition-maintenance.d.ts.map +1 -0
- package/dist/cloud/partition-maintenance.js +96 -0
- package/dist/cloud/partition-maintenance.js.map +1 -0
- package/dist/cloud/retention/index.d.ts +7 -0
- package/dist/cloud/retention/index.d.ts.map +1 -0
- package/dist/cloud/retention/index.js +7 -0
- package/dist/cloud/retention/index.js.map +1 -0
- package/dist/cloud/retention/partition-management.d.ts +61 -0
- package/dist/cloud/retention/partition-management.d.ts.map +1 -0
- package/dist/cloud/retention/partition-management.js +167 -0
- package/dist/cloud/retention/partition-management.js.map +1 -0
- package/dist/cloud/retention/retention-job.d.ts +70 -0
- package/dist/cloud/retention/retention-job.d.ts.map +1 -0
- package/dist/cloud/retention/retention-job.js +160 -0
- package/dist/cloud/retention/retention-job.js.map +1 -0
- package/dist/cloud/retention/retention-policy.d.ts +27 -0
- package/dist/cloud/retention/retention-policy.d.ts.map +1 -0
- package/dist/cloud/retention/retention-policy.js +36 -0
- package/dist/cloud/retention/retention-policy.js.map +1 -0
- package/dist/cloud/routes/api-key-routes.d.ts +38 -0
- package/dist/cloud/routes/api-key-routes.d.ts.map +1 -0
- package/dist/cloud/routes/api-key-routes.js +84 -0
- package/dist/cloud/routes/api-key-routes.js.map +1 -0
- package/dist/cloud/routes/audit-routes.d.ts +36 -0
- package/dist/cloud/routes/audit-routes.d.ts.map +1 -0
- package/dist/cloud/routes/audit-routes.js +47 -0
- package/dist/cloud/routes/audit-routes.js.map +1 -0
- package/dist/cloud/routes/billing-routes.d.ts +51 -0
- package/dist/cloud/routes/billing-routes.d.ts.map +1 -0
- package/dist/cloud/routes/billing-routes.js +114 -0
- package/dist/cloud/routes/billing-routes.js.map +1 -0
- package/dist/cloud/routes/onboarding-routes.d.ts +34 -0
- package/dist/cloud/routes/onboarding-routes.d.ts.map +1 -0
- package/dist/cloud/routes/onboarding-routes.js +58 -0
- package/dist/cloud/routes/onboarding-routes.js.map +1 -0
- package/dist/cloud/routes/org-routes.d.ts +80 -0
- package/dist/cloud/routes/org-routes.d.ts.map +1 -0
- package/dist/cloud/routes/org-routes.js +153 -0
- package/dist/cloud/routes/org-routes.js.map +1 -0
- package/dist/cloud/routes/usage-routes.d.ts +18 -0
- package/dist/cloud/routes/usage-routes.d.ts.map +1 -0
- package/dist/cloud/routes/usage-routes.js +66 -0
- package/dist/cloud/routes/usage-routes.js.map +1 -0
- package/dist/cloud/storage/adapter.d.ts +102 -0
- package/dist/cloud/storage/adapter.d.ts.map +1 -0
- package/dist/cloud/storage/adapter.js +21 -0
- package/dist/cloud/storage/adapter.js.map +1 -0
- package/dist/cloud/storage/index.d.ts +8 -0
- package/dist/cloud/storage/index.d.ts.map +1 -0
- package/dist/cloud/storage/index.js +7 -0
- package/dist/cloud/storage/index.js.map +1 -0
- package/dist/cloud/storage/postgres-adapter.d.ts +34 -0
- package/dist/cloud/storage/postgres-adapter.d.ts.map +1 -0
- package/dist/cloud/storage/postgres-adapter.js +544 -0
- package/dist/cloud/storage/postgres-adapter.js.map +1 -0
- package/dist/cloud/storage/sqlite-adapter.d.ts +29 -0
- package/dist/cloud/storage/sqlite-adapter.d.ts.map +1 -0
- package/dist/cloud/storage/sqlite-adapter.js +176 -0
- package/dist/cloud/storage/sqlite-adapter.js.map +1 -0
- package/dist/cloud/tenant-pool.d.ts +49 -0
- package/dist/cloud/tenant-pool.d.ts.map +1 -0
- package/dist/cloud/tenant-pool.js +61 -0
- package/dist/cloud/tenant-pool.js.map +1 -0
- package/dist/db/capability-store.d.ts +4 -0
- package/dist/db/capability-store.d.ts.map +1 -1
- package/dist/db/capability-store.js +20 -0
- package/dist/db/capability-store.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/audit.js +3 -3
- package/dist/routes/audit.js.map +1 -1
- package/dist/routes/capabilities-top.d.ts +15 -0
- package/dist/routes/capabilities-top.d.ts.map +1 -0
- package/dist/routes/capabilities-top.js +77 -0
- package/dist/routes/capabilities-top.js.map +1 -0
- package/dist/routes/community.d.ts.map +1 -1
- package/dist/routes/community.js +85 -3
- package/dist/routes/community.js.map +1 -1
- package/dist/routes/delegations-top.d.ts +12 -0
- package/dist/routes/delegations-top.d.ts.map +1 -0
- package/dist/routes/delegations-top.js +43 -0
- package/dist/routes/delegations-top.js.map +1 -0
- package/dist/services/community-service.d.ts +6 -0
- package/dist/services/community-service.d.ts.map +1 -1
- package/dist/services/community-service.js +31 -0
- package/dist/services/community-service.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Retention Purge Job (S-8.1)
|
|
3
|
+
*
|
|
4
|
+
* Daily cron (03:00 UTC) that purges events beyond each org's retention window.
|
|
5
|
+
* Primary mechanism: DROP entire expired monthly partitions.
|
|
6
|
+
* Partial DELETE for boundary partitions.
|
|
7
|
+
*
|
|
8
|
+
* Also handles: warning notifications before deletion, export-before-delete prompting.
|
|
9
|
+
*/
|
|
10
|
+
import type { Pool } from '../tenant-pool.js';
|
|
11
|
+
export interface RetentionJobResult {
|
|
12
|
+
orgsProcessed: number;
|
|
13
|
+
totalDropped: number;
|
|
14
|
+
totalDeleted: number;
|
|
15
|
+
warnings: RetentionWarning[];
|
|
16
|
+
errors: RetentionError[];
|
|
17
|
+
}
|
|
18
|
+
export interface RetentionWarning {
|
|
19
|
+
orgId: string;
|
|
20
|
+
type: 'approaching_expiry';
|
|
21
|
+
message: string;
|
|
22
|
+
daysUntilExpiry: number;
|
|
23
|
+
}
|
|
24
|
+
export interface RetentionError {
|
|
25
|
+
orgId: string;
|
|
26
|
+
error: string;
|
|
27
|
+
}
|
|
28
|
+
export interface OrgPurgeResult {
|
|
29
|
+
orgId: string;
|
|
30
|
+
partitionsDropped: string[];
|
|
31
|
+
rowsDeleted: number;
|
|
32
|
+
}
|
|
33
|
+
export interface RetentionJobDeps {
|
|
34
|
+
pool: Pool;
|
|
35
|
+
now?: Date;
|
|
36
|
+
logger?: RetentionLogger;
|
|
37
|
+
warningDays?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface RetentionLogger {
|
|
40
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
41
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
42
|
+
error(msg: string, meta?: Record<string, unknown>): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run the retention purge job for all organizations.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runRetentionJob(deps: RetentionJobDeps): Promise<RetentionJobResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Purge expired data for one org across given tables.
|
|
50
|
+
*
|
|
51
|
+
* Strategy (per architecture §11.2):
|
|
52
|
+
* 1. Check each partition — if its max_timestamp < cutoff, the whole partition is expired
|
|
53
|
+
* BUT we can only DROP a partition if ALL orgs' data in it is expired.
|
|
54
|
+
* Since partitions are shared across orgs, we use DELETE for per-org purging.
|
|
55
|
+
* 2. For per-org purging: DELETE WHERE org_id = :org AND timestamp < :cutoff
|
|
56
|
+
*
|
|
57
|
+
* Partition drops are handled by partition-management.ts which considers
|
|
58
|
+
* the minimum retention across all orgs (global partition lifecycle).
|
|
59
|
+
*/
|
|
60
|
+
export declare function purgeExpiredData(pool: Pool, orgId: string, tables: readonly string[], retentionDays: number, now: Date, logger?: RetentionLogger): Promise<OrgPurgeResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Check if an org has exportable data that will be deleted.
|
|
63
|
+
* Used to prompt export-before-delete in the UI.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getExpiringDataSummary(pool: Pool, orgId: string, retentionDays: number, now?: Date): Promise<{
|
|
66
|
+
eventCount: number;
|
|
67
|
+
oldestEvent: string | null;
|
|
68
|
+
cutoffDate: string;
|
|
69
|
+
}>;
|
|
70
|
+
//# sourceMappingURL=retention-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retention-job.d.ts","sourceRoot":"","sources":["../../../src/cloud/retention/retention-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAW9C,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAmBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwDzF;AAsDD;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,IAAI,EACT,MAAM,GAAE,eAA+B,GACtC,OAAO,CAAC,cAAc,CAAC,CAgCzB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,IAAiB,GACrB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBjF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Retention Purge Job (S-8.1)
|
|
3
|
+
*
|
|
4
|
+
* Daily cron (03:00 UTC) that purges events beyond each org's retention window.
|
|
5
|
+
* Primary mechanism: DROP entire expired monthly partitions.
|
|
6
|
+
* Partial DELETE for boundary partitions.
|
|
7
|
+
*
|
|
8
|
+
* Also handles: warning notifications before deletion, export-before-delete prompting.
|
|
9
|
+
*/
|
|
10
|
+
import { getEffectiveRetention, getRetentionCutoff, } from './retention-policy.js';
|
|
11
|
+
const defaultLogger = {
|
|
12
|
+
info: () => { },
|
|
13
|
+
warn: () => { },
|
|
14
|
+
error: () => { },
|
|
15
|
+
};
|
|
16
|
+
// Tables that need retention purging (partition-based)
|
|
17
|
+
const EVENT_TABLES = ['events'];
|
|
18
|
+
const AUDIT_TABLE = 'audit_log';
|
|
19
|
+
const SAFE_IDENTIFIER_RE = /^[a-z_][a-z0-9_]*$/;
|
|
20
|
+
function assertSafe(name) {
|
|
21
|
+
if (!SAFE_IDENTIFIER_RE.test(name))
|
|
22
|
+
throw new Error(`Unsafe identifier: ${name}`);
|
|
23
|
+
}
|
|
24
|
+
// ─── Core Job ────────────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Run the retention purge job for all organizations.
|
|
27
|
+
*/
|
|
28
|
+
export async function runRetentionJob(deps) {
|
|
29
|
+
const { pool, now = new Date(), logger = defaultLogger, warningDays = 7 } = deps;
|
|
30
|
+
const result = {
|
|
31
|
+
orgsProcessed: 0,
|
|
32
|
+
totalDropped: 0,
|
|
33
|
+
totalDeleted: 0,
|
|
34
|
+
warnings: [],
|
|
35
|
+
errors: [],
|
|
36
|
+
};
|
|
37
|
+
// Fetch all active orgs with their plans
|
|
38
|
+
const orgs = await getActiveOrgs(pool);
|
|
39
|
+
logger.info(`Retention job starting for ${orgs.length} orgs`);
|
|
40
|
+
for (const org of orgs) {
|
|
41
|
+
try {
|
|
42
|
+
const retention = getEffectiveRetention(org);
|
|
43
|
+
// Check for approaching expiry warnings
|
|
44
|
+
const warnings = await checkExpiryWarnings(pool, org, retention, now, warningDays);
|
|
45
|
+
result.warnings.push(...warnings);
|
|
46
|
+
// Purge expired events
|
|
47
|
+
const eventResult = await purgeExpiredData(pool, org.orgId, EVENT_TABLES, retention.eventRetentionDays, now, logger);
|
|
48
|
+
result.totalDropped += eventResult.partitionsDropped.length;
|
|
49
|
+
result.totalDeleted += eventResult.rowsDeleted;
|
|
50
|
+
// Purge expired audit logs
|
|
51
|
+
const auditResult = await purgeExpiredData(pool, org.orgId, [AUDIT_TABLE], retention.auditLogRetentionDays, now, logger);
|
|
52
|
+
result.totalDropped += auditResult.partitionsDropped.length;
|
|
53
|
+
result.totalDeleted += auditResult.rowsDeleted;
|
|
54
|
+
result.orgsProcessed++;
|
|
55
|
+
logger.info('Org retention complete', {
|
|
56
|
+
orgId: org.orgId,
|
|
57
|
+
dropped: eventResult.partitionsDropped.length + auditResult.partitionsDropped.length,
|
|
58
|
+
deleted: eventResult.rowsDeleted + auditResult.rowsDeleted,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
result.errors.push({ orgId: org.orgId, error: message });
|
|
64
|
+
logger.error(`Retention failed for org ${org.orgId}: ${message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
logger.info('Retention job complete', {
|
|
68
|
+
orgsProcessed: result.orgsProcessed,
|
|
69
|
+
totalDropped: result.totalDropped,
|
|
70
|
+
totalDeleted: result.totalDeleted,
|
|
71
|
+
warnings: result.warnings.length,
|
|
72
|
+
errors: result.errors.length,
|
|
73
|
+
});
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
// ─── Helpers ─────────────────────────────────────────────────────
|
|
77
|
+
async function getActiveOrgs(pool) {
|
|
78
|
+
const { rows } = await pool.query(`SELECT id AS "orgId", plan, custom_retention_days AS "customRetentionDays"
|
|
79
|
+
FROM orgs
|
|
80
|
+
WHERE deleted_at IS NULL`);
|
|
81
|
+
return rows.map((r) => ({
|
|
82
|
+
orgId: r.orgId,
|
|
83
|
+
plan: r.plan,
|
|
84
|
+
customRetentionDays: r.customRetentionDays,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if any org data is approaching its retention cutoff, and generate warnings.
|
|
89
|
+
*/
|
|
90
|
+
async function checkExpiryWarnings(pool, org, retention, now, warningDays) {
|
|
91
|
+
const warnings = [];
|
|
92
|
+
// Check if org has data that will expire within warningDays
|
|
93
|
+
const warningCutoff = getRetentionCutoff(retention.eventRetentionDays - warningDays, now);
|
|
94
|
+
const actualCutoff = getRetentionCutoff(retention.eventRetentionDays, now);
|
|
95
|
+
const { rows } = await pool.query(`SELECT COUNT(*) AS cnt FROM events
|
|
96
|
+
WHERE org_id = $1 AND timestamp >= $2 AND timestamp < $3`, [org.orgId, actualCutoff.toISOString(), warningCutoff.toISOString()]);
|
|
97
|
+
const count = Number(rows[0]?.cnt ?? 0);
|
|
98
|
+
if (count > 0) {
|
|
99
|
+
warnings.push({
|
|
100
|
+
orgId: org.orgId,
|
|
101
|
+
type: 'approaching_expiry',
|
|
102
|
+
message: `${count} events will expire within ${warningDays} days. Consider exporting data.`,
|
|
103
|
+
daysUntilExpiry: warningDays,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return warnings;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Purge expired data for one org across given tables.
|
|
110
|
+
*
|
|
111
|
+
* Strategy (per architecture §11.2):
|
|
112
|
+
* 1. Check each partition — if its max_timestamp < cutoff, the whole partition is expired
|
|
113
|
+
* BUT we can only DROP a partition if ALL orgs' data in it is expired.
|
|
114
|
+
* Since partitions are shared across orgs, we use DELETE for per-org purging.
|
|
115
|
+
* 2. For per-org purging: DELETE WHERE org_id = :org AND timestamp < :cutoff
|
|
116
|
+
*
|
|
117
|
+
* Partition drops are handled by partition-management.ts which considers
|
|
118
|
+
* the minimum retention across all orgs (global partition lifecycle).
|
|
119
|
+
*/
|
|
120
|
+
export async function purgeExpiredData(pool, orgId, tables, retentionDays, now, logger = defaultLogger) {
|
|
121
|
+
const cutoff = getRetentionCutoff(retentionDays, now);
|
|
122
|
+
const result = { orgId, partitionsDropped: [], rowsDeleted: 0 };
|
|
123
|
+
for (const table of tables) {
|
|
124
|
+
assertSafe(table);
|
|
125
|
+
const timestampCol = table === 'audit_log' ? 'created_at' : 'timestamp';
|
|
126
|
+
// Per-org batched DELETE (partitions are shared, can't drop per-org)
|
|
127
|
+
const BATCH_SIZE = 10000;
|
|
128
|
+
let totalDeleted = 0;
|
|
129
|
+
let batchDeleted;
|
|
130
|
+
do {
|
|
131
|
+
const deleteResult = await pool.query(`DELETE FROM ${table} WHERE ctid IN (
|
|
132
|
+
SELECT ctid FROM ${table} WHERE org_id = $1 AND ${timestampCol} < $2 LIMIT ${BATCH_SIZE}
|
|
133
|
+
)`, [orgId, cutoff.toISOString()]);
|
|
134
|
+
batchDeleted = deleteResult.rowCount ?? 0;
|
|
135
|
+
totalDeleted += batchDeleted;
|
|
136
|
+
} while (batchDeleted >= BATCH_SIZE);
|
|
137
|
+
result.rowsDeleted += totalDeleted;
|
|
138
|
+
if (totalDeleted > 0) {
|
|
139
|
+
logger.info(`Deleted ${totalDeleted} rows from ${table} for org ${orgId}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if an org has exportable data that will be deleted.
|
|
146
|
+
* Used to prompt export-before-delete in the UI.
|
|
147
|
+
*/
|
|
148
|
+
export async function getExpiringDataSummary(pool, orgId, retentionDays, now = new Date()) {
|
|
149
|
+
const cutoff = getRetentionCutoff(retentionDays, now);
|
|
150
|
+
const { rows } = await pool.query(`SELECT COUNT(*) AS cnt, MIN(timestamp) AS oldest
|
|
151
|
+
FROM events
|
|
152
|
+
WHERE org_id = $1 AND timestamp < $2`, [orgId, cutoff.toISOString()]);
|
|
153
|
+
const row = rows[0];
|
|
154
|
+
return {
|
|
155
|
+
eventCount: Number(row?.cnt ?? 0),
|
|
156
|
+
oldestEvent: row?.oldest ?? null,
|
|
157
|
+
cutoffDate: cutoff.toISOString(),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=retention-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retention-job.js","sourceRoot":"","sources":["../../../src/cloud/retention/retention-job.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Retention Policies (S-8.1)
|
|
3
|
+
*
|
|
4
|
+
* Per-tier retention windows. Orgs can override with custom settings (Enterprise).
|
|
5
|
+
*/
|
|
6
|
+
import type { TierName } from '../billing/stripe-client.js';
|
|
7
|
+
export interface RetentionPolicy {
|
|
8
|
+
eventRetentionDays: number;
|
|
9
|
+
auditLogRetentionDays: number;
|
|
10
|
+
}
|
|
11
|
+
/** Default retention per tier (architecture §11.1) */
|
|
12
|
+
export declare const TIER_RETENTION: Record<TierName, RetentionPolicy>;
|
|
13
|
+
export interface OrgRetentionInfo {
|
|
14
|
+
orgId: string;
|
|
15
|
+
plan: TierName;
|
|
16
|
+
customRetentionDays: number | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve effective retention days for an org.
|
|
20
|
+
* Enterprise orgs may have custom retention; others use tier defaults.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getEffectiveRetention(org: OrgRetentionInfo): RetentionPolicy;
|
|
23
|
+
/**
|
|
24
|
+
* Get the cutoff date for retention (events older than this should be purged).
|
|
25
|
+
*/
|
|
26
|
+
export declare function getRetentionCutoff(retentionDays: number, now?: Date): Date;
|
|
27
|
+
//# sourceMappingURL=retention-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retention-policy.d.ts","sourceRoot":"","sources":["../../../src/cloud/retention/retention-policy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,sDAAsD;AACtD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,eAAe,CAK5D,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,eAAe,CAW5E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,CAKtF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Retention Policies (S-8.1)
|
|
3
|
+
*
|
|
4
|
+
* Per-tier retention windows. Orgs can override with custom settings (Enterprise).
|
|
5
|
+
*/
|
|
6
|
+
/** Default retention per tier (architecture §11.1) */
|
|
7
|
+
export const TIER_RETENTION = {
|
|
8
|
+
free: { eventRetentionDays: 7, auditLogRetentionDays: 90 },
|
|
9
|
+
pro: { eventRetentionDays: 30, auditLogRetentionDays: 90 },
|
|
10
|
+
team: { eventRetentionDays: 90, auditLogRetentionDays: 365 },
|
|
11
|
+
enterprise: { eventRetentionDays: 365, auditLogRetentionDays: 365 },
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Resolve effective retention days for an org.
|
|
15
|
+
* Enterprise orgs may have custom retention; others use tier defaults.
|
|
16
|
+
*/
|
|
17
|
+
export function getEffectiveRetention(org) {
|
|
18
|
+
const tierDefaults = TIER_RETENTION[org.plan] ?? TIER_RETENTION.free;
|
|
19
|
+
if (org.customRetentionDays != null && org.plan === 'enterprise') {
|
|
20
|
+
return {
|
|
21
|
+
eventRetentionDays: org.customRetentionDays,
|
|
22
|
+
auditLogRetentionDays: Math.max(org.customRetentionDays, tierDefaults.auditLogRetentionDays),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return tierDefaults;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the cutoff date for retention (events older than this should be purged).
|
|
29
|
+
*/
|
|
30
|
+
export function getRetentionCutoff(retentionDays, now = new Date()) {
|
|
31
|
+
const cutoff = new Date(now);
|
|
32
|
+
cutoff.setUTCDate(cutoff.getUTCDate() - retentionDays);
|
|
33
|
+
cutoff.setUTCHours(0, 0, 0, 0);
|
|
34
|
+
return cutoff;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=retention-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retention-policy.js","sourceRoot":"","sources":["../../../src/cloud/retention/retention-policy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,sDAAsD;AACtD,MAAM,CAAC,MAAM,cAAc,GAAsC;IAC/D,IAAI,EAAE,EAAE,kBAAkB,EAAE,CAAC,EAAE,qBAAqB,EAAE,EAAE,EAAE;IAC1D,GAAG,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,qBAAqB,EAAE,EAAE,EAAE;IAC1D,IAAI,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE;IAC5D,UAAU,EAAE,EAAE,kBAAkB,EAAE,GAAG,EAAE,qBAAqB,EAAE,GAAG,EAAE;CACpE,CAAC;AAQF;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IACzD,MAAM,YAAY,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC;IAErE,IAAI,GAAG,CAAC,mBAAmB,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACjE,OAAO;YACL,kBAAkB,EAAE,GAAG,CAAC,mBAAmB;YAC3C,qBAAqB,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,YAAY,CAAC,qBAAqB,CAAC;SAC7F,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,aAAqB,EAAE,MAAY,IAAI,IAAI,EAAE;IAC9E,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,aAAa,CAAC,CAAC;IACvD,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Management Routes (S-7.3)
|
|
3
|
+
*
|
|
4
|
+
* Express-compatible route handlers for API key CRUD.
|
|
5
|
+
* Mounted at /api/cloud/orgs/:orgId/api-keys
|
|
6
|
+
*/
|
|
7
|
+
import type { MigrationClient } from '../migrate.js';
|
|
8
|
+
import type { AuditLogService } from '../auth/audit-log.js';
|
|
9
|
+
export interface ApiKeyRoutesDeps {
|
|
10
|
+
db: MigrationClient;
|
|
11
|
+
auditLog?: AuditLogService;
|
|
12
|
+
}
|
|
13
|
+
export declare function createApiKeyRouteHandlers(deps: ApiKeyRoutesDeps): {
|
|
14
|
+
/** GET /api/cloud/orgs/:orgId/api-keys — list all keys */
|
|
15
|
+
listKeys(orgId: string): Promise<{
|
|
16
|
+
status: number;
|
|
17
|
+
body: unknown;
|
|
18
|
+
}>;
|
|
19
|
+
/** POST /api/cloud/orgs/:orgId/api-keys — create a key */
|
|
20
|
+
createKey(orgId: string, userId: string, body: {
|
|
21
|
+
name: string;
|
|
22
|
+
environment: string;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
status: number;
|
|
25
|
+
body: unknown;
|
|
26
|
+
}>;
|
|
27
|
+
/** DELETE /api/cloud/orgs/:orgId/api-keys/:keyId — revoke a key */
|
|
28
|
+
revokeKey(orgId: string, keyId: string, userId: string): Promise<{
|
|
29
|
+
status: number;
|
|
30
|
+
body: unknown;
|
|
31
|
+
}>;
|
|
32
|
+
/** GET /api/cloud/orgs/:orgId/api-keys/limit — get key limit info */
|
|
33
|
+
getKeyLimit(orgId: string): Promise<{
|
|
34
|
+
status: number;
|
|
35
|
+
body: unknown;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=api-key-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-routes.d.ts","sourceRoot":"","sources":["../../../src/cloud/routes/api-key-routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,eAAe,CAAC;IACpB,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AASD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB;IAI5D,0DAA0D;oBACpC,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAKzE,0DAA0D;qBAEjD,MAAM,UACL,MAAM,QACR;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IA8B7C,mEAAmE;qBAE1D,MAAM,SACN,MAAM,UACL,MAAM,GACb,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAkB7C,qEAAqE;uBAC5C,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;EAW/E"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Management Routes (S-7.3)
|
|
3
|
+
*
|
|
4
|
+
* Express-compatible route handlers for API key CRUD.
|
|
5
|
+
* Mounted at /api/cloud/orgs/:orgId/api-keys
|
|
6
|
+
*/
|
|
7
|
+
import { ApiKeyService, ApiKeyError } from '../auth/api-keys.js';
|
|
8
|
+
const TIER_KEY_LIMITS = {
|
|
9
|
+
free: 2,
|
|
10
|
+
pro: 10,
|
|
11
|
+
team: 50,
|
|
12
|
+
enterprise: 200,
|
|
13
|
+
};
|
|
14
|
+
export function createApiKeyRouteHandlers(deps) {
|
|
15
|
+
const keyService = new ApiKeyService(deps.db);
|
|
16
|
+
return {
|
|
17
|
+
/** GET /api/cloud/orgs/:orgId/api-keys — list all keys */
|
|
18
|
+
async listKeys(orgId) {
|
|
19
|
+
const keys = await keyService.list(orgId);
|
|
20
|
+
return { status: 200, body: keys };
|
|
21
|
+
},
|
|
22
|
+
/** POST /api/cloud/orgs/:orgId/api-keys — create a key */
|
|
23
|
+
async createKey(orgId, userId, body) {
|
|
24
|
+
try {
|
|
25
|
+
const result = await keyService.create({
|
|
26
|
+
orgId,
|
|
27
|
+
name: body.name,
|
|
28
|
+
environment: body.environment,
|
|
29
|
+
createdBy: userId,
|
|
30
|
+
});
|
|
31
|
+
if (deps.auditLog) {
|
|
32
|
+
await deps.auditLog.write({
|
|
33
|
+
org_id: orgId,
|
|
34
|
+
actor_type: 'user',
|
|
35
|
+
actor_id: userId,
|
|
36
|
+
action: 'api_key.created',
|
|
37
|
+
resource_type: 'api_key',
|
|
38
|
+
resource_id: result.record.id,
|
|
39
|
+
details: { name: body.name, environment: body.environment },
|
|
40
|
+
ip_address: null,
|
|
41
|
+
result: 'success',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return { status: 201, body: result };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof ApiKeyError) {
|
|
48
|
+
return { status: 422, body: { error: err.message, code: err.code } };
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
/** DELETE /api/cloud/orgs/:orgId/api-keys/:keyId — revoke a key */
|
|
54
|
+
async revokeKey(orgId, keyId, userId) {
|
|
55
|
+
const ok = await keyService.revoke(orgId, keyId);
|
|
56
|
+
if (ok && deps.auditLog) {
|
|
57
|
+
await deps.auditLog.write({
|
|
58
|
+
org_id: orgId,
|
|
59
|
+
actor_type: 'user',
|
|
60
|
+
actor_id: userId,
|
|
61
|
+
action: 'api_key.revoked',
|
|
62
|
+
resource_type: 'api_key',
|
|
63
|
+
resource_id: keyId,
|
|
64
|
+
details: {},
|
|
65
|
+
ip_address: null,
|
|
66
|
+
result: 'success',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return { status: ok ? 200 : 404, body: { ok } };
|
|
70
|
+
},
|
|
71
|
+
/** GET /api/cloud/orgs/:orgId/api-keys/limit — get key limit info */
|
|
72
|
+
async getKeyLimit(orgId) {
|
|
73
|
+
const count = await keyService.countActive(orgId);
|
|
74
|
+
const orgResult = await deps.db.query(`SELECT plan FROM orgs WHERE id = $1`, [orgId]);
|
|
75
|
+
const plan = orgResult.rows[0]?.plan ?? 'free';
|
|
76
|
+
const limit = TIER_KEY_LIMITS[plan] ?? TIER_KEY_LIMITS.free;
|
|
77
|
+
return {
|
|
78
|
+
status: 200,
|
|
79
|
+
body: { current: count, limit, plan },
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=api-key-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-routes.js","sourceRoot":"","sources":["../../../src/cloud/routes/api-key-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Log Routes (S-7.6)
|
|
3
|
+
*
|
|
4
|
+
* Express-compatible route handlers for audit log page:
|
|
5
|
+
* - Query with filters (action, actor, time range)
|
|
6
|
+
* - Paginated results
|
|
7
|
+
* - Export to JSON
|
|
8
|
+
*/
|
|
9
|
+
import type { AuditLogService } from '../auth/audit-log.js';
|
|
10
|
+
export interface AuditRoutesDeps {
|
|
11
|
+
auditLog: AuditLogService;
|
|
12
|
+
}
|
|
13
|
+
export declare function createAuditRouteHandlers(deps: AuditRoutesDeps): {
|
|
14
|
+
/** GET /api/cloud/orgs/:orgId/audit-log?action=&actor=&from=&to=&limit=&offset= */
|
|
15
|
+
queryAuditLog(orgId: string, query: {
|
|
16
|
+
action?: string;
|
|
17
|
+
actor?: string;
|
|
18
|
+
from?: string;
|
|
19
|
+
to?: string;
|
|
20
|
+
limit?: string;
|
|
21
|
+
offset?: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
status: number;
|
|
24
|
+
body: unknown;
|
|
25
|
+
}>;
|
|
26
|
+
/** GET /api/cloud/orgs/:orgId/audit-log/export?from=&to= */
|
|
27
|
+
exportAuditLog(orgId: string, query: {
|
|
28
|
+
from?: string;
|
|
29
|
+
to?: string;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
status: number;
|
|
32
|
+
body: unknown;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=audit-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-routes.d.ts","sourceRoot":"","sources":["../../../src/cloud/routes/audit-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,sBAAsB,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe;IAE1D,mFAAmF;yBAE1E,MAAM,SACN;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GACA,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAuB7C,4DAA4D;0BAEnD,MAAM,SACN;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GACpC,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;EAgBlF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Log Routes (S-7.6)
|
|
3
|
+
*
|
|
4
|
+
* Express-compatible route handlers for audit log page:
|
|
5
|
+
* - Query with filters (action, actor, time range)
|
|
6
|
+
* - Paginated results
|
|
7
|
+
* - Export to JSON
|
|
8
|
+
*/
|
|
9
|
+
export function createAuditRouteHandlers(deps) {
|
|
10
|
+
return {
|
|
11
|
+
/** GET /api/cloud/orgs/:orgId/audit-log?action=&actor=&from=&to=&limit=&offset= */
|
|
12
|
+
async queryAuditLog(orgId, query) {
|
|
13
|
+
const filters = {
|
|
14
|
+
org_id: orgId,
|
|
15
|
+
action: query.action,
|
|
16
|
+
actor_id: query.actor,
|
|
17
|
+
from: query.from ? new Date(query.from) : undefined,
|
|
18
|
+
to: query.to ? new Date(query.to) : undefined,
|
|
19
|
+
limit: query.limit ? parseInt(query.limit, 10) : 50,
|
|
20
|
+
offset: query.offset ? parseInt(query.offset, 10) : 0,
|
|
21
|
+
};
|
|
22
|
+
const result = await deps.auditLog.query(filters);
|
|
23
|
+
return {
|
|
24
|
+
status: 200,
|
|
25
|
+
body: {
|
|
26
|
+
entries: result.entries,
|
|
27
|
+
total: result.total,
|
|
28
|
+
limit: filters.limit,
|
|
29
|
+
offset: filters.offset,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
/** GET /api/cloud/orgs/:orgId/audit-log/export?from=&to= */
|
|
34
|
+
async exportAuditLog(orgId, query) {
|
|
35
|
+
const entries = await deps.auditLog.export(orgId, query.from ? new Date(query.from) : undefined, query.to ? new Date(query.to) : undefined);
|
|
36
|
+
return {
|
|
37
|
+
status: 200,
|
|
38
|
+
body: entries,
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'Content-Disposition': `attachment; filename="audit-log-${orgId}.json"`,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=audit-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-routes.js","sourceRoot":"","sources":["../../../src/cloud/routes/audit-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,UAAU,wBAAwB,CAAC,IAAqB;IAC5D,OAAO;QACL,mFAAmF;QACnF,KAAK,CAAC,aAAa,CACjB,KAAa,EACb,KAOC;YAED,MAAM,OAAO,GAA4C;gBACvD,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,KAAK,CAAC,MAAiC;gBAC/C,QAAQ,EAAE,KAAK,CAAC,KAAK;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;gBACnD,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC7C,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBACnD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;aACtD,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAClD,OAAO;gBACL,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE;oBACJ,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB;aACF,CAAC;QACJ,CAAC;QAED,4DAA4D;QAC5D,KAAK,CAAC,cAAc,CAClB,KAAa,EACb,KAAqC;YAErC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CACxC,KAAK,EACL,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAC7C,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;YACF,OAAO;gBACL,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,qBAAqB,EAAE,mCAAmC,KAAK,QAAQ;iBACxE;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing Routes (S-7.5)
|
|
3
|
+
*
|
|
4
|
+
* Express-compatible route handlers for billing page data:
|
|
5
|
+
* - Current plan & trial status
|
|
6
|
+
* - Invoice history
|
|
7
|
+
* - Plan upgrade/downgrade
|
|
8
|
+
* - Payment method management (Stripe portal)
|
|
9
|
+
*/
|
|
10
|
+
import type { MigrationClient } from '../migrate.js';
|
|
11
|
+
import type { BillingService } from '../billing/billing-service.js';
|
|
12
|
+
import type { InvoiceService } from '../billing/invoice-service.js';
|
|
13
|
+
import type { TrialService } from '../billing/trial-service.js';
|
|
14
|
+
import type { AuditLogService } from '../auth/audit-log.js';
|
|
15
|
+
export interface BillingRoutesDeps {
|
|
16
|
+
db: MigrationClient;
|
|
17
|
+
billingService: BillingService;
|
|
18
|
+
invoiceService: InvoiceService;
|
|
19
|
+
trialService: TrialService;
|
|
20
|
+
auditLog?: AuditLogService;
|
|
21
|
+
}
|
|
22
|
+
export declare function createBillingRouteHandlers(deps: BillingRoutesDeps): {
|
|
23
|
+
/** GET /api/cloud/orgs/:orgId/billing — billing overview */
|
|
24
|
+
getBillingOverview(orgId: string, userId: string): Promise<{
|
|
25
|
+
status: number;
|
|
26
|
+
body: unknown;
|
|
27
|
+
}>;
|
|
28
|
+
/** GET /api/cloud/orgs/:orgId/billing/invoices — invoice history */
|
|
29
|
+
getInvoices(orgId: string, limit?: number, offset?: number): Promise<{
|
|
30
|
+
status: number;
|
|
31
|
+
body: unknown;
|
|
32
|
+
}>;
|
|
33
|
+
/** POST /api/cloud/orgs/:orgId/billing/upgrade — upgrade plan */
|
|
34
|
+
upgradePlan(orgId: string, userId: string, body: {
|
|
35
|
+
plan: string;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
status: number;
|
|
38
|
+
body: unknown;
|
|
39
|
+
}>;
|
|
40
|
+
/** POST /api/cloud/orgs/:orgId/billing/downgrade — downgrade to free */
|
|
41
|
+
downgradePlan(orgId: string, userId: string): Promise<{
|
|
42
|
+
status: number;
|
|
43
|
+
body: unknown;
|
|
44
|
+
}>;
|
|
45
|
+
/** POST /api/cloud/orgs/:orgId/billing/portal — create Stripe portal session URL */
|
|
46
|
+
createPortalSession(orgId: string): Promise<{
|
|
47
|
+
status: number;
|
|
48
|
+
body: unknown;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=billing-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-routes.d.ts","sourceRoot":"","sources":["../../../src/cloud/routes/billing-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,iBAAiB;IAE9D,4DAA4D;8BAEnD,MAAM,UACL,MAAM,GACb,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAwC7C,oEAAoE;uBAE3D,MAAM,oCAGZ,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAK7C,iEAAiE;uBAExD,MAAM,UACL,MAAM,QACR;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GACrB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IA0B7C,wEAAwE;yBAE/D,MAAM,UACL,MAAM,GACb,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAqB7C,oFAAoF;+BAE3E,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;EAiBhD"}
|