chore: merge executor worktree (worktree-agent-aa690b9b15c5b0f8e) — plan 24-01
This commit is contained in:
commit
2eb6c2884c
9 changed files with 862 additions and 95 deletions
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
phase: 24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud
|
||||
plan: 01
|
||||
subsystem: aws-route53
|
||||
tags: [route53, aws-sdk, migration, factory, foundation]
|
||||
dependency-graph:
|
||||
requires: []
|
||||
provides:
|
||||
- "@aws-sdk/client-route-53 dependency"
|
||||
- "route53_zones / route53_records / route53_record_history / route53_audit_log schema"
|
||||
- "lib/types/route53.ts contracts"
|
||||
- "lib/services/route53-factory.ts (getRoute53Client / isRoute53Configured / resetRoute53Client)"
|
||||
affects:
|
||||
- "plans 24-02 through 24-07 (all import these four artifacts)"
|
||||
tech-stack:
|
||||
added:
|
||||
- "@aws-sdk/client-route-53 ^3.1104.0"
|
||||
patterns:
|
||||
- "factory + is<Name>Configured() singleton (mirrors veeam-factory.ts)"
|
||||
- "dedicated-schema-per-integration migration (mirrors pax8/itglue conventions)"
|
||||
key-files:
|
||||
created:
|
||||
- migrations/102_route53_tables.sql
|
||||
- lib/types/route53.ts
|
||||
- lib/services/route53-factory.ts
|
||||
- lib/services/route53-factory.test.ts
|
||||
- .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/deferred-items.md
|
||||
modified:
|
||||
- package.json
|
||||
- package-lock.json
|
||||
- CLAUDE.md
|
||||
decisions:
|
||||
- "Fast-forwarded this worktree's branch onto master before starting (14 commits behind, zero unique commits — pure catch-up, not a merge) to pick up the phase 24 planning docs (24-01-PLAN.md etc.) that were committed to master after this worktree was created."
|
||||
metrics:
|
||||
duration: "Tasks 1-3 complete, checkpoint resolved"
|
||||
completed: "2026-08-05"
|
||||
---
|
||||
|
||||
# Phase 24 Plan 1: Route 53 Foundation Summary
|
||||
|
||||
Installed the official AWS Route 53 SDK client, created the dedicated four-table
|
||||
Postgres schema (mirror tables + change-history ledger + audit ledger), defined
|
||||
shared camelCase TypeScript types, and built the `route53-factory.ts` credential
|
||||
factory following the exact `is<Name>Configured()` + singleton shape every other
|
||||
Pulse integration uses — TDD RED/GREEN cycle, 7/7 tests passing.
|
||||
|
||||
## What Was Built
|
||||
|
||||
**Task 1 — AWS SDK + migration:**
|
||||
- `npm install @aws-sdk/client-route-53` (official `aws/aws-sdk-js-v3` package, confirmed `[OK]` in 24-RESEARCH.md's package legitimacy audit)
|
||||
- `migrations/102_route53_tables.sql` — four tables:
|
||||
- `route53_zones` (mirror, PK = AWS hosted zone id with `/hostedzone/` prefix stripped)
|
||||
- `route53_records` (mirror, PK = composite `record_key` string since Route 53 recordsets have no native id)
|
||||
- `route53_record_history` (D-06 append-only change ledger, `source` CHECK constrained to `pulse_crud`/`sync_detected_drift`)
|
||||
- `route53_audit_log` (D-03/D-07 append-only attempt log including failures, `status` CHECK constrained to `pending`/`committed`/`failed`, `zone_id` deliberately not an FK)
|
||||
- Seed row `INSERT INTO integration_settings (key, disabled) VALUES ('route53', false)` (D-10, display-only toggle — extends the existing seed list rather than editing the committed `081_integration_settings.sql`)
|
||||
- Migration was applied to the live database by the orchestrator after this worktree's commits landed: `docker exec -i pulse-postgres psql -U pulse_user -d pulse_autotask < migrations/102_route53_tables.sql`. All four tables confirmed present via `\dt route53_*`.
|
||||
|
||||
**Task 2 — Types + factory (TDD):**
|
||||
- RED: `lib/services/route53-factory.test.ts` written first, confirmed failing (module didn't exist)
|
||||
- GREEN: `lib/types/route53.ts` (camelCase interfaces/unions per the plan's `<interfaces>` contract) + `lib/services/route53-factory.ts` (singleton factory, no explicit `credentials:` option passed to `Route53Client` — relies on the AWS SDK default credential chain reading `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` from `process.env`, per 24-RESEARCH.md Pitfall 1)
|
||||
- `CLAUDE.md` integration table updated with the `AWS Route 53` / `AWS_*` exception row
|
||||
- 7/7 test assertions pass; `npx tsc --noEmit --pretty` exits clean
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
None — plan executed exactly as written for Tasks 1-2.
|
||||
|
||||
### Environment Note (not a deviation, a pre-condition)
|
||||
|
||||
This worktree's branch (`worktree-agent-aa690b9b15c5b0f8e`) was created before the
|
||||
phase 24 planning commits landed on `master` — it had zero unique commits and was
|
||||
purely 14 commits behind. Fast-forwarded (`git merge --ff-only master`) to pick up
|
||||
`24-01-PLAN.md` and related planning docs before execution could start. This was a
|
||||
clean fast-forward (no merge, no conflicts, nothing discarded).
|
||||
|
||||
### Out-of-Scope Discovery (logged, not fixed)
|
||||
|
||||
`npm test` (full suite, run as part of Task 2 verification) surfaced 2 pre-existing
|
||||
failures in `lib/services/analyzer/itglue-search.test.ts`, unrelated to this plan —
|
||||
neither that file nor `itglue-search.ts` were touched by Tasks 1-2. Logged to this
|
||||
phase's `deferred-items.md` per the scope boundary rule rather than fixed.
|
||||
|
||||
## Checkpoint Status: RESOLVED
|
||||
|
||||
Task 3's four verification items, confirmed by the orchestrator against the live
|
||||
`pulse-app` / `pulse-postgres` containers with the developer:
|
||||
|
||||
1. **BWS secret key names** — initially a MISMATCH: the Bitwarden project exposed
|
||||
`AWS_ACCESS_KEY` / `AWS_SECRET_KEY`, not the literal `AWS_ACCESS_KEY_ID` /
|
||||
`AWS_SECRET_ACCESS_KEY` the AWS SDK's default credential chain requires. No
|
||||
`AWS_REGION` key exists (factory defaults to `us-east-1`, as designed). **Resolved
|
||||
by renaming the secrets directly in Bitwarden Secrets Manager** (developer's choice,
|
||||
over adding an env-var alias shim in `docker-entrypoint.sh`) — re-verified via
|
||||
`bws secret list` inside the container afterward, confirmed `AWS_ACCESS_KEY_ID` /
|
||||
`AWS_SECRET_ACCESS_KEY` now present. **No change needed to
|
||||
`lib/services/route53-factory.ts`** — it already reads the AWS SDK's hardcoded
|
||||
env var names via the default credential chain, per Pitfall 1.
|
||||
2. **Credentials reach the container** — confirmed present in the actual Node process
|
||||
environment (`/proc/<node-pid>/environ`, presence-only check, count=2). Note:
|
||||
`docker exec pulse-app sh -lc 'echo $VAR'` as written in this task does NOT work
|
||||
for this verification — `docker exec` attaches a fresh process to the container
|
||||
namespace and does not inherit the runtime env of the `bws run`-wrapped PID 1
|
||||
process tree. Verified via the actual node process's `/proc/<pid>/environ` instead.
|
||||
3. **Outbound DNS egress** — `EGRESS-OK`: `1.1.1.1`/`8.8.8.8` UDP/53 reachable from
|
||||
inside the container. Plan 24-04 can use Node's `dns` module directly; no
|
||||
DoH-over-HTTPS fallback needed.
|
||||
4. **IAM scope** — developer to confirm from the AWS console side; not verifiable
|
||||
from inside the container/repo. Recorded here as an open operational item, not a
|
||||
blocker for 24-02 through 24-07 (D-05/D-06/D-07/D-10 schema/factory contracts do
|
||||
not depend on the IAM policy's exact scope).
|
||||
|
||||
### Incident: credential value briefly exposed during diagnosis (not part of this plan's code)
|
||||
|
||||
While diagnosing item 1's mismatch, the orchestrator ran `bws secret list -o json`
|
||||
inside the container to enumerate key *names* — this command prints full secret
|
||||
*values* by design (unlike the task's own `${VAR:+SET}`-style presence checks) and
|
||||
briefly exposed the AWS access key ID/secret value in the session transcript. The
|
||||
developer was notified immediately and advised to rotate the exposed key in AWS IAM.
|
||||
This is an operational incident, not a code defect — recorded here for traceability
|
||||
since it happened during this plan's checkpoint verification. No repository file
|
||||
contains the exposed values.
|
||||
|
||||
### Additional finding fixed during checkpoint verification (outside this plan's `files_modified`, pre-existing uncommitted work)
|
||||
|
||||
Two bugs in already-staged, uncommitted BWS infrastructure files (`Dockerfile`,
|
||||
`docker-compose.yml`, `docker-entrypoint.sh` — not part of this plan's scope, but
|
||||
blocking checkpoint verification) were found and fixed by the orchestrator:
|
||||
- `docker-compose.yml`: the `app` service's `environment:` block re-declared
|
||||
`BWS_ACCESS_TOKEN`/`BWS_PROJECT_ID` as `${VAR:-}` substitutions, which resolve
|
||||
against the root `.env` (not `.env.local`) and silently overrode the real token
|
||||
with an empty string. Fixed by removing the redundant re-declaration.
|
||||
- `Dockerfile`: the generated `bws` CLI config only set `state_dir`, but bws 2.x
|
||||
requires `server_base` (or `server_identity`) even for the default Bitwarden cloud
|
||||
instance — this crash-looped the `pulse-app` container on every start. Fixed by
|
||||
adding `server_base = "https://vault.bitwarden.com"` to the generated config.
|
||||
|
||||
Both fixes were verified live (container rebuilt, restarted, confirmed healthy) but
|
||||
remain uncommitted, matching the state of the rest of this BWS infra work — the
|
||||
developer owns when to commit that separately from this phase's plans.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
All created files confirmed present:
|
||||
- FOUND: migrations/102_route53_tables.sql
|
||||
- FOUND: lib/types/route53.ts
|
||||
- FOUND: lib/services/route53-factory.ts
|
||||
- FOUND: lib/services/route53-factory.test.ts
|
||||
- FOUND: .planning/phases/24-aws-route-53-dns-sync-track-changes-crud-operations-full-aud/deferred-items.md
|
||||
|
||||
All commits confirmed present in `git log`:
|
||||
- b9df27b feat(24-01): install AWS Route 53 SDK and create dedicated schema migration
|
||||
- 4dd9d5d test(24-01): add failing test for Route 53 credential factory
|
||||
- 210f84d feat(24-01): implement Route 53 credential factory
|
||||
- fadae68 docs(24-01): log pre-existing itglue-search test failures as out-of-scope
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Deferred Items — Phase 24 (AWS Route 53 DNS Sync)
|
||||
|
||||
Out-of-scope discoveries logged during execution. Not fixed per the scope
|
||||
boundary rule (only auto-fix issues directly caused by the current task's
|
||||
changes).
|
||||
|
||||
## Plan 24-01
|
||||
|
||||
- **`lib/services/analyzer/itglue-search.test.ts`** — 2 pre-existing test
|
||||
failures (`returns capped, redacted doc snippets when the org is found`,
|
||||
`tolerates per-call failures (configurations errors, flex still returns)`)
|
||||
surfaced by `npm test` (full suite) while verifying Task 2. Neither
|
||||
`itglue-search.ts` nor its test file were touched by this plan (git status
|
||||
confirms zero changes to either path from Task 1 or Task 2). Unrelated to
|
||||
the Route 53 factory/schema work in this plan — not fixed, logged here for
|
||||
visibility.
|
||||
|
|
@ -80,6 +80,7 @@ Examples: `getAutotaskClient()`, `getMsgraphClient()`, `getDattoRmmClient()`,
|
|||
| Veeam VSPC | `VEEAM_VSPC_*` |
|
||||
| Auvik / Addigy / IT Glue / Mimecast / S1 / Duo / Zoom / QBO / Zabbix / Salesbldr | `<NAME>_*` |
|
||||
| PAX8 | `PAX8_*` (OAuth2 client-credentials, read-only partner/reseller API) |
|
||||
| AWS Route 53 | `AWS_*` (literal `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_REGION` — intentional exception to the per-service prefix convention; the AWS SDK's default credential chain hardcodes these names. Injected by BWS at the container entrypoint, never in `.env`) |
|
||||
| Anthropic | `ANTHROPIC_API_KEY` (analyzer pipeline + `ai-triage-service.ts`) |
|
||||
| OpenRouter | `OPENROUTER_API_KEY` (alternate analyzer provider, opt-in per request) |
|
||||
| Backblaze B2 | `B2_*` (LogLift evidence storage) |
|
||||
|
|
|
|||
63
lib/services/route53-factory.test.ts
Normal file
63
lib/services/route53-factory.test.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { isRoute53Configured, getRoute53Client, resetRoute53Client } from './route53-factory';
|
||||
|
||||
const ORIGINAL_ENV = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.AWS_ACCESS_KEY_ID;
|
||||
delete process.env.AWS_SECRET_ACCESS_KEY;
|
||||
delete process.env.AWS_REGION;
|
||||
resetRoute53Client();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = { ...ORIGINAL_ENV };
|
||||
resetRoute53Client();
|
||||
});
|
||||
|
||||
describe('isRoute53Configured', () => {
|
||||
it('returns false when AWS_ACCESS_KEY_ID is unset', () => {
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
|
||||
expect(isRoute53Configured()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when AWS_SECRET_ACCESS_KEY is unset', () => {
|
||||
process.env.AWS_ACCESS_KEY_ID = 'id1';
|
||||
expect(isRoute53Configured()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when both are set to empty strings', () => {
|
||||
process.env.AWS_ACCESS_KEY_ID = '';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = '';
|
||||
expect(isRoute53Configured()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true when both are set to non-empty values', () => {
|
||||
process.env.AWS_ACCESS_KEY_ID = 'id1';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
|
||||
expect(isRoute53Configured()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRoute53Client', () => {
|
||||
it('throws an Error mentioning AWS_ACCESS_KEY_ID when credentials are absent', () => {
|
||||
expect(() => getRoute53Client()).toThrow(/AWS_ACCESS_KEY_ID/);
|
||||
});
|
||||
|
||||
it('returns the same cached instance on a second call', () => {
|
||||
process.env.AWS_ACCESS_KEY_ID = 'id1';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
|
||||
const first = getRoute53Client();
|
||||
const second = getRoute53Client();
|
||||
expect(second).toBe(first);
|
||||
});
|
||||
|
||||
it('returns a different instance after resetRoute53Client()', () => {
|
||||
process.env.AWS_ACCESS_KEY_ID = 'id1';
|
||||
process.env.AWS_SECRET_ACCESS_KEY = 'secret1';
|
||||
const first = getRoute53Client();
|
||||
resetRoute53Client();
|
||||
const second = getRoute53Client();
|
||||
expect(second).not.toBe(first);
|
||||
});
|
||||
});
|
||||
47
lib/services/route53-factory.ts
Normal file
47
lib/services/route53-factory.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { Route53Client } from '@aws-sdk/client-route-53';
|
||||
|
||||
let route53ClientInstance: Route53Client | null = null;
|
||||
|
||||
/**
|
||||
* Check if AWS Route 53 credentials are configured.
|
||||
*/
|
||||
export function isRoute53Configured(): boolean {
|
||||
return !!(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the Route 53 client singleton instance.
|
||||
*
|
||||
* Intentionally does NOT pass an explicit `credentials` option — omitting it
|
||||
* lets @aws-sdk/credential-provider-node's default credential chain read
|
||||
* AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN from
|
||||
* process.env automatically (fromEnv() is first in the chain). This is
|
||||
* exactly how Bitwarden Secrets Manager (`bws run`) injects credentials at
|
||||
* the docker-entrypoint.sh layer. Do NOT "fix" this by adding an explicit
|
||||
* credentials object — see 24-RESEARCH.md Pitfall 1.
|
||||
*/
|
||||
export function getRoute53Client(): Route53Client {
|
||||
if (!route53ClientInstance) {
|
||||
if (!isRoute53Configured()) {
|
||||
throw new Error(
|
||||
'AWS credentials missing. Please set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (and AWS_REGION) environment variables.'
|
||||
);
|
||||
}
|
||||
|
||||
// Route 53 is a global service but the SDK still requires a signing
|
||||
// region; us-east-1 is the conventional default AWS's own CLI/console use.
|
||||
route53ClientInstance = new Route53Client({
|
||||
region: process.env.AWS_REGION || 'us-east-1',
|
||||
});
|
||||
console.log('Route 53 client initialized');
|
||||
}
|
||||
|
||||
return route53ClientInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the singleton instance (useful for testing).
|
||||
*/
|
||||
export function resetRoute53Client(): void {
|
||||
route53ClientInstance = null;
|
||||
}
|
||||
100
lib/types/route53.ts
Normal file
100
lib/types/route53.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* AWS Route 53 DNS sync — shared type definitions.
|
||||
* API-response shapes (camelCase) — route handlers transform snake_case
|
||||
* Postgres rows into these manually (no ORM, per CLAUDE.md).
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Zone / Record mirror types
|
||||
// ============================================================================
|
||||
|
||||
export interface Route53Zone {
|
||||
id: string;
|
||||
name: string;
|
||||
comment: string | null;
|
||||
privateZone: boolean;
|
||||
recordCount: number;
|
||||
authoritativeNameServers: string[] | null;
|
||||
syncedAt: string;
|
||||
isDeleted: boolean;
|
||||
}
|
||||
|
||||
export interface Route53RecordValue {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Route53Record {
|
||||
recordKey: string;
|
||||
zoneId: string;
|
||||
name: string;
|
||||
type: string;
|
||||
setIdentifier: string | null;
|
||||
ttl: number | null;
|
||||
resourceRecords: Route53RecordValue[] | null;
|
||||
aliasTarget: Record<string, unknown> | null;
|
||||
syncedAt: string;
|
||||
isDeleted: boolean;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Change ledger / audit log types
|
||||
// ============================================================================
|
||||
|
||||
export type Route53HistorySource = 'pulse_crud' | 'sync_detected_drift';
|
||||
|
||||
export interface Route53RecordHistory {
|
||||
id: string;
|
||||
zoneId: string;
|
||||
recordKey: string;
|
||||
recordName: string;
|
||||
recordType: string;
|
||||
changeAction: 'create' | 'update' | 'delete';
|
||||
beforeValue: Record<string, unknown> | null;
|
||||
afterValue: Record<string, unknown> | null;
|
||||
source: Route53HistorySource;
|
||||
changedByUserId: string | null;
|
||||
changedByEmail: string | null;
|
||||
changedAt: string;
|
||||
}
|
||||
|
||||
export type Route53AuditStatus = 'pending' | 'committed' | 'failed';
|
||||
|
||||
export interface Route53AuditLog {
|
||||
id: string;
|
||||
operation: 'create' | 'update' | 'delete' | 'sync';
|
||||
zoneId: string | null;
|
||||
recordKey: string | null;
|
||||
recordName: string | null;
|
||||
recordType: string | null;
|
||||
beforeValue: Record<string, unknown> | null;
|
||||
afterValue: Record<string, unknown> | null;
|
||||
performedByUserId: string | null;
|
||||
performedByEmail: string | null;
|
||||
performedAt: string;
|
||||
completedAt: string | null;
|
||||
status: Route53AuditStatus;
|
||||
awsChangeId: string | null;
|
||||
awsChangeStatus: string | null;
|
||||
errorMessage: string | null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Writable record types (D-01)
|
||||
// ============================================================================
|
||||
|
||||
export type Route53WritableType = 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT' | 'SRV';
|
||||
|
||||
// ============================================================================
|
||||
// Sync result
|
||||
// ============================================================================
|
||||
|
||||
export interface Route53SyncResult {
|
||||
syncId: string;
|
||||
syncType: string;
|
||||
status: string;
|
||||
startedAt: string;
|
||||
completedAt: string | null;
|
||||
duration: number | null;
|
||||
entities: Record<string, unknown>;
|
||||
errors: string[];
|
||||
}
|
||||
149
migrations/102_route53_tables.sql
Normal file
149
migrations/102_route53_tables.sql
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
-- AWS Route 53 DNS sync — Phase 24 dedicated Postgres schema.
|
||||
--
|
||||
-- Lays down the full Route 53 schema this phase's downstream plans (sync
|
||||
-- service, CRUD routes, health check, admin UI) will populate and consume.
|
||||
--
|
||||
-- • Hosted zones (mirror of AWS Route 53 zones) -> route53_zones
|
||||
-- • Resource record sets (mirror of AWS records) -> route53_records
|
||||
-- • Append-only change ledger (drift + CRUD) -> route53_record_history
|
||||
-- • Append-only attempt audit log (D-03/D-07) -> route53_audit_log
|
||||
--
|
||||
-- Retention is unbounded by design (D-08) — no purge job, no TTL, and no
|
||||
-- DELETE statement against route53_record_history or route53_audit_log
|
||||
-- anywhere in this phase, matching existing Pulse convention (itglue_writes,
|
||||
-- itglue_asset_audits, phishing audit_events are all forever-retained too).
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. route53_zones — mirror of AWS Route 53 hosted zones
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- id is the AWS hosted zone id with the '/hostedzone/' prefix stripped — the
|
||||
-- natural stable identifier Route 53 already provides, no synthetic UUID
|
||||
-- needed. authoritative_name_servers backs the D-12 NS-delegation health
|
||||
-- check planned in 24-04.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS route53_zones (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
comment TEXT,
|
||||
private_zone BOOLEAN NOT NULL DEFAULT false,
|
||||
record_count INTEGER NOT NULL DEFAULT 0,
|
||||
authoritative_name_servers JSONB,
|
||||
raw_payload JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
synced_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_zones_is_deleted ON route53_zones(is_deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_zones_name ON route53_zones(name);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. route53_records — mirror of AWS Route 53 resource record sets
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- record_key is the composite string `zoneId:name:type:setIdentifier` (empty
|
||||
-- string when there is no set identifier) — Route 53 recordsets are uniquely
|
||||
-- identified by zone+name+type+SetIdentifier; there is no AWS-side record id
|
||||
-- to use as a natural primary key.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS route53_records (
|
||||
record_key TEXT PRIMARY KEY,
|
||||
zone_id TEXT NOT NULL REFERENCES route53_zones(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
set_identifier TEXT,
|
||||
ttl INTEGER,
|
||||
resource_records JSONB,
|
||||
alias_target JSONB,
|
||||
raw_payload JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
synced_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_records_zone ON route53_records(zone_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_records_is_deleted ON route53_records(is_deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_records_name_type ON route53_records(zone_id, name, type);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. route53_record_history — append-only change ledger (D-06)
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Written by BOTH the sync service (source='sync_detected_drift', when a
|
||||
-- previously-synced record's live AWS value no longer matches Postgres) and
|
||||
-- the CRUD routes (source='pulse_crud', on every successful write). Distinct
|
||||
-- from route53_audit_log below: this table is a per-record timeline of
|
||||
-- resolved changes; the audit log below is a per-attempt ledger including
|
||||
-- failures. audit_log_id is a soft (non-FK) reference so a history row
|
||||
-- survives independent of the audit log's own lifecycle.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS route53_record_history (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
zone_id TEXT NOT NULL REFERENCES route53_zones(id) ON DELETE CASCADE,
|
||||
record_key TEXT NOT NULL,
|
||||
record_name TEXT NOT NULL,
|
||||
record_type TEXT NOT NULL,
|
||||
change_action TEXT NOT NULL CHECK (change_action IN ('create','update','delete')),
|
||||
before_value JSONB,
|
||||
after_value JSONB,
|
||||
source TEXT NOT NULL CHECK (source IN ('pulse_crud','sync_detected_drift')),
|
||||
changed_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
|
||||
changed_by_email TEXT,
|
||||
audit_log_id UUID, -- soft ref -> route53_audit_log(id), no hard FK
|
||||
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_record_history_record ON route53_record_history(record_key, changed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_record_history_source ON route53_record_history(source);
|
||||
CREATE INDEX IF NOT EXISTS idx_route53_record_history_zone ON route53_record_history(zone_id, changed_at DESC);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. route53_audit_log — append-only attempt audit log (D-03/D-07)
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- One row per attempted CRUD/sync operation, including failures. zone_id is
|
||||
-- deliberately NOT an FK here (unlike route53_record_history above) — a
|
||||
-- failed attempt against a zone that was never synced must still be
|
||||
-- recordable. D-03 accepted tradeoff: destructive record operations execute
|
||||
-- immediately with no staged approval gate; this table is the compensating
|
||||
-- post-hoc traceability control (actor, timestamp, before/after for every
|
||||
-- attempt), not a pre-write block.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS route53_audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
operation TEXT NOT NULL CHECK (operation IN ('create','update','delete','sync')),
|
||||
zone_id TEXT,
|
||||
record_key TEXT,
|
||||
record_name TEXT,
|
||||
record_type TEXT,
|
||||
before_value JSONB,
|
||||
after_value JSONB,
|
||||
performed_by_user_id TEXT REFERENCES "user"(id) ON DELETE SET NULL,
|
||||
performed_by_email TEXT,
|
||||
performed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
status TEXT NOT NULL CHECK (status IN ('pending','committed','failed')),
|
||||
aws_change_id TEXT,
|
||||
aws_change_status TEXT,
|
||||
aws_response JSONB,
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_route53_audit_log_record ON route53_audit_log(zone_id, record_key, performed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS ix_route53_audit_log_status ON route53_audit_log(status);
|
||||
|
||||
COMMENT ON TABLE route53_record_history IS
|
||||
'Append-only per-record change timeline. source distinguishes CRUD writes (pulse_crud) from sync-detected drift (sync_detected_drift). Unbounded retention by design (D-08) — no purge job.';
|
||||
COMMENT ON TABLE route53_audit_log IS
|
||||
'Append-only per-attempt audit log including failures. D-03 accepted tradeoff: destructive record ops execute immediately with no pre-write approval gate; this table is the post-hoc compensating control. Unbounded retention by design (D-08) — no purge job.';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. integration_settings seed row (D-10)
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Display-only toggle on /admin/integrations — no sync/CRUD-blocking
|
||||
-- behavior anywhere in this phase (unlike PAX8's disable-gates-fullSync
|
||||
-- exception). Extends the existing seed list from migrations/081 rather
|
||||
-- than editing that committed file.
|
||||
|
||||
INSERT INTO integration_settings (key, disabled) VALUES ('route53', false) ON CONFLICT (key) DO NOTHING;
|
||||
423
package-lock.json
generated
423
package-lock.json
generated
|
|
@ -9,6 +9,7 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.91.1",
|
||||
"@aws-sdk/client-route-53": "^3.1104.0",
|
||||
"@better-auth/cli": "^1.4.10",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
|
|
@ -246,6 +247,279 @@
|
|||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53": {
|
||||
"version": "3.1104.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-route-53/-/client-route-53-3.1104.0.tgz",
|
||||
"integrity": "sha512-19Wzq64FbYtZKcN/AoXwq4Nht+HwxTIVNI5QbUzLZ8Dyld0da6xdNIOBxX/0bGu5bnijj4dxJjokvLRoTpOpOA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.78",
|
||||
"@aws-sdk/middleware-sdk-route53": "^3.972.23",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/fetch-http-handler": "^5.6.13",
|
||||
"@smithy/node-http-handler": "^4.9.13",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/core": {
|
||||
"version": "3.977.6",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.6.tgz",
|
||||
"integrity": "sha512-QiaJV4/zDrB4ZY2mfeSXSzSTc36W16sZXcGz+SPFk0CJ26gziO0cS+4LjJUMAbdeeBOvS0k0Aq1cZpfGdUXxSw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@aws-sdk/xml-builder": "^3.972.37",
|
||||
"@aws/lambda-invoke-store": "^0.3.0",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/signature-v4": "^5.6.12",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"bowser": "^2.11.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-env": {
|
||||
"version": "3.972.67",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.67.tgz",
|
||||
"integrity": "sha512-rcIpk5kxUqDaaNa6Xk23pQ6ViY7jlqzmfFWCahQcBT97ddXaXYYwzCen9Tz1Jvo6aJft6wDl5bN44/Jw5B4oLA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-http": {
|
||||
"version": "3.972.69",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.69.tgz",
|
||||
"integrity": "sha512-nggwJtZ4eeNsUw5IeWBMXsi1ryct5idi0K+/SCRF3kybLubOMaNTb3XCihXpWMiVpyzyPeIrl0zTkzhBH9porA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/fetch-http-handler": "^5.6.13",
|
||||
"@smithy/node-http-handler": "^4.9.13",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-ini": {
|
||||
"version": "3.973.12",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.12.tgz",
|
||||
"integrity": "sha512-pNEf/OeyN5X3VmLKlgSO6TqaWmW10CvI3TfwL1XhsuhYjSLT2VDaxFnCPHnOeQXSaFisMX4jNhpETriqN8DOmg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/credential-provider-env": "^3.972.67",
|
||||
"@aws-sdk/credential-provider-http": "^3.972.69",
|
||||
"@aws-sdk/credential-provider-login": "^3.972.74",
|
||||
"@aws-sdk/credential-provider-process": "^3.972.67",
|
||||
"@aws-sdk/credential-provider-sso": "^3.973.11",
|
||||
"@aws-sdk/credential-provider-web-identity": "^3.972.73",
|
||||
"@aws-sdk/nested-clients": "^3.997.41",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/credential-provider-imds": "^4.4.16",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-login": {
|
||||
"version": "3.972.74",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.74.tgz",
|
||||
"integrity": "sha512-0AQfDcf99TNmqVKv0owHrw/TQs6i4ZE5t9qmz6NvO53bE/sA/tpXhXL9AAcEP1qHc6Zzjd1UMb69+/9zdhvY3g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/nested-clients": "^3.997.41",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-node": {
|
||||
"version": "3.972.78",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.78.tgz",
|
||||
"integrity": "sha512-OgPAnfvbGAMWac6yvxJ1ihslrvDpPVwR68D2csospdNCCyPvHk9JLzYKwz48SNiS1T2znDwHauywRKRFfpyYng==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-provider-env": "^3.972.67",
|
||||
"@aws-sdk/credential-provider-http": "^3.972.69",
|
||||
"@aws-sdk/credential-provider-ini": "^3.973.12",
|
||||
"@aws-sdk/credential-provider-process": "^3.972.67",
|
||||
"@aws-sdk/credential-provider-sso": "^3.973.11",
|
||||
"@aws-sdk/credential-provider-web-identity": "^3.972.73",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/credential-provider-imds": "^4.4.16",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-process": {
|
||||
"version": "3.972.67",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.67.tgz",
|
||||
"integrity": "sha512-IlUEejorGTWKb4/Dm7K5Yw4QxUmXLThLhrvBmzVBqZFTbW72cv9LTcITmo1dsnYriALE4h68mOq4LB99x6sQ7Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-sso": {
|
||||
"version": "3.973.11",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.11.tgz",
|
||||
"integrity": "sha512-gAQBkBZxUB84d71+pPcI9L+jh2ujhuAVxc/4FgGiWFDjkPBlMKxzd5XDtkSXTFX8Ro7ansnT88+XadasxMeCRw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/nested-clients": "^3.997.41",
|
||||
"@aws-sdk/token-providers": "3.1103.0",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/credential-provider-web-identity": {
|
||||
"version": "3.972.73",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.73.tgz",
|
||||
"integrity": "sha512-SnlEmQa6SjOgs6iOPLUQl1Eyq4AKiAdPQlkOhFhqNfDtDCwibMGvL6QlkSmf3o6vAUSImzdPCxowT5dfQUZP1A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/nested-clients": "^3.997.41",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/nested-clients": {
|
||||
"version": "3.997.41",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.41.tgz",
|
||||
"integrity": "sha512-RDHqPGQWlF6tatA/Tp3rg6oIwtgN9IVderxE+9av2Y93Dfyu+mO1hZ5Bu2jpfZg2rwdNbsssnwM+sLafIczMlQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/signature-v4-multi-region": "^3.996.43",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/fetch-http-handler": "^5.6.13",
|
||||
"@smithy/node-http-handler": "^4.9.13",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/signature-v4-multi-region": {
|
||||
"version": "3.996.43",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.43.tgz",
|
||||
"integrity": "sha512-lKekx8bLBXSv4O+cslk9Zfnw2XKSkWBs3uWL5QGhH2ZAQfNS7FE0vcSSN2vD/AhxX54ZTywWxR4STThoeOXlBA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/signature-v4": "^5.6.12",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/token-providers": {
|
||||
"version": "3.1103.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1103.0.tgz",
|
||||
"integrity": "sha512-N4wy26MNn31ItGVHYHPrEuCIFY4MBBjC+C5v1lJKqIUSA7OZBdhleCY53zCCrXn27hsk7YNOaTuhQu807S4AfQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.977.6",
|
||||
"@aws-sdk/nested-clients": "^3.997.41",
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/types": {
|
||||
"version": "3.974.2",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz",
|
||||
"integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws-sdk/xml-builder": {
|
||||
"version": "3.972.37",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz",
|
||||
"integrity": "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-route-53/node_modules/@aws/lambda-invoke-store": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz",
|
||||
"integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-sesv2": {
|
||||
"version": "3.943.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.943.0.tgz",
|
||||
|
|
@ -587,6 +861,33 @@
|
|||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-sdk-route53": {
|
||||
"version": "3.972.23",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-route53/-/middleware-sdk-route53-3.972.23.tgz",
|
||||
"integrity": "sha512-p2mbkuh45ZLjaDPiQVHP679Rpj7hcK7zUAy4SBe91xur8jG43OZOyX0M9ScLP5BcqWksX87V+ucyvq8YJ/gIHQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "^3.974.2",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-sdk-route53/node_modules/@aws-sdk/types": {
|
||||
"version": "3.974.2",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz",
|
||||
"integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-sdk-s3": {
|
||||
"version": "3.943.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.943.0.tgz",
|
||||
|
|
@ -5370,20 +5671,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/abort-controller": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz",
|
||||
"integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.9.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/config-resolver": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz",
|
||||
|
|
@ -5403,21 +5690,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/core": {
|
||||
"version": "3.18.7",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz",
|
||||
"integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==",
|
||||
"dev": true,
|
||||
"version": "3.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.31.1.tgz",
|
||||
"integrity": "sha512-CyogUINxvi7C7LDsh8Syo6hVJOT9ckz4rG8dRZfTJ8r91HkMY59PnNooaj7WcHyxEkxPfBAmbgztZU+xTo76lg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/middleware-serde": "^4.2.6",
|
||||
"@smithy/protocol-http": "^5.3.5",
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-middleware": "^4.2.5",
|
||||
"@smithy/util-stream": "^4.5.6",
|
||||
"@smithy/util-utf8": "^4.2.0",
|
||||
"@smithy/uuid": "^1.1.0",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -5425,16 +5703,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/credential-provider-imds": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz",
|
||||
"integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
|
||||
"dev": true,
|
||||
"version": "4.4.16",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.16.tgz",
|
||||
"integrity": "sha512-QfuLWAkLzptffFW980AFeHZFdqds2B64rpEd3uJ6lgs3xVn9QegGMUgUcj+4d7dRrAsya3r58ZKpku97WcFb4w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/node-config-provider": "^4.3.5",
|
||||
"@smithy/property-provider": "^4.2.5",
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/url-parser": "^4.2.5",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -5442,16 +5717,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/fetch-http-handler": {
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz",
|
||||
"integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==",
|
||||
"dev": true,
|
||||
"version": "5.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.13.tgz",
|
||||
"integrity": "sha512-4fW86pEUOMbrD5nkbyl/tTvPHHWJFbuB2odl6ps9lWfHoXf9HWh3Q/Smh59qH1g7+c/BSZghX6bbUk4gsiMs8A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/protocol-http": "^5.3.5",
|
||||
"@smithy/querystring-builder": "^4.2.5",
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -5603,16 +5875,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/node-http-handler": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz",
|
||||
"integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==",
|
||||
"dev": true,
|
||||
"version": "4.9.13",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.13.tgz",
|
||||
"integrity": "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/abort-controller": "^4.2.5",
|
||||
"@smithy/protocol-http": "^5.3.5",
|
||||
"@smithy/querystring-builder": "^4.2.5",
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -5647,21 +5916,6 @@
|
|||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/querystring-builder": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz",
|
||||
"integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/util-uri-escape": "^4.2.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/querystring-parser": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz",
|
||||
|
|
@ -5704,19 +5958,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/signature-v4": {
|
||||
"version": "5.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz",
|
||||
"integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==",
|
||||
"dev": true,
|
||||
"version": "5.6.12",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.12.tgz",
|
||||
"integrity": "sha512-I6KLtq3H0qqSuV9vLglfi8puHqzygzWHOnI4z/Rdoo+q50vvo18vBRdPAvvEtcaKROz7Zn6qnPa14kRfPH6PcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/is-array-buffer": "^4.2.0",
|
||||
"@smithy/protocol-http": "^5.3.5",
|
||||
"@smithy/types": "^4.9.0",
|
||||
"@smithy/util-hex-encoding": "^4.2.0",
|
||||
"@smithy/util-middleware": "^4.2.5",
|
||||
"@smithy/util-uri-escape": "^4.2.0",
|
||||
"@smithy/util-utf8": "^4.2.0",
|
||||
"@smithy/core": "^3.31.1",
|
||||
"@smithy/types": "^4.16.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -5743,10 +5991,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@smithy/types": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz",
|
||||
"integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
|
||||
"dev": true,
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz",
|
||||
"integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
|
|
@ -5950,19 +6197,6 @@
|
|||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-uri-escape": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz",
|
||||
"integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@smithy/util-utf8": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz",
|
||||
|
|
@ -8016,7 +8250,6 @@
|
|||
"version": "2.13.1",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
|
||||
"integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.91.1",
|
||||
"@aws-sdk/client-route-53": "^3.1104.0",
|
||||
"@better-auth/cli": "^1.4.10",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue