Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions cloudflare_workers/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,24 @@ import { app as organization } from '../../supabase/functions/_backend/public/or
import { app as replication } from '../../supabase/functions/_backend/public/replication.ts'
import { app as statistics } from '../../supabase/functions/_backend/public/statistics/index.ts'
import { app as webhooks } from '../../supabase/functions/_backend/public/webhooks/index.ts'
import { app as credit_usage_alerts } from '../../supabase/functions/_backend/triggers/credit_usage_alerts.ts'
import { app as cron_clean_orphan_images } from '../../supabase/functions/_backend/triggers/cron_clean_orphan_images.ts'
import { app as cron_clear_versions } from '../../supabase/functions/_backend/triggers/cron_clear_versions.ts'
import { app as cron_email } from '../../supabase/functions/_backend/triggers/cron_email.ts'
import { app as cron_reconcile_build_status } from '../../supabase/functions/_backend/triggers/cron_reconcile_build_status.ts'
import { app as cron_stat_app } from '../../supabase/functions/_backend/triggers/cron_stat_app.ts'
import { app as cron_stat_org } from '../../supabase/functions/_backend/triggers/cron_stat_org.ts'
import { app as cron_sync_sub } from '../../supabase/functions/_backend/triggers/cron_sync_sub.ts'
import { app as logsnag_insights } from '../../supabase/functions/_backend/triggers/logsnag_insights.ts'
import { app as on_app_create } from '../../supabase/functions/_backend/triggers/on_app_create.ts'
import { app as on_app_delete } from '../../supabase/functions/_backend/triggers/on_app_delete.ts'
import { app as on_app_update } from '../../supabase/functions/_backend/triggers/on_app_update.ts'
import { app as on_channel_update } from '../../supabase/functions/_backend/triggers/on_channel_update.ts'
import { app as on_deploy_history_create } from '../../supabase/functions/_backend/triggers/on_deploy_history_create.ts'
import { app as on_manifest_create } from '../../supabase/functions/_backend/triggers/on_manifest_create.ts'
import { app as on_org_update } from '../../supabase/functions/_backend/triggers/on_org_update.ts'
import { app as on_organization_create } from '../../supabase/functions/_backend/triggers/on_organization_create.ts'
import { app as on_organization_delete } from '../../supabase/functions/_backend/triggers/on_organization_delete.ts'
import { app as on_user_create } from '../../supabase/functions/_backend/triggers/on_user_create.ts'
import { app as on_user_delete } from '../../supabase/functions/_backend/triggers/on_user_delete.ts'
import { app as on_user_update } from '../../supabase/functions/_backend/triggers/on_user_update.ts'
Expand All @@ -61,6 +67,8 @@ import { app as on_version_delete } from '../../supabase/functions/_backend/trig
import { app as on_version_update } from '../../supabase/functions/_backend/triggers/on_version_update.ts'
import { app as queue_consumer } from '../../supabase/functions/_backend/triggers/queue_consumer.ts'
import { app as stripe_event } from '../../supabase/functions/_backend/triggers/stripe_event.ts'
import { app as webhook_delivery } from '../../supabase/functions/_backend/triggers/webhook_delivery.ts'
import { app as webhook_dispatcher } from '../../supabase/functions/_backend/triggers/webhook_dispatcher.ts'
import { createAllCatch, createHono } from '../../supabase/functions/_backend/utils/hono.ts'
import { version } from '../../supabase/functions/_backend/utils/version.ts'

Expand Down Expand Up @@ -122,9 +130,15 @@ appTriggers.route('/ok', ok)
appTriggers.route('/cron_email', cron_email)
appTriggers.route('/cron_clear_versions', cron_clear_versions)
appTriggers.route('/cron_clean_orphan_images', cron_clean_orphan_images)
appTriggers.route('/cron_reconcile_build_status', cron_reconcile_build_status)
appTriggers.route('/credit_usage_alerts', credit_usage_alerts)
appTriggers.route('/logsnag_insights', logsnag_insights)
appTriggers.route('/on_channel_update', on_channel_update)
appTriggers.route('/on_app_create', on_app_create)
appTriggers.route('/on_app_delete', on_app_delete)
appTriggers.route('/on_app_update', on_app_update)
appTriggers.route('/on_org_update', on_org_update)
appTriggers.route('/on_organization_delete', on_organization_delete)
appTriggers.route('/on_user_create', on_user_create)
appTriggers.route('/on_user_update', on_user_update)
appTriggers.route('/on_user_delete', on_user_delete)
Expand All @@ -139,6 +153,8 @@ appTriggers.route('/cron_stat_app', cron_stat_app)
appTriggers.route('/cron_stat_org', cron_stat_org)
appTriggers.route('/cron_sync_sub', cron_sync_sub)
appTriggers.route('/queue_consumer', queue_consumer)
appTriggers.route('/webhook_delivery', webhook_delivery)
appTriggers.route('/webhook_dispatcher', webhook_dispatcher)

