-- Migration: Add LinkedIn post insights tables (daily snapshots) -- Description: Stores scraped post stats separately from linkedin_posts CREATE TABLE IF NOT EXISTS linkedin_post_insights_posts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, linkedin_account_id UUID REFERENCES linkedin_accounts(id) ON DELETE SET NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- Identity post_urn TEXT NOT NULL, post_url TEXT, -- Content post_text TEXT, post_date TIMESTAMP WITH TIME ZONE, author_username TEXT, -- Latest known totals (optional convenience) total_reactions INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, comments INTEGER DEFAULT 0, shares INTEGER DEFAULT 0, reactions_breakdown JSONB DEFAULT '{}'::JSONB, -- Raw data snapshot raw_data JSONB, first_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(user_id, post_urn) ); CREATE TABLE IF NOT EXISTS linkedin_post_insights_daily ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, post_id UUID NOT NULL REFERENCES linkedin_post_insights_posts(id) ON DELETE CASCADE, snapshot_date DATE NOT NULL, total_reactions INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, comments INTEGER DEFAULT 0, shares INTEGER DEFAULT 0, reactions_breakdown JSONB DEFAULT '{}'::JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(post_id, snapshot_date) ); CREATE INDEX IF NOT EXISTS idx_post_insights_posts_user_id ON linkedin_post_insights_posts(user_id); CREATE INDEX IF NOT EXISTS idx_post_insights_posts_post_date ON linkedin_post_insights_posts(post_date); CREATE INDEX IF NOT EXISTS idx_post_insights_daily_user_id ON linkedin_post_insights_daily(user_id); CREATE INDEX IF NOT EXISTS idx_post_insights_daily_snapshot_date ON linkedin_post_insights_daily(snapshot_date); -- Triggers for updated_at CREATE OR REPLACE FUNCTION update_linkedin_post_insights_posts_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS update_linkedin_post_insights_posts_updated_at ON linkedin_post_insights_posts; CREATE TRIGGER update_linkedin_post_insights_posts_updated_at BEFORE UPDATE ON linkedin_post_insights_posts FOR EACH ROW EXECUTE FUNCTION update_linkedin_post_insights_posts_updated_at(); CREATE OR REPLACE FUNCTION update_linkedin_post_insights_daily_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS update_linkedin_post_insights_daily_updated_at ON linkedin_post_insights_daily; CREATE TRIGGER update_linkedin_post_insights_daily_updated_at BEFORE UPDATE ON linkedin_post_insights_daily FOR EACH ROW EXECUTE FUNCTION update_linkedin_post_insights_daily_updated_at(); -- Enable RLS ALTER TABLE linkedin_post_insights_posts ENABLE ROW LEVEL SECURITY; ALTER TABLE linkedin_post_insights_daily ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can view own post insights posts" ON linkedin_post_insights_posts FOR SELECT USING (auth.uid() = user_id); CREATE POLICY "Users can insert own post insights posts" ON linkedin_post_insights_posts FOR INSERT WITH CHECK (auth.uid() = user_id); CREATE POLICY "Users can update own post insights posts" ON linkedin_post_insights_posts FOR UPDATE USING (auth.uid() = user_id); CREATE POLICY "Users can delete own post insights posts" ON linkedin_post_insights_posts FOR DELETE USING (auth.uid() = user_id); CREATE POLICY "Users can view own post insights daily" ON linkedin_post_insights_daily FOR SELECT USING (auth.uid() = user_id); CREATE POLICY "Users can insert own post insights daily" ON linkedin_post_insights_daily FOR INSERT WITH CHECK (auth.uid() = user_id); CREATE POLICY "Users can update own post insights daily" ON linkedin_post_insights_daily FOR UPDATE USING (auth.uid() = user_id); CREATE POLICY "Users can delete own post insights daily" ON linkedin_post_insights_daily FOR DELETE USING (auth.uid() = user_id);