mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
* feat: add trigger_history table with source tracking Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: gate trigger history reads on scopes and harden its writers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: filter trigger history scopes in SQL and match the cleared-handler diff Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: record a trigger restore from the trashbin in its history Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: record bulk http trigger creates and document the recording boundary Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: lock the trigger row when capturing its history preimage Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: only record an auto-disable that actually flipped the schedule Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: state the auto-disable invariant once instead of at four call sites Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: render trigger history changes as a structured field diff Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: make a server-initiated disable atomic with its history row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: note that the auto-disable savepoint takes no pool connection Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: note the flow fallback is the last chance to disable Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: never leave a trigger enabled because its history row failed Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: retry the disable history row instead of dropping it on first failure Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: use the design-system Button for the change-value expander Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: hold the trigger row lock across its disable history row Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the history-loss alert out of the listener cancellation race Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: read the history workspace through the trigger-workspace seam Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
66 lines
3.0 KiB
SQL
66 lines
3.0 KiB
SQL
-- Append-only record of every schedule/trigger mutation: who, what changed, and
|
|
-- from which kind of client.
|
|
CREATE TABLE IF NOT EXISTS trigger_history (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
|
-- 'schedule' or a trigger's TRIGGER_TYPE ('http', 'kafka', ...). Not the
|
|
-- TRIGGER_KIND enum: that one is capture-oriented and misses 'schedule'.
|
|
trigger_kind VARCHAR(50) NOT NULL,
|
|
path VARCHAR(255) NOT NULL,
|
|
-- 'create' | 'update' | 'delete' | 'enable' | 'disable' | 'suspend'
|
|
operation VARCHAR(20) NOT NULL,
|
|
-- 'ui' | 'cli' | 'api' | 'worker'
|
|
source VARCHAR(20) NOT NULL,
|
|
-- NULL when the server acted on its own (worker auto-disable).
|
|
username VARCHAR(255),
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
-- {field: {old, new}} for the fields that actually changed. `old` is
|
|
-- absent where it is not known: a create, and the workspace-wide handler
|
|
-- override that rewrites every schedule without reading them first. NULL
|
|
-- when the operation carries no field-level diff at all (delete).
|
|
changes JSONB
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_trigger_history_workspace_kind_path
|
|
ON trigger_history(workspace_id, trigger_kind, path, id DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_trigger_history_workspace_id
|
|
ON trigger_history(workspace_id, id DESC);
|
|
|
|
GRANT ALL ON TABLE trigger_history TO windmill_user;
|
|
GRANT ALL ON TABLE trigger_history TO windmill_admin;
|
|
GRANT ALL ON SEQUENCE trigger_history_id_seq TO windmill_user;
|
|
GRANT ALL ON SEQUENCE trigger_history_id_seq TO windmill_admin;
|
|
|
|
ALTER TABLE trigger_history ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY admin_all ON trigger_history FOR ALL TO windmill_admin USING (true) WITH CHECK (true);
|
|
|
|
-- Every mutating trigger route writes through the RLS pool, so windmill_user
|
|
-- must be able to append.
|
|
CREATE POLICY allow_insert ON trigger_history FOR INSERT TO windmill_user WITH CHECK (true);
|
|
|
|
-- Reads mirror the path half of the live trigger's own policies: a row can
|
|
-- quote a schedule's `args`, so it must not be readable by anyone who could not
|
|
-- read the trigger it describes. Deliberately narrower than the live row on one
|
|
-- point — the `extra_perms` grants have no counterpart here, since the history
|
|
-- does not carry the row's ACL and must survive its deletion. Narrower is the
|
|
-- safe direction.
|
|
CREATE POLICY see_own ON trigger_history FOR SELECT TO windmill_user
|
|
USING (
|
|
SPLIT_PART(path::text, '/', 1) = 'u'
|
|
AND SPLIT_PART(path::text, '/', 2) = current_setting('session.user')
|
|
);
|
|
|
|
CREATE POLICY see_member ON trigger_history FOR SELECT TO windmill_user
|
|
USING (
|
|
SPLIT_PART(path::text, '/', 1) = 'g'
|
|
AND SPLIT_PART(path::text, '/', 2) = ANY(regexp_split_to_array(current_setting('session.groups'), ','))
|
|
);
|
|
|
|
CREATE POLICY see_folder_extra_perms_user ON trigger_history FOR SELECT TO windmill_user
|
|
USING (
|
|
SPLIT_PART(path::text, '/', 1) = 'f'
|
|
AND SPLIT_PART(path::text, '/', 2) = ANY(regexp_split_to_array(current_setting('session.folders_read'), ','))
|
|
);
|