-- Document locks: track who is currently editing a document -- Uses a heartbeat model: editors must refresh their lock periodically -- Stale locks (no heartbeat for 60s) are considered expired CREATE TABLE document_locks ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, locked_at TIMESTAMPTZ NOT NULL DEFAULT now(), last_heartbeat TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(document_id) ); -- Index for fast lookups CREATE INDEX idx_document_locks_document ON document_locks(document_id); CREATE INDEX idx_document_locks_heartbeat ON document_locks(last_heartbeat); -- RLS ALTER TABLE document_locks ENABLE ROW LEVEL SECURITY; -- Anyone in the org can view locks (to see who's editing) CREATE POLICY "Org members can view document locks" ON document_locks FOR SELECT USING (EXISTS ( SELECT 1 FROM documents d JOIN org_members om ON d.org_id = om.org_id WHERE d.id = document_locks.document_id AND om.user_id = auth.uid() )); -- Users can manage their own locks CREATE POLICY "Users can insert their own locks" ON document_locks FOR INSERT WITH CHECK (user_id = auth.uid()); CREATE POLICY "Users can update their own locks" ON document_locks FOR UPDATE USING (user_id = auth.uid()); CREATE POLICY "Users can delete their own locks" ON document_locks FOR DELETE USING (user_id = auth.uid()); -- Allow taking over expired locks (heartbeat older than 60 seconds) CREATE POLICY "Anyone can delete expired locks" ON document_locks FOR DELETE USING (last_heartbeat < now() - interval '60 seconds');