app.route('/triggers', appTriggers)
app.route('/private', appPrivate)
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
"@logsnag/node": "1.0.1",
"@revenuecat/purchases-capacitor": "11.2.13",
"@std/semver": "npm:@jsr/std__semver@1.0.8",
"@supabase/supabase-js": "2.93.3",
"@supabase/supabase-js": "2.100.1",
"@tailwindcss/forms": "^0.5.11",
"@types/semver": "^7.7.1",
"@vuepic/vue-datepicker": "^12.1.0",
Expand Down Expand Up @@ -210,7 +210,7 @@
"qrcode": "^1.5.4",
"semver": "^7.7.4",
"stripe": "^19.3.1",
"supabase": "^2.78.1",
"supabase": "^2.84.2",
"turndown": "^7.2.2",
"vite-plugin-devtools-json": "^1.0.0",
"vue": "3.5.29",
Expand Down
3 changes: 1 addition & 2 deletions supabase/functions/_backend/private/download_link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ app.post('/', middlewareAuth, async (c) => {
const { data: manifest, error: getManifestError } = await supabase
.from('manifest')
.select('*')
.eq('app_id', body.app_id)
.eq('id', body.id)
.eq('app_version_id', bundle.id)

if (getManifestError) {
throw simpleError('cannot_get_manifest', 'Cannot get manifest', { getManifestError })
Expand Down
8 changes: 6 additions & 2 deletions supabase/functions/_backend/triggers/webhook_delivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ app.post('/', middlewareAPISecret, async (c) => {
try {
const body = await c.req.json()

// Extract delivery data from the queue message
const deliveryData: DeliveryMessage = body.payload || body
// queue_consumer posts the queue payload directly, while direct trigger calls may
// still send the full pgmq envelope. Only unwrap when the delivery envelope is
// not already present on the body.
const deliveryData: DeliveryMessage = body?.delivery_id && body?.webhook_id && body?.url
? body
: (body.payload || body)

cloudlog({
requestId: c.get('requestId'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- Ensure webhook queues are drained by the table-driven cron scheduler.
--
-- Webhooks were originally added to the legacy hard-coded process_all_cron_tasks
-- implementation, but the later cron_tasks migration rebuilt the high-frequency
-- queue list without carrying webhook_dispatcher/webhook_delivery forward.
-- Update the active cron_tasks row in place so existing environments start
-- processing webhook queues again.

WITH updated_target AS (
SELECT
ct.name,
(
WITH current_target AS (
SELECT COALESCE(ct.target::jsonb, '[]'::jsonb) AS target
),
ordered_items AS (
SELECT value, ordinality
FROM current_target,
jsonb_array_elements_text(current_target.target) WITH ORDINALITY AS existing_items(value, ordinality)

UNION ALL

SELECT 'webhook_dispatcher', 1000000
FROM current_target
WHERE NOT current_target.target ? 'webhook_dispatcher'

UNION ALL

SELECT 'webhook_delivery', 1000001
FROM current_target
WHERE NOT current_target.target ? 'webhook_delivery'
)
SELECT
COALESCE(
jsonb_agg(value ORDER BY ordinality),
'["webhook_dispatcher","webhook_delivery"]'::jsonb
)::text
FROM ordered_items
) AS normalized_target
FROM public.cron_tasks AS ct
WHERE ct.name = 'high_frequency_queues'
)
UPDATE public.cron_tasks AS ct
SET
target = updated_target.normalized_target,
updated_at = now()
FROM updated_target
WHERE ct.name = updated_target.name;
41 changes: 41 additions & 0 deletions supabase/tests/49_test_webhook_cron_registration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
BEGIN;

SELECT plan(2);

SELECT tests.authenticate_as_service_role();

SELECT ok(
(
SELECT count(*)::int
FROM public.cron_tasks
WHERE
name = 'high_frequency_queues'
AND enabled = TRUE
AND task_type = 'function_queue'::public.cron_task_type
AND target::jsonb ? 'webhook_dispatcher'
AND target::jsonb ? 'webhook_delivery'
) = 1,
'cron_tasks high_frequency_queues includes webhook dispatcher and delivery queues'
);

SELECT ok(
(
WITH queue_order AS (
SELECT value, ordinality
FROM public.cron_tasks,
jsonb_array_elements_text(target::jsonb) WITH ORDINALITY AS queue_items(value, ordinality)
WHERE name = 'high_frequency_queues'
)
SELECT
MAX(CASE WHEN value = 'webhook_dispatcher' THEN ordinality END)
< MAX(CASE WHEN value = 'webhook_delivery' THEN ordinality END)
FROM queue_order
),
'cron_tasks high_frequency_queues processes webhook dispatcher before delivery'
);

SELECT tests.clear_authentication();

SELECT * FROM finish(); -- noqa: AM04

ROLLBACK;
Loading
Loading