Skip to content

Commit fe68879

Browse files
Merge pull request #29 from vincentgrobler/feature/ticket-5.1-marketplace-schema
feat(ticket-5.1): marketplace DB schema
2 parents 4399d51 + 9d3ac2f commit fe68879

2 files changed

Lines changed: 162 additions & 1 deletion

File tree

src/types/index.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface Agent {
5555
description: string
5656
avatar_url: string | null
5757
model: string
58+
provider: string | null
5859
system_prompt: string
5960
temperature: number
6061
tools: string[]
@@ -63,6 +64,11 @@ export interface Agent {
6364
output_template_id: string | null
6465
status: AgentStatus
6566
config: Record<string, unknown>
67+
// Marketplace fields
68+
is_published: boolean
69+
marketplace_tags: string[]
70+
install_count: number
71+
rating_avg: number
6672
created_at: string
6773
updated_at: string
6874
}
@@ -338,7 +344,32 @@ export interface AuditLog {
338344

339345
export type ProviderBillingModel = 'per-token' | 'subscription-quota' | 'unknown'
340346

341-
// ─── Database row types (for Supabase typed client) ──────────────────────────
347+
// ─── Agent Install ───────────────────────────────────────────────────────────────────
348+
349+
export interface AgentInstall {
350+
id: string
351+
agent_id: string
352+
workspace_id: string
353+
installed_by: string | null
354+
source_workspace_id: string | null
355+
cloned_agent_id: string | null
356+
installed_at: string
357+
}
358+
359+
// ─── Agent Review ────────────────────────────────────────────────────────────────────
360+
361+
export interface AgentReview {
362+
id: string
363+
agent_id: string
364+
workspace_id: string
365+
user_id: string
366+
rating: number
367+
review_text: string
368+
created_at: string
369+
updated_at: string
370+
}
371+
372+
// ─── Database row types (for Supabase typed client) ──────────────────────
342373

343374
export type WorkspaceRow = Workspace
344375
export type WorkspaceMemberRow = WorkspaceMember
@@ -357,3 +388,5 @@ export type VoiceProfileRow = VoiceProfile
357388
export type OutputTemplateRow = OutputTemplate
358389
export type UsageRecordRow = UsageRecord
359390
export type AuditLogRow = AuditLog
391+
export type AgentInstallRow = AgentInstall
392+
export type AgentReviewRow = AgentReview
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
-- SPDX-License-Identifier: AGPL-3.0-or-later
2+
-- Copyright (C) 2026 CrewForm
3+
--
4+
-- 014_marketplace_schema.sql — Marketplace columns on agents + installs & reviews
5+
6+
-- ────────────────────────────────────────────────────────────────────────────
7+
-- ALTER agents — add marketplace columns
8+
-- ────────────────────────────────────────────────────────────────────────────
9+
10+
ALTER TABLE public.agents
11+
ADD COLUMN IF NOT EXISTS is_published BOOLEAN NOT NULL DEFAULT false,
12+
ADD COLUMN IF NOT EXISTS marketplace_tags TEXT[] NOT NULL DEFAULT '{}',
13+
ADD COLUMN IF NOT EXISTS install_count INTEGER NOT NULL DEFAULT 0,
14+
ADD COLUMN IF NOT EXISTS rating_avg NUMERIC(2,1) NOT NULL DEFAULT 0,
15+
ADD COLUMN IF NOT EXISTS provider TEXT;
16+
17+
CREATE INDEX IF NOT EXISTS idx_agents_published
18+
ON public.agents(is_published) WHERE is_published = true;
19+
20+
CREATE INDEX IF NOT EXISTS idx_agents_marketplace_tags
21+
ON public.agents USING GIN(marketplace_tags);
22+
23+
-- ────────────────────────────────────────────────────────────────────────────
24+
-- agent_installs — tracks who installed which marketplace agent
25+
-- ────────────────────────────────────────────────────────────────────────────
26+
27+
CREATE TABLE public.agent_installs (
28+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
29+
agent_id UUID NOT NULL REFERENCES public.agents(id) ON DELETE CASCADE,
30+
workspace_id UUID NOT NULL REFERENCES public.workspaces(id) ON DELETE CASCADE,
31+
installed_by UUID REFERENCES auth.users(id) ON DELETE SET NULL,
32+
source_workspace_id UUID REFERENCES public.workspaces(id) ON DELETE SET NULL,
33+
cloned_agent_id UUID REFERENCES public.agents(id) ON DELETE SET NULL,
34+
installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
35+
);
36+
37+
CREATE INDEX idx_agent_installs_agent ON public.agent_installs(agent_id);
38+
CREATE INDEX idx_agent_installs_workspace ON public.agent_installs(workspace_id);
39+
40+
ALTER TABLE public.agent_installs ENABLE ROW LEVEL SECURITY;
41+
42+
CREATE POLICY "agent_installs_select" ON public.agent_installs
43+
FOR SELECT USING (
44+
workspace_id IN (SELECT workspace_id FROM public.workspace_members WHERE user_id = auth.uid())
45+
);
46+
47+
CREATE POLICY "agent_installs_insert" ON public.agent_installs
48+
FOR INSERT WITH CHECK (
49+
workspace_id IN (SELECT workspace_id FROM public.workspace_members WHERE user_id = auth.uid())
50+
);
51+
52+
-- ────────────────────────────────────────────────────────────────────────────
53+
-- agent_reviews — marketplace reviews with 1–5 rating
54+
-- ────────────────────────────────────────────────────────────────────────────
55+
56+
CREATE TABLE public.agent_reviews (
57+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
58+
agent_id UUID NOT NULL REFERENCES public.agents(id) ON DELETE CASCADE,
59+
workspace_id UUID NOT NULL REFERENCES public.workspaces(id) ON DELETE CASCADE,
60+
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
61+
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
62+
review_text TEXT NOT NULL DEFAULT '',
63+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
64+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
65+
66+
-- One review per user per agent
67+
UNIQUE (agent_id, user_id)
68+
);
69+
70+
CREATE INDEX idx_agent_reviews_agent ON public.agent_reviews(agent_id);
71+
72+
CREATE TRIGGER trg_agent_reviews_updated_at
73+
BEFORE UPDATE ON public.agent_reviews
74+
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
75+
76+
ALTER TABLE public.agent_reviews ENABLE ROW LEVEL SECURITY;
77+
78+
-- Anyone can read published agent reviews
79+
CREATE POLICY "agent_reviews_select" ON public.agent_reviews
80+
FOR SELECT USING (true);
81+
82+
CREATE POLICY "agent_reviews_insert" ON public.agent_reviews
83+
FOR INSERT WITH CHECK (user_id = auth.uid());
84+
85+
CREATE POLICY "agent_reviews_update" ON public.agent_reviews
86+
FOR UPDATE USING (user_id = auth.uid());
87+
88+
CREATE POLICY "agent_reviews_delete" ON public.agent_reviews
89+
FOR DELETE USING (user_id = auth.uid());
90+
91+
-- ────────────────────────────────────────────────────────────────────────────
92+
-- Trigger: auto-update agents.rating_avg on review changes
93+
-- ────────────────────────────────────────────────────────────────────────────
94+
95+
CREATE OR REPLACE FUNCTION public.update_agent_rating()
96+
RETURNS TRIGGER AS $$
97+
DECLARE
98+
target_agent_id UUID;
99+
avg_rating NUMERIC(2,1);
100+
BEGIN
101+
-- Determine which agent to update
102+
IF TG_OP = 'DELETE' THEN
103+
target_agent_id := OLD.agent_id;
104+
ELSE
105+
target_agent_id := NEW.agent_id;
106+
END IF;
107+
108+
-- Calculate new average
109+
SELECT COALESCE(ROUND(AVG(rating)::NUMERIC, 1), 0)
110+
INTO avg_rating
111+
FROM public.agent_reviews
112+
WHERE agent_id = target_agent_id;
113+
114+
-- Update agent
115+
UPDATE public.agents
116+
SET rating_avg = avg_rating
117+
WHERE id = target_agent_id;
118+
119+
IF TG_OP = 'DELETE' THEN
120+
RETURN OLD;
121+
END IF;
122+
RETURN NEW;
123+
END;
124+
$$ LANGUAGE plpgsql SECURITY DEFINER;
125+
126+
CREATE TRIGGER trg_agent_reviews_rating
127+
AFTER INSERT OR UPDATE OR DELETE ON public.agent_reviews
128+
FOR EACH ROW EXECUTE FUNCTION public.update_agent_rating();

0 commit comments

Comments
 (0)