From 99d64221a045f9ecfa0b6aa953c3be591acaf59f Mon Sep 17 00:00:00 2001 From: root Date: Sun, 31 May 2026 02:18:39 +1000 Subject: [PATCH] =?UTF-8?q?feat(schema):=20003=20=E2=80=94=20resources,=20?= =?UTF-8?q?deps,=20credentials,=20source=5Fdocs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/db/migrations/003_resources.sql | 62 +++++++++++++++++++++++++++++ tests/db/migration_003.test.js | 18 +++++++++ 2 files changed, 80 insertions(+) create mode 100644 lib/db/migrations/003_resources.sql create mode 100644 tests/db/migration_003.test.js diff --git a/lib/db/migrations/003_resources.sql b/lib/db/migrations/003_resources.sql new file mode 100644 index 0000000..491c3cf --- /dev/null +++ b/lib/db/migrations/003_resources.sql @@ -0,0 +1,62 @@ +CREATE TABLE resources ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + space_id uuid NOT NULL REFERENCES spaces(id) ON DELETE CASCADE, + slug text NOT NULL, + name text NOT NULL, + runtime_type text NOT NULL + CHECK (runtime_type IN ('lxc','vm','docker','bare-metal')), + host text, + url text, + version text, + status text NOT NULL DEFAULT 'unknown' + CHECK (status IN ('running','stopped','down','unknown')), + monitoring jsonb NOT NULL DEFAULT '{}'::jsonb, + metadata jsonb NOT NULL DEFAULT '{}'::jsonb, + last_check timestamptz, + maintenance_until timestamptz, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (space_id, slug) +); + +CREATE TABLE resource_dependencies ( + resource_id uuid NOT NULL REFERENCES resources(id) ON DELETE CASCADE, + depends_on uuid NOT NULL REFERENCES resources(id) ON DELETE CASCADE, + kind text, + PRIMARY KEY (resource_id, depends_on), + CHECK (resource_id <> depends_on) +); + +CREATE TABLE resource_credentials ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + resource_id uuid NOT NULL REFERENCES resources(id) ON DELETE CASCADE, + label text NOT NULL, + vault_path text NOT NULL, + kind text, + notes text, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE source_docs ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + resource_id uuid REFERENCES resources(id) ON DELETE CASCADE, + name text NOT NULL, + upstream_url text NOT NULL, + version text, + format text, + sync_source text, + local_path text, + body_text text, + embedding vector(1024), + last_synced timestamptz, + metadata jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX idx_resources_space ON resources(space_id); +CREATE INDEX idx_source_docs_resource ON source_docs(resource_id); +CREATE INDEX idx_source_docs_fts ON source_docs + USING GIN (to_tsvector('english', coalesce(body_text,''))); +CREATE INDEX idx_source_docs_embed ON source_docs + USING hnsw (embedding vector_cosine_ops); diff --git a/tests/db/migration_003.test.js b/tests/db/migration_003.test.js new file mode 100644 index 0000000..b872b82 --- /dev/null +++ b/tests/db/migration_003.test.js @@ -0,0 +1,18 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { resetDb, withClient } from '../helpers/db.js'; +import { migrateUp } from '../../lib/db/migrate.js'; + +describe('migration 003 — resources', () => { + beforeEach(async () => { await resetDb(); await migrateUp(); }); + + it('creates resources, resource_dependencies, resource_credentials, source_docs', async () => { + await withClient(async (c) => { + for (const t of ['resources','resource_dependencies','resource_credentials','source_docs']) { + const { rows } = await c.query( + `SELECT to_regclass('public.' || $1) AS t;`, [t] + ); + expect(rows[0].t).toBe(t); + } + }); + }); +});