@oktis-works/database 0.7.0 → 0.8.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.
@@ -536,6 +536,34 @@ CREATE TABLE IF NOT EXISTS webhooks (
536
536
  -- mantidos para que instalações existentes migrem sem perder seus segredos.
537
537
  ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS auth_type VARCHAR(30) NOT NULL DEFAULT 'hmac_sha256';
538
538
  ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS auth_config JSONB NOT NULL DEFAULT '{}'::jsonb;
539
+
540
+ -- ============================================================
541
+ -- Webhook hardening (Item 19 — rotação de segredos + dead-letter)
542
+ -- ============================================================
543
+ CREATE TABLE IF NOT EXISTS webhook_secrets (
544
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
545
+ webhook_id UUID NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
546
+ secret VARCHAR(255) NOT NULL,
547
+ version INT NOT NULL DEFAULT 1,
548
+ is_active BOOLEAN NOT NULL DEFAULT true,
549
+ rotated_at TIMESTAMPTZ,
550
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
551
+ UNIQUE(webhook_id, version)
552
+ );
553
+
554
+ CREATE INDEX IF NOT EXISTS idx_webhook_secrets_webhook ON webhook_secrets(webhook_id, is_active);
555
+
556
+ CREATE TABLE IF NOT EXISTS webhook_dead_letters (
557
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
558
+ webhook_id UUID NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
559
+ event VARCHAR(191) NOT NULL,
560
+ payload JSONB NOT NULL,
561
+ error TEXT,
562
+ attempts INT NOT NULL DEFAULT 1,
563
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
564
+ );
565
+
566
+ CREATE INDEX IF NOT EXISTS idx_webhook_dead_letters_webhook ON webhook_dead_letters(webhook_id, created_at);
539
567
  UPDATE webhooks
540
568
  SET auth_config = jsonb_build_object('secret', secret)
541
569
  WHERE auth_type = 'hmac_sha256' AND auth_config = '{}'::jsonb AND secret <> '';
@@ -779,6 +807,103 @@ CREATE TABLE IF NOT EXISTS content_taxonomy_terms (
779
807
  CREATE INDEX IF NOT EXISTS idx_content_terms_content ON content_taxonomy_terms(content_id);
780
808
  CREATE INDEX IF NOT EXISTS idx_content_terms_term ON content_taxonomy_terms(term_id);
781
809
 
810
+ -- ============================================================
811
+ -- Comments (Item 11 — paridade WordPress: comentários moderados, thread)
812
+ -- ============================================================
813
+ CREATE TABLE IF NOT EXISTS comments (
814
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
815
+ tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
816
+ content_id UUID NOT NULL REFERENCES content(id) ON DELETE CASCADE,
817
+ parent_id UUID REFERENCES comments(id) ON DELETE CASCADE,
818
+ author_name VARCHAR(255) NOT NULL,
819
+ author_email VARCHAR(255),
820
+ author_url VARCHAR(500),
821
+ author_id UUID REFERENCES users(id) ON DELETE SET NULL,
822
+ body TEXT NOT NULL,
823
+ status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
824
+ ip_address VARCHAR(64),
825
+ user_agent VARCHAR(500),
826
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
827
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
828
+ );
829
+
830
+ CREATE INDEX IF NOT EXISTS idx_comments_content_status ON comments(content_id, status);
831
+ CREATE INDEX IF NOT EXISTS idx_comments_tenant_status ON comments(tenant_id, status);
832
+ CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments(parent_id);
833
+
834
+ CREATE TABLE IF NOT EXISTS comment_meta (
835
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
836
+ comment_id UUID NOT NULL REFERENCES comments(id) ON DELETE CASCADE,
837
+ key VARCHAR(191) NOT NULL,
838
+ value JSONB,
839
+ UNIQUE(comment_id, key)
840
+ );
841
+
842
+ ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
843
+ ALTER TABLE comments FORCE ROW LEVEL SECURITY;
844
+ DO $$
845
+ BEGIN
846
+ IF NOT EXISTS (
847
+ SELECT 1 FROM pg_policies
848
+ WHERE policyname = 'tenant_isolation_comments' AND tablename = 'comments'
849
+ ) THEN
850
+ CREATE POLICY tenant_isolation_comments ON comments
851
+ USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid OR tenant_id IS NULL);
852
+ END IF;
853
+ END $$;
854
+
855
+ ALTER TABLE comment_meta ENABLE ROW LEVEL SECURITY;
856
+ ALTER TABLE comment_meta FORCE ROW LEVEL SECURITY;
857
+ DO $$
858
+ BEGIN
859
+ IF NOT EXISTS (
860
+ SELECT 1 FROM pg_policies
861
+ WHERE policyname = 'tenant_isolation_comment_meta' AND tablename = 'comment_meta'
862
+ ) THEN
863
+ CREATE POLICY tenant_isolation_comment_meta ON comment_meta
864
+ USING (EXISTS (
865
+ SELECT 1 FROM comments c
866
+ WHERE c.id = comment_meta.comment_id
867
+ AND (c.tenant_id = current_setting('app.current_tenant_id', true)::uuid OR c.tenant_id IS NULL)
868
+ ));
869
+ END IF;
870
+ END $$;
871
+
872
+ -- ============================================================
873
+ -- Mails (Item 12 — paridade WordPress: outbox durável para api.mail.send)
874
+ -- ============================================================
875
+ CREATE TABLE IF NOT EXISTS mails (
876
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
877
+ tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
878
+ plugin_name VARCHAR(255) NOT NULL,
879
+ to_email VARCHAR(320) NOT NULL,
880
+ subject VARCHAR(998) NOT NULL,
881
+ html TEXT,
882
+ text TEXT,
883
+ template VARCHAR(191),
884
+ params JSONB,
885
+ status VARCHAR(20) NOT NULL DEFAULT 'QUEUED',
886
+ attempts INT NOT NULL DEFAULT 0,
887
+ error TEXT,
888
+ sent_at TIMESTAMPTZ,
889
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
890
+ );
891
+
892
+ CREATE INDEX IF NOT EXISTS idx_mails_tenant_status ON mails(tenant_id, status);
893
+
894
+ ALTER TABLE mails ENABLE ROW LEVEL SECURITY;
895
+ ALTER TABLE mails FORCE ROW LEVEL SECURITY;
896
+ DO $$
897
+ BEGIN
898
+ IF NOT EXISTS (
899
+ SELECT 1 FROM pg_policies
900
+ WHERE policyname = 'tenant_isolation_mails' AND tablename = 'mails'
901
+ ) THEN
902
+ CREATE POLICY tenant_isolation_mails ON mails
903
+ USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid OR tenant_id IS NULL);
904
+ END IF;
905
+ END $$;
906
+
782
907
  ALTER TABLE taxonomy_terms ENABLE ROW LEVEL SECURITY;
783
908
  ALTER TABLE taxonomy_terms FORCE ROW LEVEL SECURITY;
784
909
  ALTER TABLE content_taxonomy_terms ENABLE ROW LEVEL SECURITY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oktis-works/database",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,15 +12,15 @@
12
12
  }
13
13
  },
14
14
  "scripts": {
15
- "build": "tsc && cp src/schema/schema.sql dist/schema/schema.sql",
15
+ "build": "tsc && mkdir -p dist/schema && cp src/schema/schema.sql dist/schema/schema.sql",
16
16
  "dev": "tsc --watch",
17
17
  "clean": "rm -rf dist tsconfig.tsbuildinfo",
18
18
  "db:migrate": "node --loader ts-node/esm src/migrations/runner.ts",
19
19
  "db:rollback": "node --loader ts-node/esm src/migrations/runner.ts rollback"
20
20
  },
21
21
  "dependencies": {
22
- "@oktis-works/types": "^0.7.0",
23
- "@oktis-works/config": "^0.6.2",
22
+ "@oktis-works/types": "^0.9.0",
23
+ "@oktis-works/config": "^0.6.4",
24
24
  "postgres": "3.4.0"
25
25
  },
26
26
  "devDependencies": {