jat-feedback 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -25
- package/dist/jat-feedback.js +42 -37
- package/dist/jat-feedback.mjs +3155 -2987
- package/package.json +3 -2
- package/routes/README.md +45 -0
- package/routes/tasks/+page.server.ts +45 -0
- package/routes/tasks/+page.svelte +513 -0
- package/supabase/functions/jat-webhook/index.ts +53 -22
- package/supabase/migrations/3.0.0_rename_to_project_tasks.sql +170 -0
|
@@ -4,6 +4,12 @@ import { createClient } from "https://esm.sh/@supabase/supabase-js@2"
|
|
|
4
4
|
const supabaseUrl = Deno.env.get("SUPABASE_URL")!
|
|
5
5
|
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
|
|
6
6
|
|
|
7
|
+
// JAT sends the project's JWT service role key as the Bearer token.
|
|
8
|
+
// On newer Supabase projects, SUPABASE_SERVICE_ROLE_KEY in the runtime
|
|
9
|
+
// may use the sb_secret_ format instead of the JWT format. JAT_WEBHOOK_SECRET
|
|
10
|
+
// is a custom secret set to the JWT service role key for those projects.
|
|
11
|
+
const webhookSecret = Deno.env.get("JAT_WEBHOOK_SECRET") || supabaseServiceKey
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* JAT Webhook — generic status-sync endpoint.
|
|
9
15
|
*
|
|
@@ -12,24 +18,34 @@ const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
|
|
|
12
18
|
* `supabase/functions/jat-webhook/` directory and deploy it.
|
|
13
19
|
*
|
|
14
20
|
* Deploy:
|
|
15
|
-
* supabase functions deploy jat-webhook
|
|
21
|
+
* supabase functions deploy jat-webhook --no-verify-jwt
|
|
22
|
+
*
|
|
23
|
+
* Setup (if auth fails with "Invalid authorization"):
|
|
24
|
+
* Your project's SUPABASE_SERVICE_ROLE_KEY runtime var may use the
|
|
25
|
+
* new sb_secret_ format. Set JAT_WEBHOOK_SECRET to the JWT service
|
|
26
|
+
* role key from your project's API settings:
|
|
16
27
|
*
|
|
17
|
-
*
|
|
28
|
+
* supabase secrets set JAT_WEBHOOK_SECRET="eyJhbG..."
|
|
29
|
+
*
|
|
30
|
+
* Expected payload:
|
|
18
31
|
* {
|
|
19
32
|
* source: "jat",
|
|
20
33
|
* event: "status_changed" | "task_closed",
|
|
21
|
-
* reference_table: "
|
|
34
|
+
* reference_table: "project_tasks",
|
|
22
35
|
* reference_id: "uuid-of-row",
|
|
23
36
|
* data: {
|
|
24
37
|
* status: "in_progress" | "completed" | ...,
|
|
25
38
|
* task_id: "myproject-abc",
|
|
26
|
-
* notes?: "optional developer note"
|
|
39
|
+
* notes?: "optional developer note",
|
|
40
|
+
* issue_type?: "bug" | "feature" | "task" | "epic",
|
|
41
|
+
* assignee?: "agent or user name",
|
|
42
|
+
* labels?: ["label1", "label2"],
|
|
43
|
+
* due_date?: "2026-04-01T00:00:00Z",
|
|
44
|
+
* source?: "jat"
|
|
27
45
|
* }
|
|
28
46
|
* }
|
|
29
47
|
*
|
|
30
|
-
* Auth: Bearer token must match
|
|
31
|
-
* SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are injected automatically
|
|
32
|
-
* by Supabase when the function runs — no configuration needed.
|
|
48
|
+
* Auth: Bearer token must match JAT_WEBHOOK_SECRET or SUPABASE_SERVICE_ROLE_KEY.
|
|
33
49
|
*
|
|
34
50
|
* integrations.json callback config:
|
|
35
51
|
* {
|
|
@@ -41,7 +57,7 @@ const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
|
|
|
41
57
|
* "in_progress": "in_progress",
|
|
42
58
|
* "closed": "completed"
|
|
43
59
|
* },
|
|
44
|
-
* "referenceTable": "
|
|
60
|
+
* "referenceTable": "project_tasks",
|
|
45
61
|
* "referenceIdFrom": "item_id"
|
|
46
62
|
* }
|
|
47
63
|
* }
|
|
@@ -56,12 +72,19 @@ interface WebhookPayload {
|
|
|
56
72
|
status?: string
|
|
57
73
|
task_id?: string
|
|
58
74
|
notes?: string
|
|
75
|
+
issue_type?: string
|
|
76
|
+
assignee?: string
|
|
77
|
+
labels?: string[]
|
|
78
|
+
due_date?: string
|
|
79
|
+
source?: string
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
// Tables and the columns that JAT is allowed to update.
|
|
63
84
|
// Add more tables here if you want JAT to sync status for other record types.
|
|
85
|
+
// "feedback_reports" kept as alias for backward compat with pre-v3 callers.
|
|
64
86
|
const TABLE_CONFIG: Record<string, { statusCol: string; taskIdCol: string }> = {
|
|
87
|
+
project_tasks: { statusCol: "status", taskIdCol: "jat_task_id" },
|
|
65
88
|
feedback_reports: { statusCol: "status", taskIdCol: "jat_task_id" },
|
|
66
89
|
}
|
|
67
90
|
|
|
@@ -82,7 +105,7 @@ Deno.serve(async (req) => {
|
|
|
82
105
|
})
|
|
83
106
|
}
|
|
84
107
|
const token = authHeader.slice(7)
|
|
85
|
-
if (token !== supabaseServiceKey) {
|
|
108
|
+
if (token !== webhookSecret && token !== supabaseServiceKey) {
|
|
86
109
|
return new Response(JSON.stringify({ error: "Invalid authorization" }), {
|
|
87
110
|
status: 403,
|
|
88
111
|
headers: { "Content-Type": "application/json" },
|
|
@@ -116,11 +139,19 @@ Deno.serve(async (req) => {
|
|
|
116
139
|
)
|
|
117
140
|
}
|
|
118
141
|
|
|
142
|
+
// Resolve actual table name — "feedback_reports" alias maps to "project_tasks"
|
|
143
|
+
const actualTable = reference_table === "feedback_reports" ? "project_tasks" : reference_table
|
|
144
|
+
|
|
119
145
|
// Build the update object from the payload fields
|
|
120
146
|
const update: Record<string, unknown> = {}
|
|
121
147
|
if (data.status) update[config.statusCol] = data.status
|
|
122
148
|
if (data.task_id) update[config.taskIdCol] = data.task_id
|
|
123
149
|
if (data.notes) update.dev_notes = data.notes
|
|
150
|
+
if (data.issue_type) update.issue_type = data.issue_type
|
|
151
|
+
if (data.assignee) update.assignee = data.assignee
|
|
152
|
+
if (data.labels) update.labels = data.labels
|
|
153
|
+
if (data.due_date) update.due_date = data.due_date
|
|
154
|
+
if (data.source) update.source = data.source
|
|
124
155
|
|
|
125
156
|
if (Object.keys(update).length === 0) {
|
|
126
157
|
return new Response(
|
|
@@ -130,30 +161,30 @@ Deno.serve(async (req) => {
|
|
|
130
161
|
}
|
|
131
162
|
|
|
132
163
|
const supabase = createClient(supabaseUrl, supabaseServiceKey)
|
|
133
|
-
const
|
|
134
|
-
.from(
|
|
164
|
+
const result = await supabase
|
|
165
|
+
.from(actualTable)
|
|
135
166
|
.update(update)
|
|
136
167
|
.eq("id", reference_id)
|
|
137
168
|
.select("id")
|
|
138
169
|
|
|
139
|
-
if (error) {
|
|
140
|
-
console.error(`JAT webhook failed: ${error.message}`, {
|
|
141
|
-
|
|
170
|
+
if (result.error) {
|
|
171
|
+
console.error(`JAT webhook failed: ${result.error.message}`, {
|
|
172
|
+
table: actualTable,
|
|
142
173
|
reference_id,
|
|
143
174
|
update,
|
|
144
175
|
})
|
|
145
176
|
return new Response(
|
|
146
|
-
JSON.stringify({ error: `Update failed: ${error.message}` }),
|
|
177
|
+
JSON.stringify({ error: `Update failed: ${result.error.message}` }),
|
|
147
178
|
{ status: 500, headers: { "Content-Type": "application/json" } },
|
|
148
179
|
)
|
|
149
180
|
}
|
|
150
181
|
|
|
151
|
-
if (!data || data.length === 0) {
|
|
152
|
-
console.warn(`JAT webhook: no rows matched ${
|
|
182
|
+
if (!result.data || result.data.length === 0) {
|
|
183
|
+
console.warn(`JAT webhook: no rows matched ${actualTable}[${reference_id}]`, update)
|
|
153
184
|
return new Response(
|
|
154
185
|
JSON.stringify({
|
|
155
|
-
error: `No rows matched: ${
|
|
156
|
-
table:
|
|
186
|
+
error: `No rows matched: ${actualTable} id=${reference_id}`,
|
|
187
|
+
table: actualTable,
|
|
157
188
|
id: reference_id,
|
|
158
189
|
rowsAffected: 0,
|
|
159
190
|
}),
|
|
@@ -161,15 +192,15 @@ Deno.serve(async (req) => {
|
|
|
161
192
|
)
|
|
162
193
|
}
|
|
163
194
|
|
|
164
|
-
console.log(`JAT webhook: ${event} → ${
|
|
195
|
+
console.log(`JAT webhook: ${event} → ${actualTable}[${reference_id}] (${result.data.length} row(s))`, update)
|
|
165
196
|
|
|
166
197
|
return new Response(
|
|
167
198
|
JSON.stringify({
|
|
168
199
|
success: true,
|
|
169
|
-
table:
|
|
200
|
+
table: actualTable,
|
|
170
201
|
id: reference_id,
|
|
171
202
|
updated: update,
|
|
172
|
-
rowsAffected: data.length,
|
|
203
|
+
rowsAffected: result.data.length,
|
|
173
204
|
}),
|
|
174
205
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
175
206
|
)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
-- jat-feedback v3.0.0 — rename feedback_reports → project_tasks + extend schema
|
|
2
|
+
--
|
|
3
|
+
-- BREAKING CHANGE: This migration renames the core table and adds new columns
|
|
4
|
+
-- for unified task management (feedback + JAT tasks + manual entries).
|
|
5
|
+
--
|
|
6
|
+
-- Apply as a new migration in your project:
|
|
7
|
+
-- cp node_modules/jat-feedback/supabase/migrations/3.0.0_rename_to_project_tasks.sql \
|
|
8
|
+
-- supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_3_0_0.sql
|
|
9
|
+
-- supabase db push
|
|
10
|
+
--
|
|
11
|
+
-- Prerequisites: 1.0.0, 1.1.0, 1.8.0 migrations must already be applied.
|
|
12
|
+
|
|
13
|
+
-- ============================================================================
|
|
14
|
+
-- 1. Rename table
|
|
15
|
+
-- ============================================================================
|
|
16
|
+
|
|
17
|
+
ALTER TABLE feedback_reports RENAME TO project_tasks;
|
|
18
|
+
|
|
19
|
+
-- Rename type → source_type to avoid confusion with issue_type
|
|
20
|
+
ALTER TABLE project_tasks RENAME COLUMN type TO source_type;
|
|
21
|
+
|
|
22
|
+
-- ============================================================================
|
|
23
|
+
-- 2. Add new columns
|
|
24
|
+
-- ============================================================================
|
|
25
|
+
|
|
26
|
+
-- Source of the task: feedback widget, JAT task sync, or manual creation
|
|
27
|
+
ALTER TABLE project_tasks ADD COLUMN source TEXT DEFAULT 'feedback'
|
|
28
|
+
CHECK (source IN ('feedback', 'jat', 'manual'));
|
|
29
|
+
|
|
30
|
+
-- Issue classification (extends the original bug/enhancement/other type)
|
|
31
|
+
ALTER TABLE project_tasks ADD COLUMN issue_type TEXT DEFAULT 'bug'
|
|
32
|
+
CHECK (issue_type IN ('bug', 'feature', 'task', 'epic'));
|
|
33
|
+
|
|
34
|
+
-- Assignment and scheduling
|
|
35
|
+
ALTER TABLE project_tasks ADD COLUMN assignee TEXT;
|
|
36
|
+
ALTER TABLE project_tasks ADD COLUMN due_date TIMESTAMPTZ;
|
|
37
|
+
|
|
38
|
+
-- Labels for flexible categorization
|
|
39
|
+
ALTER TABLE project_tasks ADD COLUMN labels TEXT[];
|
|
40
|
+
|
|
41
|
+
-- Self-referential parent for hierarchical tasks (epics → children)
|
|
42
|
+
ALTER TABLE project_tasks ADD COLUMN parent_id UUID REFERENCES project_tasks(id);
|
|
43
|
+
|
|
44
|
+
-- Updated timestamp
|
|
45
|
+
ALTER TABLE project_tasks ADD COLUMN updated_at TIMESTAMPTZ DEFAULT now();
|
|
46
|
+
|
|
47
|
+
-- ============================================================================
|
|
48
|
+
-- 3. Auto-update updated_at trigger
|
|
49
|
+
-- ============================================================================
|
|
50
|
+
|
|
51
|
+
CREATE OR REPLACE FUNCTION update_project_tasks_updated_at()
|
|
52
|
+
RETURNS TRIGGER AS $$
|
|
53
|
+
BEGIN
|
|
54
|
+
NEW.updated_at = now();
|
|
55
|
+
RETURN NEW;
|
|
56
|
+
END;
|
|
57
|
+
$$ LANGUAGE plpgsql;
|
|
58
|
+
|
|
59
|
+
CREATE TRIGGER project_tasks_updated_at
|
|
60
|
+
BEFORE UPDATE ON project_tasks
|
|
61
|
+
FOR EACH ROW
|
|
62
|
+
EXECUTE FUNCTION update_project_tasks_updated_at();
|
|
63
|
+
|
|
64
|
+
-- ============================================================================
|
|
65
|
+
-- 4. Create project_tasks_comments table
|
|
66
|
+
-- ============================================================================
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS project_tasks_comments (
|
|
69
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
70
|
+
task_id UUID NOT NULL REFERENCES project_tasks(id) ON DELETE CASCADE,
|
|
71
|
+
author TEXT NOT NULL,
|
|
72
|
+
author_role TEXT DEFAULT 'user',
|
|
73
|
+
text TEXT NOT NULL DEFAULT '',
|
|
74
|
+
created_at TIMESTAMPTZ DEFAULT now()
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE INDEX idx_project_tasks_comments_task
|
|
78
|
+
ON project_tasks_comments(task_id, created_at);
|
|
79
|
+
|
|
80
|
+
-- RLS for comments
|
|
81
|
+
ALTER TABLE project_tasks_comments ENABLE ROW LEVEL SECURITY;
|
|
82
|
+
|
|
83
|
+
CREATE POLICY "Authenticated users can insert comments"
|
|
84
|
+
ON project_tasks_comments FOR INSERT TO authenticated
|
|
85
|
+
WITH CHECK (true);
|
|
86
|
+
|
|
87
|
+
CREATE POLICY "Authenticated users can read comments"
|
|
88
|
+
ON project_tasks_comments FOR SELECT TO authenticated
|
|
89
|
+
USING (true);
|
|
90
|
+
|
|
91
|
+
CREATE POLICY "Service role full access to comments"
|
|
92
|
+
ON project_tasks_comments FOR ALL TO service_role
|
|
93
|
+
USING (true) WITH CHECK (true);
|
|
94
|
+
|
|
95
|
+
-- ============================================================================
|
|
96
|
+
-- 5. Rename indexes (drop old, create new)
|
|
97
|
+
-- ============================================================================
|
|
98
|
+
|
|
99
|
+
-- The table rename does NOT automatically rename indexes.
|
|
100
|
+
DROP INDEX IF EXISTS idx_feedback_reports_new;
|
|
101
|
+
DROP INDEX IF EXISTS idx_feedback_reports_rejected;
|
|
102
|
+
DROP INDEX IF EXISTS idx_feedback_reports_reporter;
|
|
103
|
+
DROP INDEX IF EXISTS idx_feedback_reports_completed;
|
|
104
|
+
|
|
105
|
+
-- Recreate with new names
|
|
106
|
+
CREATE INDEX idx_project_tasks_new
|
|
107
|
+
ON project_tasks(status, jat_task_id)
|
|
108
|
+
WHERE status = 'submitted' AND jat_task_id IS NULL;
|
|
109
|
+
|
|
110
|
+
CREATE INDEX idx_project_tasks_rejected
|
|
111
|
+
ON project_tasks(status)
|
|
112
|
+
WHERE status = 'rejected';
|
|
113
|
+
|
|
114
|
+
CREATE INDEX idx_project_tasks_reporter
|
|
115
|
+
ON project_tasks(reporter_user_id, created_at DESC);
|
|
116
|
+
|
|
117
|
+
CREATE INDEX idx_project_tasks_completed
|
|
118
|
+
ON project_tasks(reporter_user_id, status)
|
|
119
|
+
WHERE status = 'completed';
|
|
120
|
+
|
|
121
|
+
-- ============================================================================
|
|
122
|
+
-- 6. New indexes for extended schema
|
|
123
|
+
-- ============================================================================
|
|
124
|
+
|
|
125
|
+
-- Filter by source (feedback vs jat vs manual)
|
|
126
|
+
CREATE INDEX idx_project_tasks_source
|
|
127
|
+
ON project_tasks(source);
|
|
128
|
+
|
|
129
|
+
-- Parent-child lookups
|
|
130
|
+
CREATE INDEX idx_project_tasks_parent
|
|
131
|
+
ON project_tasks(parent_id)
|
|
132
|
+
WHERE parent_id IS NOT NULL;
|
|
133
|
+
|
|
134
|
+
-- Assignee lookups
|
|
135
|
+
CREATE INDEX idx_project_tasks_assignee
|
|
136
|
+
ON project_tasks(assignee)
|
|
137
|
+
WHERE assignee IS NOT NULL;
|
|
138
|
+
|
|
139
|
+
-- Due date ordering
|
|
140
|
+
CREATE INDEX idx_project_tasks_due_date
|
|
141
|
+
ON project_tasks(due_date)
|
|
142
|
+
WHERE due_date IS NOT NULL;
|
|
143
|
+
|
|
144
|
+
-- ============================================================================
|
|
145
|
+
-- 7. Update RLS policies (rename references from feedback_reports)
|
|
146
|
+
-- ============================================================================
|
|
147
|
+
|
|
148
|
+
-- Drop old policies (they reference the old table name internally)
|
|
149
|
+
DROP POLICY IF EXISTS "Authenticated users can insert feedback" ON project_tasks;
|
|
150
|
+
DROP POLICY IF EXISTS "Users can read own feedback reports" ON project_tasks;
|
|
151
|
+
DROP POLICY IF EXISTS "Users can respond to own completed feedback" ON project_tasks;
|
|
152
|
+
DROP POLICY IF EXISTS "Service role full access to feedback" ON project_tasks;
|
|
153
|
+
|
|
154
|
+
-- Recreate with updated names
|
|
155
|
+
CREATE POLICY "Authenticated users can insert project tasks"
|
|
156
|
+
ON project_tasks FOR INSERT TO authenticated
|
|
157
|
+
WITH CHECK (true);
|
|
158
|
+
|
|
159
|
+
CREATE POLICY "Users can read own project tasks"
|
|
160
|
+
ON project_tasks FOR SELECT TO authenticated
|
|
161
|
+
USING (reporter_user_id = auth.uid());
|
|
162
|
+
|
|
163
|
+
CREATE POLICY "Users can update own project tasks"
|
|
164
|
+
ON project_tasks FOR UPDATE TO authenticated
|
|
165
|
+
USING (reporter_user_id = auth.uid())
|
|
166
|
+
WITH CHECK (reporter_user_id = auth.uid());
|
|
167
|
+
|
|
168
|
+
CREATE POLICY "Service role full access to project tasks"
|
|
169
|
+
ON project_tasks FOR ALL TO service_role
|
|
170
|
+
USING (true) WITH CHECK (true);
|