补充方案
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
-- =====================================================================================
|
||||
-- RPC: notify-worker safe recipients lookup (RLS-safe)
|
||||
--
|
||||
-- 背景:
|
||||
-- - public.ml_orders 已开启 RLS,PostgREST 在未携带可解码 JWT 时,auth.uid() 为 NULL,
|
||||
-- 直接 SELECT 会被策略过滤为 0 行,导致 notify-worker 报 “order not found for waybill”。
|
||||
-- - 在一些自托管场景中,Authorization: Bearer <service_role JWT> 可能因 JWT_SECRET 不一致被 PostgREST 拒绝(401 PGRST301)。
|
||||
--
|
||||
-- 方案:
|
||||
-- - 提供 SECURITY DEFINER 的 RPC:只返回订单的收件人映射(user_id / merchant_id)。
|
||||
-- - 通过请求头 x-notify-worker-token 做显式鉴权(避免把表全局 SELECT 放开)。
|
||||
--
|
||||
-- 使用:
|
||||
-- - notify-worker 调用 POST /rest/v1/rpc/notify_get_order_recipients
|
||||
-- 并携带 header: x-notify-worker-token: <NOTIFY_WORKER_RPC_TOKEN>
|
||||
-- =====================================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.notify_get_order_recipients(
|
||||
p_order_id UUID DEFAULT NULL,
|
||||
p_order_no TEXT DEFAULT NULL
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id UUID,
|
||||
order_no VARCHAR,
|
||||
user_id UUID,
|
||||
merchant_id UUID
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $func$
|
||||
DECLARE
|
||||
headers_json JSON;
|
||||
token TEXT;
|
||||
expected_token TEXT;
|
||||
BEGIN
|
||||
-- 1) 读取请求头 token(PostgREST 会把 headers 放入 GUC request.headers)
|
||||
expected_token := current_setting('app.notify_worker_token', true);
|
||||
headers_json := NULLIF(current_setting('request.headers', true), '')::json;
|
||||
IF headers_json IS NOT NULL THEN
|
||||
token := headers_json->>'x-notify-worker-token';
|
||||
END IF;
|
||||
|
||||
IF expected_token IS NULL OR expected_token = '' THEN
|
||||
RAISE EXCEPTION 'server misconfigured: app.notify_worker_token is not set';
|
||||
END IF;
|
||||
|
||||
IF token IS NULL OR token <> expected_token THEN
|
||||
RAISE EXCEPTION 'permission denied: invalid x-notify-worker-token';
|
||||
END IF;
|
||||
|
||||
-- 2) 参数校验
|
||||
IF (p_order_id IS NULL OR p_order_id::text = '') AND (p_order_no IS NULL OR btrim(p_order_no) = '') THEN
|
||||
RAISE EXCEPTION 'p_order_id or p_order_no must be provided';
|
||||
END IF;
|
||||
|
||||
-- 3) 返回映射(SECURITY DEFINER 可绕过 RLS;只返回最小必要字段)
|
||||
RETURN QUERY
|
||||
SELECT o.id, o.order_no, o.user_id, o.merchant_id
|
||||
FROM public.ml_orders o
|
||||
WHERE (p_order_id IS NOT NULL AND o.id = p_order_id)
|
||||
OR (p_order_no IS NOT NULL AND o.order_no = p_order_no)
|
||||
LIMIT 1;
|
||||
END;
|
||||
$func$;
|
||||
|
||||
-- 默认收紧:撤销 PUBLIC,按需授予 anon/authenticated/service_role 执行权限。
|
||||
REVOKE ALL ON FUNCTION public.notify_get_order_recipients(UUID, TEXT) FROM PUBLIC;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN
|
||||
GRANT EXECUTE ON FUNCTION public.notify_get_order_recipients(UUID, TEXT) TO anon;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN
|
||||
GRANT EXECUTE ON FUNCTION public.notify_get_order_recipients(UUID, TEXT) TO authenticated;
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'service_role') THEN
|
||||
GRANT EXECUTE ON FUNCTION public.notify_get_order_recipients(UUID, TEXT) TO service_role;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
COMMIT;
|
||||
@@ -10,6 +10,7 @@
|
||||
- `SUPA_USE_BEARER`(可选):是否附加 `Authorization: Bearer <SUPA_KEY>`,默认 `false`。
|
||||
- 在一些自托管 Supabase/Kong(key-auth)环境中,**只需要** `apikey`;如果误加 Bearer 且 key 不是 JWT,可能出现 `PGRST301`("None of the keys was able to decode the JWT")。
|
||||
- `WEBHOOK_SECRET`(可选):与第三方共享的 HMAC-SHA256 secret,用于校验 `X-Signature`(签名为 hex)
|
||||
- `WEBHOOK_REJECT_INVALID_SIGNATURE`(可选):若为 `true`,且配置了 `WEBHOOK_SECRET`,则验签失败会直接返回 HTTP 401(默认不拒绝,只记录)。
|
||||
- `WEBHOOK_PORT`(可选):接收器监听端口,默认 `7201`(推荐用这个,便于与 push-server 共享同一份 `server/config.json`)
|
||||
- `PORT`(可选):接收器监听端口(兼容旧用法;若共享 `server/config.json` 且其中 `PORT=7301`,会导致端口冲突)
|
||||
|
||||
@@ -85,6 +86,8 @@ curl -i -X POST http://localhost:7201/webhook/express/status \
|
||||
-d "$BODY"
|
||||
```
|
||||
|
||||
> 重要:签名计算必须使用**原始请求体文本**(raw body)。接收器也会使用 raw body 进行验签;不要用 JSON 对象 stringify 后的字符串替代。
|
||||
|
||||
健康检查:
|
||||
- `GET http://localhost:7201/health`(端口以 `PORT` 为准)
|
||||
|
||||
@@ -125,12 +128,15 @@ Stop-Process -Id <PID>
|
||||
验证写入(查看 Supabase):
|
||||
```bash
|
||||
# 示例:列最近 5 条原始回文
|
||||
curl -s -H "apikey: $SUPA_KEY" -H "Authorization: Bearer $SUPA_KEY" \
|
||||
curl -s -H "apikey: $SUPA_KEY" -H "Accept: application/json" \
|
||||
"$SUPA_URL/rest/v1/platform_express_event_raw?select=*&order=received_at.desc&limit=5" | jq .
|
||||
|
||||
# 查看最近轨迹事件
|
||||
curl -s -H "apikey: $SUPA_KEY" -H "Authorization: Bearer $SUPA_KEY" \
|
||||
curl -s -H "apikey: $SUPA_KEY" -H "Accept: application/json" \
|
||||
"$SUPA_URL/rest/v1/platform_express_tracking_events?select=*&order=created_at.desc&limit=5" | jq .
|
||||
|
||||
# 如果你的环境已确认 Bearer 可用(不会触发 PGRST301),也可以额外加上:
|
||||
# -H "Authorization: Bearer $SUPA_KEY"
|
||||
```
|
||||
|
||||
与仓库中 Mock 实现的关系:
|
||||
@@ -146,6 +152,6 @@ curl -s -H "apikey: $SUPA_KEY" -H "Authorization: Bearer $SUPA_KEY" \
|
||||
- 若需要我加重放防护或返回 4xx/5xx 更精确的逻辑,也可继续实现。
|
||||
|
||||
文件位置:
|
||||
- [Webhook 接收器](pages/mall/delivery/server/webhook-receiver.js)
|
||||
- [Webhook 接收器](pages/mall/delivery/webhook-server/webhook-receiver.js)
|
||||
|
||||
作者:自动生成(可手动调整)
|
||||
|
||||
Reference in New Issue
Block a user