Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot] 2026-04-17 17:00:42 +00:00
parent 5f2790ee76
commit 5bbc5d2574
2 changed files with 461 additions and 1 deletions

View file

@ -14,7 +14,257 @@ export type Database = {
} }
public: { public: {
Tables: { Tables: {
[_ in never]: never agent_instances: {
Row: {
container_name: string | null
created_at: string
id: string
status: string
telegram_bot_token_vault_id: string | null
telegram_bot_username: string | null
updated_at: string
user_id: string
uuid_tenant: string
vps_host: string | null
}
Insert: {
container_name?: string | null
created_at?: string
id?: string
status?: string
telegram_bot_token_vault_id?: string | null
telegram_bot_username?: string | null
updated_at?: string
user_id: string
uuid_tenant?: string
vps_host?: string | null
}
Update: {
container_name?: string | null
created_at?: string
id?: string
status?: string
telegram_bot_token_vault_id?: string | null
telegram_bot_username?: string | null
updated_at?: string
user_id?: string
uuid_tenant?: string
vps_host?: string | null
}
Relationships: [
{
foreignKeyName: "agent_instances_user_id_fkey"
columns: ["user_id"]
isOneToOne: true
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
enterprise_leads: {
Row: {
company_name: string
contact_name: string
created_at: string
email: string
id: string
message: string | null
phone: string | null
status: string
team_size: string
}
Insert: {
company_name: string
contact_name: string
created_at?: string
email: string
id?: string
message?: string | null
phone?: string | null
status?: string
team_size: string
}
Update: {
company_name?: string
contact_name?: string
created_at?: string
email?: string
id?: string
message?: string | null
phone?: string | null
status?: string
team_size?: string
}
Relationships: []
}
plans: {
Row: {
created_at: string
description: string | null
display_order: number
features: Json
highlighted: boolean
id: string
is_enterprise: boolean
name: string
price_monthly_brl: number | null
price_yearly_brl: number | null
slug: string
stripe_price_id_monthly: string | null
stripe_price_id_yearly: string | null
}
Insert: {
created_at?: string
description?: string | null
display_order?: number
features?: Json
highlighted?: boolean
id?: string
is_enterprise?: boolean
name: string
price_monthly_brl?: number | null
price_yearly_brl?: number | null
slug: string
stripe_price_id_monthly?: string | null
stripe_price_id_yearly?: string | null
}
Update: {
created_at?: string
description?: string | null
display_order?: number
features?: Json
highlighted?: boolean
id?: string
is_enterprise?: boolean
name?: string
price_monthly_brl?: number | null
price_yearly_brl?: number | null
slug?: string
stripe_price_id_monthly?: string | null
stripe_price_id_yearly?: string | null
}
Relationships: []
}
profiles: {
Row: {
avatar_url: string | null
company_name: string | null
cpf_cnpj: string | null
created_at: string
full_name: string
id: string
onboarding_completed: boolean
phone: string | null
stripe_customer_id: string | null
updated_at: string
}
Insert: {
avatar_url?: string | null
company_name?: string | null
cpf_cnpj?: string | null
created_at?: string
full_name?: string
id: string
onboarding_completed?: boolean
phone?: string | null
stripe_customer_id?: string | null
updated_at?: string
}
Update: {
avatar_url?: string | null
company_name?: string | null
cpf_cnpj?: string | null
created_at?: string
full_name?: string
id?: string
onboarding_completed?: boolean
phone?: string | null
stripe_customer_id?: string | null
updated_at?: string
}
Relationships: []
}
stripe_webhook_events: {
Row: {
event_type: string
id: string
payload: Json | null
processed_at: string
stripe_event_id: string
}
Insert: {
event_type: string
id?: string
payload?: Json | null
processed_at?: string
stripe_event_id: string
}
Update: {
event_type?: string
id?: string
payload?: Json | null
processed_at?: string
stripe_event_id?: string
}
Relationships: []
}
subscriptions: {
Row: {
billing_cycle: string
cancel_at_period_end: boolean
created_at: string
current_period_end: string | null
current_period_start: string | null
id: string
plan_id: string | null
status: string
stripe_subscription_id: string | null
updated_at: string
user_id: string
}
Insert: {
billing_cycle: string
cancel_at_period_end?: boolean
created_at?: string
current_period_end?: string | null
current_period_start?: string | null
id?: string
plan_id?: string | null
status: string
stripe_subscription_id?: string | null
updated_at?: string
user_id: string
}
Update: {
billing_cycle?: string
cancel_at_period_end?: boolean
created_at?: string
current_period_end?: string | null
current_period_start?: string | null
id?: string
plan_id?: string | null
status?: string
stripe_subscription_id?: string | null
updated_at?: string
user_id?: string
}
Relationships: [
{
foreignKeyName: "subscriptions_plan_id_fkey"
columns: ["plan_id"]
isOneToOne: false
referencedRelation: "plans"
referencedColumns: ["id"]
},
{
foreignKeyName: "subscriptions_user_id_fkey"
columns: ["user_id"]
isOneToOne: false
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
} }
Views: { Views: {
[_ in never]: never [_ in never]: never

View file

@ -0,0 +1,210 @@
-- ============================================
-- HELPER: trigger para updated_at
-- ============================================
CREATE OR REPLACE FUNCTION public.update_updated_at_column()
RETURNS TRIGGER
LANGUAGE plpgsql
SET search_path = public
AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$;
-- ============================================
-- PROFILES
-- ============================================
CREATE TABLE public.profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
full_name TEXT NOT NULL DEFAULT '',
company_name TEXT,
cpf_cnpj TEXT,
phone TEXT,
avatar_url TEXT,
stripe_customer_id TEXT UNIQUE,
onboarding_completed BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_profiles_stripe_customer_id ON public.profiles(stripe_customer_id);
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Usuários veem o próprio perfil"
ON public.profiles FOR SELECT
USING (auth.uid() = id);
CREATE POLICY "Usuários atualizam o próprio perfil"
ON public.profiles FOR UPDATE
USING (auth.uid() = id);
CREATE POLICY "Usuários inserem o próprio perfil"
ON public.profiles FOR INSERT
WITH CHECK (auth.uid() = id);
CREATE TRIGGER update_profiles_updated_at
BEFORE UPDATE ON public.profiles
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
-- Trigger: cria profile automaticamente no signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
INSERT INTO public.profiles (id, full_name)
VALUES (
NEW.id,
COALESCE(NEW.raw_user_meta_data->>'full_name', '')
);
RETURN NEW;
END;
$$;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- ============================================
-- PLANS
-- ============================================
CREATE TABLE public.plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
price_monthly_brl NUMERIC(10,2),
price_yearly_brl NUMERIC(10,2),
stripe_price_id_monthly TEXT,
stripe_price_id_yearly TEXT,
features JSONB NOT NULL DEFAULT '[]'::jsonb,
highlighted BOOLEAN NOT NULL DEFAULT false,
is_enterprise BOOLEAN NOT NULL DEFAULT false,
display_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Planos são públicos"
ON public.plans FOR SELECT
USING (true);
INSERT INTO public.plans (slug, name, description, price_monthly_brl, price_yearly_brl, features, highlighted, is_enterprise, display_order) VALUES
('basic', 'Basic', 'Para quem está começando a explorar IA pessoal', 69.90, 671.04,
'["Memória persistente básica","Até 500 mensagens/mês","Integração com Telegram","Suporte por e-mail"]'::jsonb,
false, false, 1),
('starter', 'Starter', 'Para profissionais que querem produtividade real', 199.90, 1919.04,
'["Memória persistente avançada","Até 3.000 mensagens/mês","Integração Google Workspace","Skills personalizadas (até 10)","Suporte prioritário"]'::jsonb,
false, false, 2),
('professional', 'Professional', 'Para quem usa IA o dia inteiro', 399.90, 3839.04,
'["Memória persistente ilimitada","Mensagens ilimitadas","Skills ilimitadas","Agendamentos automáticos","Integrações premium","Suporte dedicado em horário comercial"]'::jsonb,
true, false, 3),
('enterprise', 'Enterprise', 'Para times e empresas', NULL, NULL,
'["Tudo do Professional","Múltiplos usuários","SSO e auditoria","SLA garantido","Onboarding personalizado","Gerente de conta dedicado"]'::jsonb,
false, true, 4);
-- ============================================
-- SUBSCRIPTIONS
-- ============================================
CREATE TABLE public.subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
plan_id UUID REFERENCES public.plans(id),
stripe_subscription_id TEXT UNIQUE,
status TEXT NOT NULL CHECK (status IN ('active','trialing','past_due','canceled','incomplete','incomplete_expired','unpaid')),
billing_cycle TEXT NOT NULL CHECK (billing_cycle IN ('monthly','yearly')),
current_period_start TIMESTAMPTZ,
current_period_end TIMESTAMPTZ,
cancel_at_period_end BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_subscriptions_user_id ON public.subscriptions(user_id);
CREATE INDEX idx_subscriptions_stripe_subscription_id ON public.subscriptions(stripe_subscription_id);
CREATE INDEX idx_subscriptions_status ON public.subscriptions(status);
ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Usuários veem a própria assinatura"
ON public.subscriptions FOR SELECT
USING (auth.uid() = user_id);
CREATE TRIGGER update_subscriptions_updated_at
BEFORE UPDATE ON public.subscriptions
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
-- ============================================
-- AGENT INSTANCES
-- ============================================
CREATE TABLE public.agent_instances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID UNIQUE NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'provisioning' CHECK (status IN ('provisioning','active','suspended','error')),
vps_host TEXT,
container_name TEXT,
telegram_bot_token_vault_id UUID,
telegram_bot_username TEXT,
uuid_tenant UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_agent_instances_user_id ON public.agent_instances(user_id);
CREATE INDEX idx_agent_instances_uuid_tenant ON public.agent_instances(uuid_tenant);
CREATE INDEX idx_agent_instances_status ON public.agent_instances(status);
ALTER TABLE public.agent_instances ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Usuários veem o próprio agente"
ON public.agent_instances FOR SELECT
USING (auth.uid() = user_id);
CREATE TRIGGER update_agent_instances_updated_at
BEFORE UPDATE ON public.agent_instances
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
-- ============================================
-- STRIPE WEBHOOK EVENTS (idempotência)
-- ============================================
CREATE TABLE public.stripe_webhook_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
stripe_event_id TEXT UNIQUE NOT NULL,
event_type TEXT NOT NULL,
processed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
payload JSONB
);
CREATE INDEX idx_stripe_webhook_events_event_id ON public.stripe_webhook_events(stripe_event_id);
CREATE INDEX idx_stripe_webhook_events_event_type ON public.stripe_webhook_events(event_type);
ALTER TABLE public.stripe_webhook_events ENABLE ROW LEVEL SECURITY;
-- Sem policies: apenas service_role acessa
-- ============================================
-- ENTERPRISE LEADS
-- ============================================
CREATE TABLE public.enterprise_leads (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_name TEXT NOT NULL,
contact_name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
team_size TEXT NOT NULL CHECK (team_size IN ('1-10','11-50','51-200','200+')),
message TEXT,
status TEXT NOT NULL DEFAULT 'new' CHECK (status IN ('new','contacted','qualified','converted','lost')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE public.enterprise_leads ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Qualquer pessoa pode enviar lead"
ON public.enterprise_leads FOR INSERT
WITH CHECK (true);
-- Leitura: apenas service_role