@authhero/cloudflare-adapter 3.0.12 → 3.1.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 +61 -0
- package/dist/cloudflare-adapter.cjs +28 -28
- package/dist/cloudflare-adapter.d.ts +141 -2
- package/dist/cloudflare-adapter.mjs +282 -180
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/analytics-engine-outbox-metrics/index.d.ts +61 -0
- package/dist/types/customDomains/index.d.ts +24 -1
- package/dist/types/customDomains/sync.d.ts +78 -0
- package/dist/types/index.d.ts +4 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -676,6 +676,67 @@ curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engin
|
|
|
676
676
|
-d "SELECT blob3 as type, count() as count FROM authhero_logs WHERE index1 = 'tenant-123' GROUP BY blob3"
|
|
677
677
|
```
|
|
678
678
|
|
|
679
|
+
## Outbox Metrics Sink (Analytics Engine)
|
|
680
|
+
|
|
681
|
+
`createAnalyticsEngineOutboxMetricsSink` turns authhero's outbox relay metrics
|
|
682
|
+
into Analytics Engine data points, so dead-letters and retry backoff are
|
|
683
|
+
queryable instead of only appearing in `console` output.
|
|
684
|
+
|
|
685
|
+
### Setup
|
|
686
|
+
|
|
687
|
+
1. Add a dataset binding in `wrangler.toml`:
|
|
688
|
+
|
|
689
|
+
```toml
|
|
690
|
+
[[analytics_engine_datasets]]
|
|
691
|
+
binding = "OUTBOX_METRICS"
|
|
692
|
+
dataset = "authhero_outbox_metrics"
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
2. Pass the sink to both relay paths — `init()` (inline per-request delivery)
|
|
696
|
+
and `runOutboxRelay()` (the cron drain):
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
import { createAnalyticsEngineOutboxMetricsSink } from "@authhero/cloudflare-adapter";
|
|
700
|
+
import { init, runOutboxRelay } from "authhero";
|
|
701
|
+
|
|
702
|
+
const metrics = createAnalyticsEngineOutboxMetricsSink({
|
|
703
|
+
analyticsEngineBinding: env.OUTBOX_METRICS,
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
const { app } = init({ dataAdapter, outbox: { enabled: true, metrics } });
|
|
707
|
+
|
|
708
|
+
// in scheduled():
|
|
709
|
+
await runOutboxRelay({ dataAdapter, issuer: env.ISSUER, metrics });
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
Without `analyticsEngineBinding` the sink is a silent no-op, so the same wiring
|
|
713
|
+
works in local dev and tests.
|
|
714
|
+
|
|
715
|
+
### Data Schema
|
|
716
|
+
|
|
717
|
+
| Field | Type | Description |
|
|
718
|
+
| ------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
719
|
+
| blob1 | string | metric name (`outbox_events_processed_total`, `outbox_events_dead_lettered_total`, `outbox_retry_delay_seconds`) |
|
|
720
|
+
| blob2 | string | tenant_id |
|
|
721
|
+
| blob3 | string | event_type |
|
|
722
|
+
| blob4 | string | source (`request` or `cron`) |
|
|
723
|
+
| blob5 | string | destination (delivery failures only) |
|
|
724
|
+
| blob6 | string | error (delivery failures only) |
|
|
725
|
+
| double1 | number | value — `1` for counters, retry delay in seconds for `outbox_retry_delay_seconds` |
|
|
726
|
+
| double2 | number | retry_count of the event |
|
|
727
|
+
| double3 | number | timestamp (epoch ms) |
|
|
728
|
+
| index1 | string | tenant_id (for efficient filtering) |
|
|
729
|
+
|
|
730
|
+
Analytics Engine samples rows under load, so weight counters by
|
|
731
|
+
`_sample_interval` to get true totals:
|
|
732
|
+
|
|
733
|
+
```bash
|
|
734
|
+
# Dead-lettered events per tenant
|
|
735
|
+
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql" \
|
|
736
|
+
-H "Authorization: Bearer $API_TOKEN" \
|
|
737
|
+
-d "SELECT index1 as tenant, sum(double1 * _sample_interval) as dead_lettered FROM authhero_outbox_metrics WHERE blob1 = 'outbox_events_dead_lettered_total' GROUP BY index1"
|
|
738
|
+
```
|
|
739
|
+
|
|
679
740
|
## Geo Adapter
|
|
680
741
|
|
|
681
742
|
The Cloudflare Geo adapter extracts geographic location information from Cloudflare's automatic request headers. This is used to enrich authentication logs with location data.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require("./sync-defaults-errors-Htp1NWTq.js");let l=require("@authhero/adapter-interfaces"),u=require("@hono/zod-openapi"),d=require("hono/http-exception"),f=require("wretch");f=s(f);let p=require("wretch/middlewares");var m=u.z.object({code:u.z.number(),message:u.z.string()}),h=u.z.object({message:u.z.string()}),g=u.z.object({emails:u.z.array(u.z.string()).optional(),http_body:u.z.string().optional(),http_url:u.z.string().optional(),txt_name:u.z.string().optional(),txt_value:u.z.string().optional()}),_=u.z.object({ciphers:u.z.array(u.z.string()).optional(),early_hints:u.z.string().optional(),http2:u.z.string().optional(),min_tls_version:u.z.string().optional(),tls_1_3:u.z.string().optional()}),ee=u.z.object({id:u.z.string(),bundle_method:u.z.string().optional(),certificate_authority:u.z.string(),custom_certificate:u.z.string().optional(),custom_csr_id:u.z.string().optional(),custom_key:u.z.string().optional(),expires_on:u.z.string().optional(),hosts:u.z.array(u.z.string()).optional(),issuer:u.z.string().optional(),method:u.z.string(),serial_number:u.z.string().optional(),settings:_.optional(),signature:u.z.string().optional(),type:u.z.string(),uploaded_on:u.z.string().optional(),validation_errors:u.z.array(h).optional(),validation_records:u.z.array(g).optional(),wildcard:u.z.boolean()}),te=u.z.object({name:u.z.string(),type:u.z.string(),value:u.z.string()}),ne=u.z.object({http_body:u.z.string().optional(),http_url:u.z.string().optional()}),v=u.z.object({id:u.z.string(),ssl:ee,hostname:u.z.string(),custom_metadata:u.z.record(u.z.string(),u.z.string()).optional(),custom_origin_server:u.z.string().optional(),custom_origin_sni:u.z.string().optional(),ownership_verification:te.optional(),ownership_verification_http:ne.optional(),status:u.z.string(),verification_errors:u.z.array(u.z.string()).optional(),created_at:u.z.string()}),y=u.z.object({errors:u.z.array(m),messages:u.z.array(m),success:u.z.boolean(),result:v}),b=u.z.object({errors:u.z.array(m),messages:u.z.array(m),success:u.z.boolean(),result:u.z.array(v)});function x(e){return(0,f.default)(`https://api.cloudflare.com/client/v4/zones/${e.zoneId}`).headers({"X-Auth-Email":e.authEmail,"X-Auth-Key":e.authKey,"Content-Type":`application/json`}).middlewares([(0,p.retry)(),(0,p.dedupe)()])}function S(e){let t={},n={};if(!e)return{sslOverrides:t,rest:n};for(let[r,i]of Object.entries(e))r.startsWith(`ssl.`)?t[r.slice(4)]=i:n[r]=i;return{sslOverrides:t,rest:n}}function C(e){let t=[];if(e.ssl.validation_records)for(let n of e.ssl.validation_records)n.txt_name&&n.txt_value&&t.push({name:`txt`,record:n.txt_value,domain:n.txt_name});if(e.ownership_verification&&t.push({name:`txt`,record:e.ownership_verification.value,domain:e.ownership_verification.name}),e.ownership_verification_http?.http_body&&e.ownership_verification_http?.http_url&&t.push({name:`http`,http_body:e.ownership_verification_http.http_body,http_url:e.ownership_verification_http.http_url}),e.ssl.validation_records)for(let n of e.ssl.validation_records)n.http_body&&n.http_url&&t.push({name:`http`,http_body:n.http_body,http_url:n.http_url});let n={...e.domain_metadata||{},"ssl.method":e.ssl.method,"ssl.type":e.ssl.type,"ssl.certificate_authority":e.ssl.certificate_authority};return{custom_domain_id:e.id,domain:e.hostname,primary:e.primary,status:e.status===`active`?`ready`:`pending`,type:e.type??`auth0_managed_certs`,verification:{methods:u.z.array(l.verificationMethodsSchema).parse(t)},domain_metadata:n}}function re(e){let t=e instanceof Error?e.message:String(e);return/duplicate/i.test(t)}async function ie(e,t,n){let r;try{r=await x(e).get(`/custom_hostnames?hostname=${encodeURIComponent(n)}`).json()}catch{return null}let i=b.safeParse(r);if(!i.success||!i.data.success)return null;let a=i.data.result.find(e=>e.hostname.toLowerCase()===n.toLowerCase());return!a||e.enterprise&&a.custom_metadata?.tenant_id!==t?null:a}function ae(e){return{create:async(t,n)=>{let{sslOverrides:r,rest:i}=S(n.domain_metadata);if(await e.customDomainAdapter.getByDomain(n.domain))throw new d.HTTPException(409,{message:`The domain ${n.domain} is already registered`});let a;try{let i=y.parse(await x(e).post({hostname:n.domain,ssl:{method:`txt`,type:`dv`,...r},custom_metadata:e.enterprise?{tenant_id:t}:void 0},`/custom_hostnames`).json());if(!i.success)throw Error(JSON.stringify(i.errors));a=i.result}catch(r){if(!re(r))throw r;let i=await ie(e,t,n.domain);if(!i)throw r;a=i}let o=C({...a,primary:!1,type:n.type,domain_metadata:i});return await e.customDomainAdapter.create(t,{custom_domain_id:o.custom_domain_id,domain:o.domain,type:o.type,domain_metadata:n.domain_metadata}),await e.customDomainAdapter.update(t,o.custom_domain_id,{status:o.status,primary:o.primary,verification:o.verification}),o},get:async(t,n)=>{let r=await e.customDomainAdapter.get(t,n);if(!r)throw new d.HTTPException(404);let i;try{i=await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json()}catch(e){return console.warn(`[custom-domains] CF fetch failed for ${n} (tenant=${t}); returning stale DB row:`,e instanceof Error?e.message:e),r}let a=y.safeParse(i);if(!a.success||!a.data.success)return console.warn(`[custom-domains] CF response unparseable for ${n} (tenant=${t}); returning stale DB row.`,a.success?{cfErrors:a.data.errors}:{zodIssues:a.error.issues,body:i}),r;let{result:o}=a.data;if(e.enterprise&&o.custom_metadata?.tenant_id!==t)throw new d.HTTPException(404);let s;try{s=C({...r,...o})}catch(e){return console.warn(`[custom-domains] mapCustomDomainResponse failed for ${n} (tenant=${t}); returning stale DB row.`,e instanceof Error?e.message:e),r}if(s.status!==r.status||JSON.stringify(s.verification)!==JSON.stringify(r.verification))try{await e.customDomainAdapter.update(t,n,{status:s.status,verification:s.verification})}catch(e){console.warn(`[custom-domains] DB writeback failed for ${n} (tenant=${t}); returning merged response without DB sync.`,e instanceof Error?e.message:e)}return s},getByDomain:async t=>e.customDomainAdapter.getByDomain(t),list:async t=>e.customDomainAdapter.list(t),remove:async(t,n)=>{if(e.enterprise){let{result:r,success:i}=y.parse(await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json());if(!i||r.custom_metadata?.tenant_id!==t)throw new d.HTTPException(404)}let r=await x(e).delete(`/custom_hostnames/${encodeURIComponent(n)}`).res();return r.ok&&await e.customDomainAdapter.remove(t,n),r.ok},update:async(t,n,r)=>{let{sslOverrides:i}=S(r.domain_metadata),a={};if(Object.keys(i).length>0){let t;try{t=await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json()}catch(e){throw new d.HTTPException(503,{message:`Failed to fetch current custom hostname state: ${e instanceof Error?e.message:String(e)}`})}let r=y.safeParse(t);if(!r.success||!r.data.success)throw new d.HTTPException(503,{message:`Failed to parse current custom hostname state: ${JSON.stringify(r.success?r.data.errors:r.error.issues)}`});a.ssl={method:r.data.result.ssl.method,type:r.data.result.ssl.type,certificate_authority:r.data.result.ssl.certificate_authority,...i}}if(Object.keys(a).length>0){let t=await x(e).patch(a,`/custom_hostnames/${encodeURIComponent(n)}`).res().catch(e=>{throw new d.HTTPException(503,{message:`Failed to update custom hostname: ${e instanceof Error?e.message:String(e)}`})});if(!t.ok)throw new d.HTTPException(503,{message:await t.text()})}return e.customDomainAdapter.update(t,n,r)},uploadCertificate:async(t,n,r)=>{let i=await e.customDomainAdapter.get(t,n);if(!i)throw new d.HTTPException(404);let a=await x(e).patch({ssl:{custom_certificate:r.certificate,custom_key:r.private_key}},`/custom_hostnames/${encodeURIComponent(n)}`).json(),{result:o,errors:s,success:c}=y.parse(a);if(!c)throw new d.HTTPException(503,{message:JSON.stringify(s)});let l=C({...i,...o});return await e.customDomainAdapter.update(t,n,{status:l.status,verification:l.verification}),l}}}var oe=class{config;cache=null;constructor(e){this.config=e}async getCache(){if(this.cache)return this.cache;if(typeof caches>`u`)throw Error(`caches API is not available - CloudflareCache should only be used in Cloudflare Workers`);return this.config.cacheName?this.cache=await caches.open(this.config.cacheName):this.cache=caches.default,this.cache}getKey(e){return this.config.keyPrefix?`${this.config.keyPrefix}:${e}`:e}createRequest(e){return new Request(`https://cache.internal/${this.getKey(e)}`)}async matchWithTimeout(e,t){let n=this.config.getTimeoutMs??200;return n<=0?e.match(t):new Promise(r=>{let i=setTimeout(()=>r(void 0),n);e.match(t).then(e=>{clearTimeout(i),r(e)},()=>{clearTimeout(i),r(void 0)})})}async get(e){try{let t=await this.getCache(),n=this.createRequest(e),r=await this.matchWithTimeout(t,n);if(!r)return null;let i=await r.text();if(!i)return await this.delete(e),null;let a;try{a=JSON.parse(i)}catch{return await this.delete(e),null}return a.expiresAt&&new Date(a.expiresAt)<new Date?(await this.delete(e),null):a.value}catch(t){return console.error(`CloudflareCache: get error for key ${e}:`,t),null}}async set(e,t,n){try{let r=await this.getCache(),i=n??this.config.defaultTtlSeconds,a=i!==void 0,o=a?Math.max(0,i):0,s={value:t,expiresAt:a?new Date(Date.now()+(o>0?o*1e3:-1)).toISOString():void 0,cachedAt:new Date().toISOString()},c=this.createRequest(e),l={"Content-Type":`application/json`};a&&o>0&&(l[`Cache-Control`]=`max-age=${o}`);let u=new Response(JSON.stringify(s),{headers:l});await r.put(c,u)}catch(t){console.error(`CloudflareCache: set error for key ${e}:`,t)}}async delete(e){try{let t=await this.getCache(),n=this.createRequest(e);return await t.delete(n)}catch(t){return console.error(`CloudflareCache: delete error for key ${e}:`,t),!1}}async deleteByPrefix(e){return console.warn(`CloudflareCache.deleteByPrefix() is not implemented - Cloudflare Cache API does not support prefix-based deletion`),0}async clear(){console.warn(`CloudflareCache.clear() is not implemented - Cloudflare Cache API does not support clearing all entries`)}};function se(e={}){return new oe({defaultTtlSeconds:300,keyPrefix:`authhero`,getTimeoutMs:200,...e})}var ce=`useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict`,w=(e=21)=>{let t=``,n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+=ce[n[e]&63];return t};async function T(e,t){let n=e.timeout||3e4,r=new AbortController,i=setTimeout(()=>r.abort(),n);try{let n=`https://api.sql.cloudflarestorage.com/api/v1/accounts/${e.accountId}/r2-sql/query/${e.warehouseName}`,i=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${e.authToken}`},body:JSON.stringify({query:t}),signal:r.signal});if(!i.ok)throw Error(`R2 SQL query failed: ${i.status} ${i.statusText}`);let a=await i.json();if(!a.success&&a.errors)throw Error(`R2 SQL error: ${a.errors.join(`, `)}`);return a.data||a.result?.data||[]}finally{clearTimeout(i)}}function E(e){return`'${e.replace(/'/g,`''`)}'`}function D(e){return`"${e.replace(/"/g,`""`)}"`}function O(e){let t=e=>{if(!e)return``;try{return JSON.parse(e)}catch{return e}};return{type:e.type,date:e.date,description:e.description,ip:e.ip,user_agent:e.user_agent,details:t(e.details),isMobile:!!e.isMobile,user_id:e.user_id,user_name:e.user_name,connection:e.connection,connection_id:e.connection_id,client_id:e.client_id,client_name:e.client_name,audience:e.audience,scope:e.scope,strategy:e.strategy,strategy_type:e.strategy_type,hostname:e.hostname,auth0_client:t(e.auth0_client),log_id:e.id,location_info:e.country_code||e.city_name||e.latitude||e.longitude||e.time_zone||e.continent_code?{country_code:e.country_code||``,city_name:e.city_name||``,latitude:e.latitude||``,longitude:e.longitude||``,time_zone:e.time_zone||``,continent_code:e.continent_code||``}:void 0}}function le(e){return async(t,n)=>{let r=n.log_id||w(),i={...n,log_id:r};return await ue(e,t,i),i}}async function ue(e,t,n){let r=e=>e?JSON.stringify(e):void 0,i={id:n.log_id,tenant_id:t,type:n.type,date:n.date,description:n.description?.substring(0,256),ip:n.ip,user_agent:n.user_agent,details:r(n.details)?.substring(0,8192),isMobile:+!!n.isMobile,user_id:n.user_id,user_name:n.user_name,connection:n.connection,connection_id:n.connection_id,client_id:n.client_id,client_name:n.client_name,audience:n.audience,scope:n.scope,strategy:n.strategy,strategy_type:n.strategy_type,hostname:n.hostname,auth0_client:r(n.auth0_client),log_id:n.log_id,country_code:n.location_info?.country_code,city_name:n.location_info?.city_name,latitude:n.location_info?.latitude,longitude:n.location_info?.longitude,time_zone:n.location_info?.time_zone,continent_code:n.location_info?.continent_code};try{if(e.pipelineBinding)await e.pipelineBinding.send([i]);else if(e.pipelineEndpoint){let t=e.timeout||3e4,n=new AbortController,r=setTimeout(()=>n.abort(),t);try{let t=await fetch(e.pipelineEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify([i]),signal:n.signal});if(!t.ok)throw Error(`Pipeline ingestion failed: ${t.status} ${t.statusText}`)}finally{clearTimeout(r)}}else throw Error(`Either pipelineEndpoint or pipelineBinding must be configured`)}catch(e){throw console.error(`Failed to send log to Pipeline:`,e),e}}function de(e){return async(t,n)=>{let r=e.namespace||`default`,i=e.tableName||`logs`,a=await T(e,`
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require("./sync-defaults-errors-Htp1NWTq.js");let l=require("@authhero/adapter-interfaces"),u=require("@hono/zod-openapi"),d=require("hono/http-exception"),f=require("wretch");f=s(f);let p=require("wretch/middlewares");var m=u.z.object({code:u.z.number(),message:u.z.string()}),h=u.z.object({message:u.z.string()}),g=u.z.object({emails:u.z.array(u.z.string()).optional(),http_body:u.z.string().optional(),http_url:u.z.string().optional(),txt_name:u.z.string().optional(),txt_value:u.z.string().optional()}),_=u.z.object({ciphers:u.z.array(u.z.string()).optional(),early_hints:u.z.string().optional(),http2:u.z.string().optional(),min_tls_version:u.z.string().optional(),tls_1_3:u.z.string().optional()}),ee=u.z.object({id:u.z.string(),bundle_method:u.z.string().optional(),certificate_authority:u.z.string(),custom_certificate:u.z.string().optional(),custom_csr_id:u.z.string().optional(),custom_key:u.z.string().optional(),expires_on:u.z.string().optional(),hosts:u.z.array(u.z.string()).optional(),issuer:u.z.string().optional(),method:u.z.string(),serial_number:u.z.string().optional(),settings:_.optional(),signature:u.z.string().optional(),type:u.z.string(),uploaded_on:u.z.string().optional(),validation_errors:u.z.array(h).optional(),validation_records:u.z.array(g).optional(),wildcard:u.z.boolean()}),te=u.z.object({name:u.z.string(),type:u.z.string(),value:u.z.string()}),ne=u.z.object({http_body:u.z.string().optional(),http_url:u.z.string().optional()}),v=u.z.object({id:u.z.string(),ssl:ee,hostname:u.z.string(),custom_metadata:u.z.record(u.z.string(),u.z.string()).optional(),custom_origin_server:u.z.string().optional(),custom_origin_sni:u.z.string().optional(),ownership_verification:te.optional(),ownership_verification_http:ne.optional(),status:u.z.string(),verification_errors:u.z.array(u.z.string()).optional(),created_at:u.z.string()}),y=u.z.object({errors:u.z.array(m),messages:u.z.array(m),success:u.z.boolean(),result:v}),b=u.z.object({errors:u.z.array(m),messages:u.z.array(m),success:u.z.boolean(),result:u.z.array(v)});function x(e){return(0,f.default)(`https://api.cloudflare.com/client/v4/zones/${e.zoneId}`).headers({"X-Auth-Email":e.authEmail,"X-Auth-Key":e.authKey,"Content-Type":`application/json`}).middlewares([(0,p.retry)(),(0,p.dedupe)()])}function S(e){let t={},n={};if(!e)return{sslOverrides:t,rest:n};for(let[r,i]of Object.entries(e))r.startsWith(`ssl.`)?t[r.slice(4)]=i:n[r]=i;return{sslOverrides:t,rest:n}}function C(e){let t=[];if(e.ssl.validation_records)for(let n of e.ssl.validation_records)n.txt_name&&n.txt_value&&t.push({name:`txt`,record:n.txt_value,domain:n.txt_name});if(e.ownership_verification&&t.push({name:`txt`,record:e.ownership_verification.value,domain:e.ownership_verification.name}),e.ownership_verification_http?.http_body&&e.ownership_verification_http?.http_url&&t.push({name:`http`,http_body:e.ownership_verification_http.http_body,http_url:e.ownership_verification_http.http_url}),e.ssl.validation_records)for(let n of e.ssl.validation_records)n.http_body&&n.http_url&&t.push({name:`http`,http_body:n.http_body,http_url:n.http_url});let n={...e.domain_metadata||{},"ssl.method":e.ssl.method,"ssl.type":e.ssl.type,"ssl.certificate_authority":e.ssl.certificate_authority};return{custom_domain_id:e.id,domain:e.hostname,primary:e.primary,status:e.status===`active`?`ready`:`pending`,type:e.type??`auth0_managed_certs`,verification:{methods:u.z.array(l.verificationMethodsSchema).parse(t)},domain_metadata:n}}function re(e){let t=e instanceof Error?e.message:String(e);return/duplicate/i.test(t)}async function ie(e,t,n){let r;try{r=await x(e).get(`/custom_hostnames?hostname=${encodeURIComponent(n)}`).json()}catch{return null}let i=b.safeParse(r);if(!i.success||!i.data.success)return null;let a=i.data.result.find(e=>e.hostname.toLowerCase()===n.toLowerCase());return!a||e.enterprise&&a.custom_metadata?.tenant_id!==t?null:a}async function ae(e,t,n,r){let i=n.custom_domain_id,a;try{a=C({...n,...r})}catch(e){return console.warn(`[custom-domains] mapCustomDomainResponse failed for ${i} (tenant=${t}); keeping the stored row.`,e instanceof Error?e.message:e),{domain:n,outcome:`failed`}}if(a.status===n.status&&JSON.stringify(a.verification)===JSON.stringify(n.verification))return{domain:a,outcome:`unchanged`};try{await e.customDomainAdapter.update(t,i,{status:a.status,verification:a.verification})}catch(e){return console.warn(`[custom-domains] DB writeback failed for ${i} (tenant=${t}); returning merged response without DB sync.`,e instanceof Error?e.message:e),{domain:a,outcome:`failed`}}return{domain:a,outcome:`updated`}}function oe(e){return{create:async(t,n)=>{let{sslOverrides:r,rest:i}=S(n.domain_metadata);if(await e.customDomainAdapter.getByDomain(n.domain))throw new d.HTTPException(409,{message:`The domain ${n.domain} is already registered`});let a;try{let i=y.parse(await x(e).post({hostname:n.domain,ssl:{method:`txt`,type:`dv`,...r},custom_metadata:e.enterprise?{tenant_id:t}:void 0},`/custom_hostnames`).json());if(!i.success)throw Error(JSON.stringify(i.errors));a=i.result}catch(r){if(!re(r))throw r;let i=await ie(e,t,n.domain);if(!i)throw r;a=i}let o=C({...a,primary:!1,type:n.type,domain_metadata:i});return await e.customDomainAdapter.create(t,{custom_domain_id:o.custom_domain_id,domain:o.domain,type:o.type,domain_metadata:n.domain_metadata}),await e.customDomainAdapter.update(t,o.custom_domain_id,{status:o.status,primary:o.primary,verification:o.verification}),o},get:async(t,n)=>{let r=await e.customDomainAdapter.get(t,n);if(!r)throw new d.HTTPException(404);let i;try{i=await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json()}catch(e){return console.warn(`[custom-domains] CF fetch failed for ${n} (tenant=${t}); returning stale DB row:`,e instanceof Error?e.message:e),r}let a=y.safeParse(i);if(!a.success||!a.data.success)return console.warn(`[custom-domains] CF response unparseable for ${n} (tenant=${t}); returning stale DB row.`,a.success?{cfErrors:a.data.errors}:{zodIssues:a.error.issues,body:i}),r;let{result:o}=a.data;if(e.enterprise&&o.custom_metadata?.tenant_id!==t)throw new d.HTTPException(404);let{domain:s}=await ae(e,t,r,o);return s},getByDomain:async t=>e.customDomainAdapter.getByDomain(t),list:async t=>e.customDomainAdapter.list(t),remove:async(t,n)=>{if(e.enterprise){let{result:r,success:i}=y.parse(await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json());if(!i||r.custom_metadata?.tenant_id!==t)throw new d.HTTPException(404)}let r=await x(e).delete(`/custom_hostnames/${encodeURIComponent(n)}`).res();return r.ok&&await e.customDomainAdapter.remove(t,n),r.ok},update:async(t,n,r)=>{let{sslOverrides:i}=S(r.domain_metadata),a={};if(Object.keys(i).length>0){let t;try{t=await x(e).get(`/custom_hostnames/${encodeURIComponent(n)}`).json()}catch(e){throw new d.HTTPException(503,{message:`Failed to fetch current custom hostname state: ${e instanceof Error?e.message:String(e)}`})}let r=y.safeParse(t);if(!r.success||!r.data.success)throw new d.HTTPException(503,{message:`Failed to parse current custom hostname state: ${JSON.stringify(r.success?r.data.errors:r.error.issues)}`});a.ssl={method:r.data.result.ssl.method,type:r.data.result.ssl.type,certificate_authority:r.data.result.ssl.certificate_authority,...i}}if(Object.keys(a).length>0){let t=await x(e).patch(a,`/custom_hostnames/${encodeURIComponent(n)}`).res().catch(e=>{throw new d.HTTPException(503,{message:`Failed to update custom hostname: ${e instanceof Error?e.message:String(e)}`})});if(!t.ok)throw new d.HTTPException(503,{message:await t.text()})}return e.customDomainAdapter.update(t,n,r)},uploadCertificate:async(t,n,r)=>{let i=await e.customDomainAdapter.get(t,n);if(!i)throw new d.HTTPException(404);let a=await x(e).patch({ssl:{custom_certificate:r.certificate,custom_key:r.private_key}},`/custom_hostnames/${encodeURIComponent(n)}`).json(),{result:o,errors:s,success:c}=y.parse(a);if(!c)throw new d.HTTPException(503,{message:JSON.stringify(s)});let l=C({...i,...o});return await e.customDomainAdapter.update(t,n,{status:l.status,verification:l.verification}),l}}}var se=class{config;cache=null;constructor(e){this.config=e}async getCache(){if(this.cache)return this.cache;if(typeof caches>`u`)throw Error(`caches API is not available - CloudflareCache should only be used in Cloudflare Workers`);return this.config.cacheName?this.cache=await caches.open(this.config.cacheName):this.cache=caches.default,this.cache}getKey(e){return this.config.keyPrefix?`${this.config.keyPrefix}:${e}`:e}createRequest(e){return new Request(`https://cache.internal/${this.getKey(e)}`)}async matchWithTimeout(e,t){let n=this.config.getTimeoutMs??200;return n<=0?e.match(t):new Promise(r=>{let i=setTimeout(()=>r(void 0),n);e.match(t).then(e=>{clearTimeout(i),r(e)},()=>{clearTimeout(i),r(void 0)})})}async get(e){try{let t=await this.getCache(),n=this.createRequest(e),r=await this.matchWithTimeout(t,n);if(!r)return null;let i=await r.text();if(!i)return await this.delete(e),null;let a;try{a=JSON.parse(i)}catch{return await this.delete(e),null}return a.expiresAt&&new Date(a.expiresAt)<new Date?(await this.delete(e),null):a.value}catch(t){return console.error(`CloudflareCache: get error for key ${e}:`,t),null}}async set(e,t,n){try{let r=await this.getCache(),i=n??this.config.defaultTtlSeconds,a=i!==void 0,o=a?Math.max(0,i):0,s={value:t,expiresAt:a?new Date(Date.now()+(o>0?o*1e3:-1)).toISOString():void 0,cachedAt:new Date().toISOString()},c=this.createRequest(e),l={"Content-Type":`application/json`};a&&o>0&&(l[`Cache-Control`]=`max-age=${o}`);let u=new Response(JSON.stringify(s),{headers:l});await r.put(c,u)}catch(t){console.error(`CloudflareCache: set error for key ${e}:`,t)}}async delete(e){try{let t=await this.getCache(),n=this.createRequest(e);return await t.delete(n)}catch(t){return console.error(`CloudflareCache: delete error for key ${e}:`,t),!1}}async deleteByPrefix(e){return console.warn(`CloudflareCache.deleteByPrefix() is not implemented - Cloudflare Cache API does not support prefix-based deletion`),0}async clear(){console.warn(`CloudflareCache.clear() is not implemented - Cloudflare Cache API does not support clearing all entries`)}};function ce(e={}){return new se({defaultTtlSeconds:300,keyPrefix:`authhero`,getTimeoutMs:200,...e})}var le=`useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict`,w=(e=21)=>{let t=``,n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+=le[n[e]&63];return t};async function T(e,t){let n=e.timeout||3e4,r=new AbortController,i=setTimeout(()=>r.abort(),n);try{let n=`https://api.sql.cloudflarestorage.com/api/v1/accounts/${e.accountId}/r2-sql/query/${e.warehouseName}`,i=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${e.authToken}`},body:JSON.stringify({query:t}),signal:r.signal});if(!i.ok)throw Error(`R2 SQL query failed: ${i.status} ${i.statusText}`);let a=await i.json();if(!a.success&&a.errors)throw Error(`R2 SQL error: ${a.errors.join(`, `)}`);return a.data||a.result?.data||[]}finally{clearTimeout(i)}}function E(e){return`'${e.replace(/'/g,`''`)}'`}function D(e){return`"${e.replace(/"/g,`""`)}"`}function O(e){let t=e=>{if(!e)return``;try{return JSON.parse(e)}catch{return e}};return{type:e.type,date:e.date,description:e.description,ip:e.ip,user_agent:e.user_agent,details:t(e.details),isMobile:!!e.isMobile,user_id:e.user_id,user_name:e.user_name,connection:e.connection,connection_id:e.connection_id,client_id:e.client_id,client_name:e.client_name,audience:e.audience,scope:e.scope,strategy:e.strategy,strategy_type:e.strategy_type,hostname:e.hostname,auth0_client:t(e.auth0_client),log_id:e.id,location_info:e.country_code||e.city_name||e.latitude||e.longitude||e.time_zone||e.continent_code?{country_code:e.country_code||``,city_name:e.city_name||``,latitude:e.latitude||``,longitude:e.longitude||``,time_zone:e.time_zone||``,continent_code:e.continent_code||``}:void 0}}function ue(e){return async(t,n)=>{let r=n.log_id||w(),i={...n,log_id:r};return await de(e,t,i),i}}async function de(e,t,n){let r=e=>e?JSON.stringify(e):void 0,i={id:n.log_id,tenant_id:t,type:n.type,date:n.date,description:n.description?.substring(0,256),ip:n.ip,user_agent:n.user_agent,details:r(n.details)?.substring(0,8192),isMobile:+!!n.isMobile,user_id:n.user_id,user_name:n.user_name,connection:n.connection,connection_id:n.connection_id,client_id:n.client_id,client_name:n.client_name,audience:n.audience,scope:n.scope,strategy:n.strategy,strategy_type:n.strategy_type,hostname:n.hostname,auth0_client:r(n.auth0_client),log_id:n.log_id,country_code:n.location_info?.country_code,city_name:n.location_info?.city_name,latitude:n.location_info?.latitude,longitude:n.location_info?.longitude,time_zone:n.location_info?.time_zone,continent_code:n.location_info?.continent_code};try{if(e.pipelineBinding)await e.pipelineBinding.send([i]);else if(e.pipelineEndpoint){let t=e.timeout||3e4,n=new AbortController,r=setTimeout(()=>n.abort(),t);try{let t=await fetch(e.pipelineEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify([i]),signal:n.signal});if(!t.ok)throw Error(`Pipeline ingestion failed: ${t.status} ${t.statusText}`)}finally{clearTimeout(r)}}else throw Error(`Either pipelineEndpoint or pipelineBinding must be configured`)}catch(e){throw console.error(`Failed to send log to Pipeline:`,e),e}}function fe(e){return async(t,n)=>{let r=e.namespace||`default`,i=e.tableName||`logs`,a=await T(e,`
|
|
2
2
|
SELECT * FROM ${D(r)}.${D(i)}
|
|
3
3
|
WHERE tenant_id = ${E(t)}
|
|
4
4
|
AND id = ${E(n)}
|
|
5
5
|
LIMIT 1
|
|
6
|
-
`);if(a.length===0)return null;let o=a[0];return o?O(o):null}}function
|
|
6
|
+
`);if(a.length===0)return null;let o=a[0];return o?O(o):null}}function pe(e){let t={},n=/(\w+):(?:"([^"]*)"|(\S+))/g,r;for(;(r=n.exec(e))!==null;){let e=r[1],n=r[2]===void 0?r[3]:r[2];!e||n===void 0||(t[e]=n)}return t}function me(e){let t=[];for(let[n,r]of Object.entries(e)){let e=n.replace(/[^a-zA-Z0-9_]/g,``);e&&r&&t.push(`${D(e)} = ${E(r)}`)}return t}function he(e){return async(t,n={})=>{let{page:r=0,per_page:i=50,include_totals:a=!1,sort:o,q:s}=n,c=e.namespace||`default`,l=e.tableName||`logs`,u=[`tenant_id = ${E(t)}`];if(s){let e=pe(s);u.push(...me(e))}let d=u.join(` AND `),f=`ORDER BY date DESC`;if(o&&o.sort_by){let e=o.sort_by.replace(/[^a-zA-Z0-9_]/g,``),t=o.sort_order===`asc`?`ASC`:`DESC`;f=`ORDER BY ${D(e)} ${t}`}let p=r*i,m=`LIMIT ${i} OFFSET ${p}`,h=(await T(e,`
|
|
7
7
|
SELECT * FROM ${D(c)}.${D(l)}
|
|
8
8
|
WHERE ${d}
|
|
9
9
|
${f}
|
|
@@ -11,59 +11,59 @@ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{val
|
|
|
11
11
|
`)).map(O);if(!a)return{logs:h,start:0,limit:0,length:0};let g=(await T(e,`
|
|
12
12
|
SELECT COUNT(*) as count FROM ${D(c)}.${D(l)}
|
|
13
13
|
WHERE ${d}
|
|
14
|
-
`))[0]?.count||0;return{logs:h,start:p,limit:i,length:Number(g)}}}function
|
|
14
|
+
`))[0]?.count||0;return{logs:h,start:p,limit:i,length:Number(g)}}}function ge(){return{async getDaily(){throw Error(`Stats queries are not supported by R2 SQL logs adapter. Use Analytics Engine or Kysely adapter instead.`)},async getActiveUsers(){throw Error(`Stats queries are not supported by R2 SQL logs adapter. Use Analytics Engine or Kysely adapter instead.`)}}}function _e(e){let t=!!e.pipelineEndpoint,n=!!e.pipelineBinding;if(!t&&!n)throw Error(`R2 SQL logs adapter requires one of: "pipelineEndpoint" or "pipelineBinding"`);if(!e.authToken)throw Error(`R2 SQL logs adapter requires "authToken" configuration`);if(!e.warehouseName)throw Error(`R2 SQL logs adapter requires "warehouseName" configuration`);return{create:ue(e),list:he(e),get:fe(e)}}async function k(e,t){let n=e.timeout||3e4,r=new AbortController,i=setTimeout(()=>r.abort(),n);try{let n=`https://api.cloudflare.com/client/v4/accounts/${e.accountId}/analytics_engine/sql`,i=await fetch(n,{method:`POST`,headers:{"Content-Type":`text/plain`,Authorization:`Bearer ${e.apiToken}`},body:t,signal:r.signal});if(!i.ok){let e=await i.text();throw Error(`Analytics Engine query failed: ${i.status} ${i.statusText} - ${e}`)}let a=await i.json();if(!a.success&&a.errors&&a.errors.length>0)throw Error(`Analytics Engine error: ${a.errors.map(e=>e.message).join(`, `)}`);return a.data||[]}finally{clearTimeout(i)}}function A(e){return`'${e.replace(/'/g,`''`).replace(/\\/g,`\\\\`)}'`}function j(e){return`"${e.replace(/"/g,`""`)}"`}function M(e){let t=e=>{if(e)try{return JSON.parse(e)}catch{return}},n=e.double2||e.timestamp,r=n?new Date(n).toISOString():``;return{log_id:e.blob1,type:e.blob3,date:r,description:e.blob4,ip:e.blob5,user_agent:e.blob6,user_id:e.blob7,user_name:e.blob8,connection:e.blob9,connection_id:e.blob10,client_id:e.blob11,client_name:e.blob12,audience:e.blob13,scope:e.blob14,strategy:e.blob15,strategy_type:e.blob16,hostname:e.blob17,details:t(e.blob18),auth0_client:t(e.blob19),location_info:t(e.blob20),isMobile:e.double1===1}}function ve(e){return async(t,n)=>{let r=n.log_id||w(),i={...n,log_id:r};return ye(e,t,i),i}}function ye(e,t,n){if(!e.analyticsEngineBinding){console.error(`Analytics Engine binding not configured`);return}let r=e=>e==null?``:typeof e==`string`?e:JSON.stringify(e),i=(e,t=1024)=>e.substring(0,t);try{e.analyticsEngineBinding.writeDataPoint({blobs:[i(n.log_id),i(t),i(n.type||``),i(n.description||``),i(n.ip||``),i(n.user_agent||``),i(n.user_id||``),i(n.user_name||``),i(n.connection||``),i(n.connection_id||``),i(n.client_id||``),i(n.client_name||``),i(n.audience||``),i(n.scope||``),i(n.strategy||``),i(n.strategy_type||``),i(n.hostname||``),i(r(n.details)),i(r(n.auth0_client)),i(r(n.location_info))],doubles:[+!!n.isMobile,new Date(n.date).getTime()],indexes:[t.substring(0,96)]})}catch(e){console.error(`Failed to write log to Analytics Engine:`,e)}}function be(e){return async(t,n)=>{let r=await k(e,`
|
|
15
15
|
SELECT *
|
|
16
|
-
FROM ${
|
|
17
|
-
WHERE index1 = ${
|
|
18
|
-
AND blob1 = ${
|
|
16
|
+
FROM ${j(e.dataset||`authhero_logs`)}
|
|
17
|
+
WHERE index1 = ${A(t)}
|
|
18
|
+
AND blob1 = ${A(n)}
|
|
19
19
|
LIMIT 1
|
|
20
|
-
`);return r.length===0||!r[0]?null:
|
|
20
|
+
`);return r.length===0||!r[0]?null:M(r[0])}}function N(e){let t=e;return t.startsWith(`"`)&&t.endsWith(`"`)&&t.length>1&&(t=t.slice(1,-1)),(0,l.unescapeLuceneValue)(t)}function xe(e){let t={},n=[];for(let r of(0,l.tokenizeLuceneQuery)(e)){if(r===`OR`||r===`AND`)continue;let e=r.match(/^(\w+):([\s\S]*)$/);if(e){let n=e[1],r=N(e[2]);t[n]||(t[n]=[]),t[n].push(r)}else{let e=N(r);e&&n.push(e)}}return{fields:t,terms:n}}function P(e){return{log_id:`blob1`,tenant_id:`blob2`,type:`blob3`,description:`blob4`,ip:`blob5`,user_agent:`blob6`,user_id:`blob7`,user_name:`blob8`,connection:`blob9`,connection_id:`blob10`,client_id:`blob11`,client_name:`blob12`,audience:`blob13`,scope:`blob14`,strategy:`blob15`,strategy_type:`blob16`,hostname:`blob17`}[e]||null}function Se(e){let t=[];for(let[n,r]of Object.entries(e)){let e=r.filter(e=>e!==``);if(e.length===0)continue;if(n===`success`){let n=[],r=new Set;for(let t of e)r.has(t)||(r.add(t),t===`true`?n.push(`blob3 LIKE 's%'`):t===`false`&&n.push(`blob3 LIKE 'f%'`));n.length>0&&t.push(`(${n.join(` OR `)})`);continue}let i=P(n);if(i)if(e.length===1)t.push(`${i} = ${A(e[0])}`);else{let n=e.map(e=>A(e)).join(`, `);t.push(`${i} IN (${n})`)}}return t}function Ce(e){return e.filter(e=>e!==``).map(e=>{let t=A(e),n=A(`%${e}%`);return`(blob7 = ${t} OR blob5 LIKE ${n} OR blob4 LIKE ${n})`})}function we(e){return e===`date`?`double2`:P(e)||`timestamp`}function Te(e){return async(t,n={})=>{let{page:r=0,per_page:i=50,include_totals:a=!1,sort:o,q:s,from_date:c,to_date:l}=n,u=e.dataset||`authhero_logs`,d=[`index1 = ${A(t)}`];if(s){let{fields:e,terms:t}=xe(s);d.push(...Se(e)),d.push(...Ce(t))}typeof c==`number`&&Number.isFinite(c)&&d.push(`double2 >= ${Math.floor(c)*1e3}`),typeof l==`number`&&Number.isFinite(l)&&d.push(`double2 <= ${Math.floor(l)*1e3}`);let f=d.join(` AND `),p=`ORDER BY timestamp DESC`;o&&o.sort_by&&(p=`ORDER BY ${we(o.sort_by)} ${o.sort_order===`asc`?`ASC`:`DESC`}`);let m=r*i,h=`LIMIT ${i} OFFSET ${m}`,g=(await k(e,`
|
|
21
21
|
SELECT *
|
|
22
|
-
FROM ${
|
|
22
|
+
FROM ${j(u)}
|
|
23
23
|
WHERE ${f}
|
|
24
24
|
${p}
|
|
25
25
|
${h}
|
|
26
|
-
`)).map(
|
|
26
|
+
`)).map(M);if(!a)return{logs:g,start:0,limit:0,length:0};let _=`
|
|
27
27
|
SELECT count() as count
|
|
28
|
-
FROM ${
|
|
28
|
+
FROM ${j(u)}
|
|
29
29
|
WHERE ${f}
|
|
30
|
-
`;try{let t=(await
|
|
30
|
+
`;try{let t=(await k(e,_))[0]?.count||0;return{logs:g,start:m,limit:i,length:Number(t)}}catch{return{logs:g,start:m,limit:i,length:g.length}}}}var F=[`s`],Ee=[`pwd_leak`];function I(e){return/^\d{8}$/.test(e)?`${e.slice(0,4)}-${e.slice(4,6)}-${e.slice(6,8)}`:e}function L(e){return e.toISOString().split(`T`)[0]}function R(e,t){let n=new Date(`${e}T${t===`end`?`23:59:59.999`:`00:00:00.000`}Z`).getTime();if(Number.isNaN(n))return NaN;let r=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!r)return n;let i=new Date(n);return i.getUTCFullYear()!==Number(r[1])||i.getUTCMonth()+1!==Number(r[2])||i.getUTCDate()!==Number(r[3])?NaN:n}function z(e){let t=e.dataset||`authhero_logs`;return{async getDaily(n,r={}){let{from:i,to:a}=r,o=new Date,s=new Date(o);s.setDate(s.getDate()-30);let c=i?I(i):L(s),l=a?I(a):L(o),u=R(c,`start`),d=R(l,`end`);if(Number.isNaN(u)||Number.isNaN(d))throw Error(`Invalid stats date range: from='${c}' to='${l}'`);return(await k(e,`
|
|
31
31
|
SELECT
|
|
32
32
|
toDate(toDateTime(double2 / 1000)) AS date,
|
|
33
|
-
SUM(CASE WHEN blob3 IN (${
|
|
33
|
+
SUM(CASE WHEN blob3 IN (${F.map(e=>A(e)).join(`, `)}) THEN 1 ELSE 0 END) AS logins,
|
|
34
34
|
SUM(CASE WHEN blob3 = 'ss' THEN 1 ELSE 0 END) AS signups,
|
|
35
|
-
SUM(CASE WHEN blob3 IN (${
|
|
35
|
+
SUM(CASE WHEN blob3 IN (${Ee.map(e=>A(e)).join(`, `)}) THEN 1 ELSE 0 END) AS leaked_passwords,
|
|
36
36
|
MIN(double2) AS first_event,
|
|
37
37
|
MAX(double2) AS last_event
|
|
38
38
|
FROM "${t}"
|
|
39
|
-
WHERE index1 = ${
|
|
39
|
+
WHERE index1 = ${A(n)}
|
|
40
40
|
AND double2 >= ${u}
|
|
41
41
|
AND double2 <= ${d}
|
|
42
42
|
GROUP BY date
|
|
43
43
|
ORDER BY date ASC
|
|
44
|
-
`)).map(e=>({date:String(e.date),logins:Number(e.logins)||0,signups:Number(e.signups)||0,leaked_passwords:Number(e.leaked_passwords)||0,created_at:e.first_event?new Date(Number(e.first_event)).toISOString():new Date().toISOString(),updated_at:e.last_event?new Date(Number(e.last_event)).toISOString():new Date().toISOString()}))},async getActiveUsers(n){let r=new Date;r.setDate(r.getDate()-30);let i=r.getTime(),a=
|
|
44
|
+
`)).map(e=>({date:String(e.date),logins:Number(e.logins)||0,signups:Number(e.signups)||0,leaked_passwords:Number(e.leaked_passwords)||0,created_at:e.first_event?new Date(Number(e.first_event)).toISOString():new Date().toISOString(),updated_at:e.last_event?new Date(Number(e.last_event)).toISOString():new Date().toISOString()}))},async getActiveUsers(n){let r=new Date;r.setDate(r.getDate()-30);let i=r.getTime(),a=F.map(e=>A(e)).join(`, `),o=(await k(e,`
|
|
45
45
|
SELECT COUNT(DISTINCT blob7) AS count
|
|
46
46
|
FROM "${t}"
|
|
47
|
-
WHERE index1 = ${
|
|
47
|
+
WHERE index1 = ${A(n)}
|
|
48
48
|
AND double2 >= ${i}
|
|
49
49
|
AND blob3 IN (${a})
|
|
50
50
|
AND blob7 IS NOT NULL
|
|
51
51
|
AND blob7 != ''
|
|
52
|
-
`))[0];return o&&o.count!==void 0&&Number(o.count)||0}}}var
|
|
52
|
+
`))[0];return o&&o.count!==void 0&&Number(o.count)||0}}}var De={"active-users":[`s`,`seacft`],logins:[`s`,`f`,`fp`],signups:[`ss`,`fs`],"refresh-tokens":[`seacft`,`fertft`],sessions:[`slo`],logouts:[`slo`,`flo`],"password-changes":[`scp`,`fcp`,`scpr`,`fcpr`],"password-migrations":[`spm`],mfa:[`gd_auth_succeed`,`gd_auth_failed`,`gd_auth_rejected`],"email-verifications":[`sv`,`fv`,`svr`,`fvr`],"codes-sent":[`cls`,`cs`]},Oe={connection:`blob9`,client_id:`blob11`,user_type:`blob16`,event:`blob3`},ke={connection:`blob9`,client_id:`blob11`,user_type:`blob16`,user_id:`blob7`},Ae={"active-users":{expr:`count(DISTINCT blob7)`,alias:`active_users`,type:`UInt64`},logins:{expr:`count()`,alias:`logins`,type:`UInt64`},signups:{expr:`count()`,alias:`signups`,type:`UInt64`},"refresh-tokens":{expr:`count()`,alias:`refresh_tokens`,type:`UInt64`},sessions:{expr:`count()`,alias:`sessions`,type:`UInt64`},logouts:{expr:`count()`,alias:`logouts`,type:`UInt64`},"password-changes":{expr:`count()`,alias:`password_changes`,type:`UInt64`},"password-migrations":{expr:`count()`,alias:`password_migrations`,type:`UInt64`},mfa:{expr:`count()`,alias:`mfa`,type:`UInt64`},"email-verifications":{expr:`count()`,alias:`email_verifications`,type:`UInt64`},"codes-sent":{expr:`count()`,alias:`codes_sent`,type:`UInt64`}};function je(e,t){let n=`toDateTime(intDiv(double2, 1000), ${A(t)})`;switch(e){case`hour`:return`toStartOfHour(${n})`;case`week`:return`toStartOfWeek(${n})`;case`month`:return`toStartOfMonth(${n})`;default:return`toStartOfDay(${n})`}}function Me(e){return e===`hour`?`DateTime`:`Date`}function Ne(e,t,n){let r=new Date(e.from).getTime(),i=new Date(e.to).getTime(),a=n.map(e=>A(e)).join(`, `),o=[`index1 = ${A(t)}`,`double2 >= ${r}`,`double2 < ${i}`,`blob3 IN (${a})`];for(let[t,n]of Object.entries(ke)){let r=e.filters[t];if(r&&r.length>0){let e=r.map(e=>A(e)).join(`, `);o.push(`${n} IN (${e})`)}}return o.join(` AND `)}function Pe(e,t,n){if(e.order_by){let t=e.order_by.startsWith(`-`);return`${j(t?e.order_by.slice(1):e.order_by)} ${t?`DESC`:`ASC`}`}return t.length>0&&t[0]===`time`?`${j(`time`)} ASC`:`${j(n)} DESC`}function B(e){let t=e.dataset||`authhero_logs`;return{async query(n,r,i){let a=De[r],o=Ae[r],s=[],c=[];for(let e of i.group_by)if(e===`time`){let e=je(i.interval,i.tz);s.push(`${e} AS ${j(`time`)}`),c.push({name:`time`,type:Me(i.interval)})}else{let t=Oe[e];s.push(`${t} AS ${j(e)}`),c.push({name:e,type:`String`})}c.push({name:o.alias,type:o.type});let l=[...s,`${o.expr} AS ${j(o.alias)}`],u=i.group_by.length?`GROUP BY ${i.group_by.map(e=>j(e===`time`?`time`:e)).join(`, `)}`:``,d=Pe(i,i.group_by.map(e=>e===`time`?`time`:e),o.alias),f=`
|
|
53
53
|
SELECT ${l.join(`, `)}
|
|
54
|
-
FROM ${
|
|
55
|
-
WHERE ${
|
|
54
|
+
FROM ${j(t)}
|
|
55
|
+
WHERE ${Ne(i,n,a)}
|
|
56
56
|
${u}
|
|
57
57
|
ORDER BY ${d}
|
|
58
58
|
LIMIT ${Math.max(0,i.limit)} OFFSET ${Math.max(0,i.offset)}
|
|
59
59
|
FORMAT JSON
|
|
60
|
-
`,p=`https://api.cloudflare.com/client/v4/accounts/${e.accountId}/analytics_engine/sql`,m=e.timeout||3e4,h=new AbortController,g=setTimeout(()=>h.abort(),m),_=Date.now();try{let t=await fetch(p,{method:`POST`,headers:{"Content-Type":`text/plain`,Authorization:`Bearer ${e.apiToken}`},body:f,signal:h.signal});if(!t.ok){let e=await t.text();throw Error(`Analytics Engine query failed: ${t.status} ${t.statusText} - ${e}`)}let n=await t.json();if(n.success===!1&&n.errors?.length)throw Error(`Analytics Engine error: ${n.errors.map(e=>e.message).join(`, `)}`);let r=n.data??[];return{meta:n.meta??c,data:r,rows:n.rows??r.length,rows_before_limit_at_least:n.rows_before_limit_at_least??r.length,statistics:{elapsed:(Date.now()-_)/1e3}}}finally{clearTimeout(g)}}}}function
|
|
60
|
+
`,p=`https://api.cloudflare.com/client/v4/accounts/${e.accountId}/analytics_engine/sql`,m=e.timeout||3e4,h=new AbortController,g=setTimeout(()=>h.abort(),m),_=Date.now();try{let t=await fetch(p,{method:`POST`,headers:{"Content-Type":`text/plain`,Authorization:`Bearer ${e.apiToken}`},body:f,signal:h.signal});if(!t.ok){let e=await t.text();throw Error(`Analytics Engine query failed: ${t.status} ${t.statusText} - ${e}`)}let n=await t.json();if(n.success===!1&&n.errors?.length)throw Error(`Analytics Engine error: ${n.errors.map(e=>e.message).join(`, `)}`);let r=n.data??[];return{meta:n.meta??c,data:r,rows:n.rows??r.length,rows_before_limit_at_least:n.rows_before_limit_at_least??r.length,statistics:{elapsed:(Date.now()-_)/1e3}}}finally{clearTimeout(g)}}}}function V(e){return e.analyticsEngineBinding||console.warn(`Analytics Engine: No binding configured. Logs will not be written to Analytics Engine.`),(!e.accountId||!e.apiToken)&&console.warn(`Analytics Engine: accountId and apiToken are required for querying logs via SQL API.`),{create:ve(e),list:Te(e),get:be(e)}}var Fe=`authhero_action_executions`,Ie=1024,Le=`[truncated]`;function H(e){if(!(typeof e!=`string`||e.length===0))try{return JSON.parse(e)}catch{return}}function Re(e){let t=l.actionExecutionStatusSchema.safeParse(e);return t.success?t.data:`unspecified`}function U(e,t=Ie){return e.length<=t?e:`${e.substring(0,t-11)}${Le}`}function W(e){return e==null?``:JSON.stringify(e)}function ze(e){let t=typeof e.blob6==`string`&&e.blob6.length>0?e.blob6:typeof e.double1==`number`?new Date(e.double1).toISOString():``,n=typeof e.blob7==`string`&&e.blob7.length>0?e.blob7:t;return{id:typeof e.blob1==`string`?e.blob1:``,tenant_id:typeof e.index1==`string`?e.index1:``,trigger_id:typeof e.blob2==`string`?e.blob2:``,status:Re(e.blob3),results:H(e.blob4)??[],logs:H(e.blob5),created_at:t,updated_at:n}}function Be(e){return async(t,n)=>{let r=Date.now(),i=new Date(r).toISOString(),a={id:n.id,tenant_id:t,trigger_id:n.trigger_id,status:n.status,results:n.results,logs:n.logs,created_at:i,updated_at:i};return Ve(e,t,a,r),a}}function Ve(e,t,n,r){if(!e.analyticsEngineBinding){console.error(`Analytics Engine action_executions binding not configured; skipping write`);return}try{e.analyticsEngineBinding.writeDataPoint({blobs:[U(n.id),U(n.trigger_id),U(n.status),U(W(n.results)),U(W(n.logs)),U(n.created_at),U(n.updated_at)],doubles:[r],indexes:[t.substring(0,96)]})}catch(e){console.error(`Failed to write action_execution to Analytics Engine:`,e)}}function He(e){return async(t,n)=>{let r=await k(e,`
|
|
61
61
|
SELECT *
|
|
62
|
-
FROM ${
|
|
63
|
-
WHERE index1 = ${
|
|
64
|
-
AND blob1 = ${
|
|
62
|
+
FROM ${j(e.dataset||Fe)}
|
|
63
|
+
WHERE index1 = ${A(t)}
|
|
64
|
+
AND blob1 = ${A(n)}
|
|
65
65
|
LIMIT 1
|
|
66
|
-
`);return r.length===0||!r[0]?null:
|
|
66
|
+
`);return r.length===0||!r[0]?null:ze(r[0])}}function G(e){return e.analyticsEngineBinding||console.warn(`Analytics Engine: No action_executions binding configured. Executions will not be written.`),(!e.accountId||!e.apiToken)&&console.warn(`Analytics Engine: accountId and apiToken are required to read action_executions via the SQL API.`),{create:Be(e),get:He(e)}}function Ue(){return{async getGeoInfo(e){try{let t=e[`cf-ipcountry`],n=e[`cf-ipcity`],r=e[`cf-iplatitude`],i=e[`cf-iplongitude`],a=e[`cf-timezone`],o=e[`cf-ipcontinent`];return t?{country_code:t,city_name:n||``,latitude:r||``,longitude:i||``,time_zone:a||``,continent_code:o||``}:null}catch(e){return console.warn(`Failed to get geo info from Cloudflare headers:`,e),null}}}}var We=class{bindings;constructor(e){this.bindings=e}async consume(e,t){let n=this.bindings[e];if(!n)return{allowed:!0};try{let{success:e}=await n.limit({key:t});return{allowed:e}}catch(t){return console.error(`CloudflareRateLimit: limit() error for scope ${e}:`,t),{allowed:!0}}}};function K(e){if(e&&Object.keys(e).some(t=>e[t]))return new We(e)}var Ge=5,Ke=1e3,q=50;async function qe(e,t={}){let n=Math.trunc(t.perPage??q),r=Number.isFinite(n)?Math.min(Ke,Math.max(Ge,n)):q,i=t.maxPages??200,a={scanned:0,matched:0,updated:0,unknown:0,mismatched:0,errors:0};for(let t=1;t<=i;t++){let n;try{n=await x(e).get(`/custom_hostnames?page=${t}&per_page=${r}`).json()}catch(n){return console.warn(`[custom-domains] sync failed to list page ${t} of zone ${e.zoneId}; stopping the sweep:`,n instanceof Error?n.message:n),a.errors++,a}let i=b.safeParse(n);if(!i.success||!i.data.success)return console.warn(`[custom-domains] sync got an unparseable listing for page ${t} of zone ${e.zoneId}; stopping the sweep.`,i.success?{cfErrors:i.data.errors}:{zodIssues:i.error.issues}),a.errors++,a;let o=i.data.result;for(let t of o){a.scanned++;try{let n=await e.customDomainAdapter.getByDomain(t.hostname);if(!n){a.unknown++;continue}if(e.enterprise&&t.custom_metadata?.tenant_id!==n.tenant_id){console.warn(`[custom-domains] sync skipping ${t.hostname}: zone says tenant=${t.custom_metadata?.tenant_id}, database says tenant=${n.tenant_id}.`),a.mismatched++;continue}if(n.custom_domain_id!==t.id){console.warn(`[custom-domains] sync skipping ${t.hostname} (tenant=${n.tenant_id}): stored id ${n.custom_domain_id} no longer matches the zone's ${t.id}.`),a.mismatched++;continue}a.matched++;let{outcome:r}=await ae(e,n.tenant_id,n,t);r===`updated`?a.updated++:r===`failed`&&a.errors++}catch(e){console.warn(`[custom-domains] sync failed for ${t.hostname}:`,e instanceof Error?e.message:e),a.errors++}}if(o.length<r)return a}return console.warn(`[custom-domains] sync hit the ${i}-page limit for zone ${e.zoneId}; the tail of the zone was not swept.`),a}function J(e){return`// Auto-generated AuthHero code hook worker
|
|
67
67
|
|
|
68
68
|
const fnNames = {
|
|
69
69
|
"post-user-login": "onExecutePostLogin",
|
|
@@ -150,9 +150,9 @@ export default {
|
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
152
|
};
|
|
153
|
-
`}var
|
|
154
|
-
const TRIGGER_FN_NAMES = ${JSON.stringify(
|
|
155
|
-
const API_SHAPES = ${JSON.stringify(
|
|
153
|
+
`}var Y=class{config;constructor(e){this.config=e}async execute(e){let t=Date.now();if(!e.hookCodeId)return{success:!1,error:`DispatchNamespaceCodeExecutor requires hookCodeId`,durationMs:Date.now()-t,apiCalls:[]};let n=`hook-${e.hookCodeId}`,r=JSON.stringify({triggerId:e.triggerId,event:e.event});try{let i;if(this.config.dispatcher)i=await this.config.dispatcher.get(n,{},{limits:{cpuMs:e.cpuLimitMs??5e3}}).fetch(new Request(`https://hook.internal/execute`,{method:`POST`,headers:{"Content-Type":`application/json`},body:r}));else if(this.config.dispatchUrl)i=await fetch(`${this.config.dispatchUrl}/${n}`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.config.apiToken}`},body:r});else return{success:!1,error:`No dispatcher binding or dispatchUrl configured`,durationMs:Date.now()-t,apiCalls:[]};if(!i.ok){let e=await i.text();return{success:!1,error:`Worker invocation failed (${i.status}): ${e}`,durationMs:Date.now()-t,apiCalls:[]}}return{...await i.json(),durationMs:Date.now()-t}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e),durationMs:Date.now()-t,apiCalls:[]}}}async deploy(e,t){let n=`hook-${e}`,r=J(t),i=`https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/workers/dispatch/namespaces/${this.config.dispatchNamespace}/scripts/${n}`,a=JSON.stringify({main_module:`index.js`,compatibility_date:this.config.compatibilityDate??`2024-11-20`}),o=new FormData;o.append(`metadata`,new Blob([a],{type:`application/json`})),o.append(`index.js`,new Blob([r],{type:`application/javascript+module`}),`index.js`);let s=await fetch(i,{method:`PUT`,headers:{Authorization:`Bearer ${this.config.apiToken}`},body:o});if(!s.ok){let e=await s.text();throw Error(`Failed to deploy hook worker ${n}: ${s.status} ${e}`)}}async remove(e){let t=`hook-${e}`,n=`https://api.cloudflare.com/client/v4/accounts/${this.config.accountId}/workers/dispatch/namespaces/${this.config.dispatchNamespace}/scripts/${t}`,r=await fetch(n,{method:`DELETE`,headers:{Authorization:`Bearer ${this.config.apiToken}`}});if(!r.ok&&r.status!==404){let e=await r.text();throw Error(`Failed to remove hook worker ${t}: ${r.status} ${e}`)}}},Je=Y,Ye={"post-user-login":`onExecutePostLogin`,"credentials-exchange":`onExecuteCredentialsExchange`,"pre-user-registration":`onExecutePreUserRegistration`,"post-user-registration":`onExecutePostUserRegistration`},Xe=l.TRIGGER_API_SHAPES;async function Ze(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest(`SHA-256`,t),r=new Uint8Array(n),i=``;for(let e of r)i+=e.toString(16).padStart(2,`0`);return i}function Qe(e){return`
|
|
154
|
+
const TRIGGER_FN_NAMES = ${JSON.stringify(Ye)};
|
|
155
|
+
const API_SHAPES = ${JSON.stringify(Xe)};
|
|
156
156
|
const MAX_LOG_ENTRIES = 50;
|
|
157
157
|
const MAX_LOG_LENGTH = 500;
|
|
158
158
|
|
|
@@ -250,4 +250,4 @@ export default {
|
|
|
250
250
|
}
|
|
251
251
|
},
|
|
252
252
|
};
|
|
253
|
-
`}var
|
|
253
|
+
`}var $e=class{loader;compatibilityDate;constructor(e){this.loader=e.loader,this.compatibilityDate=e.compatibilityDate||`2025-01-01`}async execute(e){let t=Date.now();try{let t=Qe(e.code),n={compatibilityDate:this.compatibilityDate,mainModule:`hook.js`,modules:{"hook.js":t}},r=e.hookCodeId?`${e.hookCodeId}-${await Ze(e.code)}`:null;return await(await(r?this.loader.get(r,async()=>n):this.loader.load(n)).getEntrypoint().fetch(new Request(`https://hook/execute`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({event:e.event,triggerId:e.triggerId})}))).json()}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e),durationMs:Date.now()-t,apiCalls:[]}}}},X=new TextEncoder;function Z(e,t){if(X.encode(e).length<=t)return e;let n=0,r=``;for(let i of e){let e=X.encode(i).length;if(n+e>t)break;n+=e,r+=i}return r}var Q=(e,t=1024)=>Z(e,t);function et(e){let t=e.analyticsEngineBinding;return e=>{if(t)try{t.writeDataPoint({blobs:[Q(e.name),Q(e.tenantId),Q(e.eventType),Q(e.source),Q(e.destination??``),Q(e.error??``)],doubles:[e.value,e.retryCount??0,Date.now()],indexes:[Z(e.tenantId,96)]})}catch(e){console.error(`Failed to write outbox metric to Analytics Engine:`,e)}}}function tt(e){let t=c.n(e);return{async onProvision(e){let{scriptName:n,databaseName:r}=t.names(e),{databaseVersion:i,bundleConfiguration:a,workerVersion:o}=t.validate(),{id:s,created:c}=await t.findOrCreateDatabase(r);return await t.applyMigrations(s,c),await t.uploadScript(n,s),await t.uploadSecrets(n,e),{d1DatabaseId:s,scriptName:n,d1Name:r,bundleConfiguration:a,workerVersion:o,databaseVersion:i}},async onDeprovision(e){await t.deprovision(e)}}}function nt(e){return e.deployment_type===`wfp`}var rt=2048;function $(e){let t;if(e instanceof Error)t=e.message;else try{t=String(e)}catch{t=Object.prototype.toString.call(e)}return t.slice(0,rt)}function it(e){let{provisioner:t,tenants:n,syncDefaults:r}=e,i=e.shouldProvision??nt,a=e.logger;async function o(e,t){try{await n.update(e,{provisioning_state:`failed`,provisioning_error:$(t),provisioning_state_changed_at:new Date().toISOString()})}catch(t){a?.warn(`Failed to write provisioning_state="failed" for tenant ${e}:`,t)}}async function s(e,i){let s=async(t,n,r)=>{if(i)try{await i(t,n,r)}catch(n){a?.warn(`Failed to report provisioning step "${t}" for tenant ${e}:`,n)}};await s(`provision-resources`,`started`);let l;try{l=await t.onProvision(e)}catch(t){throw await s(`provision-resources`,`failed`,{message:$(t)}),await o(e,t),t}let u={d1_database_id:l.d1DatabaseId,worker_script_name:l.scriptName,bundle_configuration:l.bundleConfiguration,worker_version:l.workerVersion,database_version:l.databaseVersion};await s(`provision-resources`,`succeeded`,{...u});try{if(r){await s(`seed-defaults`,`started`);try{let t=c.t(await r(e));if(t.length>0)throw Error(`sync-defaults seed reported ${t.length} error(s): ${t.join(`; `)}`);await s(`seed-defaults`,`succeeded`)}catch(e){throw await s(`seed-defaults`,`failed`,{message:$(e)}),e}}await n.update(e,{...u,provisioning_state:`ready`,provisioning_error:void 0,provisioning_state_changed_at:new Date().toISOString()})}catch(t){try{await n.update(e,{...u,provisioning_state:`failed`,provisioning_error:$(t),provisioning_state_changed_at:new Date().toISOString()})}catch(t){a?.warn(`Failed to write provisioning_state="failed" for tenant ${e}:`,t)}throw t}}return{async onProvision(e,t){let r=await n.get(e);r&&i(r)&&await s(e,t)},async onUpgrade(e,t){let r=await n.get(e);if(!r)throw Error(`Cannot upgrade tenant "${e}": not found.`);if(!i(r))throw Error(`Cannot upgrade tenant "${e}": not a WFP-provisioned tenant.`);try{await n.update(e,{provisioning_state:`pending`,provisioning_error:void 0,provisioning_state_changed_at:new Date().toISOString()})}catch(t){a?.warn(`Failed to write provisioning_state="pending" for tenant ${e}:`,t)}await s(e,t)},async onDeprovision(e){let r=await n.get(e);r&&!i(r)||await t.onDeprovision(e)}}}var at=`DISPATCHER`,ot=`tenant-{tenant_id}-auth`,st=[`/u/widget/`];function ct(e){return typeof e==`object`&&!!e&&`get`in e&&typeof Reflect.get(e,`get`)==`function`}function lt(e,t){return e.replace(/\{tenant_id\}/g,t)}function ut(e){let{tenants:t,controlPlaneTenantId:n,dispatcherBinding:r=at,scriptNameTemplate:i=ot,resolveTenantId:a=e=>e.req.header(`tenant-id`),localPaths:o=st}=e;return async(e,s)=>{if(o.length>0){let t=e.req.path;if(o.some(e=>t.startsWith(e)))return s()}let c=await a(e);if(!c||c===n)return s();let l=await t.get(c);if(!l||l.deployment_type!==`wfp`||l.provisioning_state&&l.provisioning_state!==`ready`)return s();let u=Reflect.get(e.env??{},r);if(!ct(u))return s();let d=lt(i,c),f;try{f=await u.get(d).fetch(e.req.raw)}catch(t){let n=t instanceof Error?t.message:String(t),r=/not\s*found|no\s*such|does not exist/i.test(n),i=r?`wfp_worker_not_found`:`wfp_dispatch_failed`;return console.error(`[wfp-forward] ${i} tenant=${c} script=${d}: ${n}`),e.header(`X-Authhero-Error`,i),e.header(`X-Wfp-Tenant`,c),e.json({error:i,detail:r?`Tenant '${c}' is marked ready but its worker '${d}' is not deployed in the dispatch namespace.`:`The worker for tenant '${c}' could not be reached.`,tenant_id:c},r?503:502)}if(f.status>=500){let e=f.headers.get(`X-Authhero-Error`);console.error(`[wfp-forward] tenant worker ${f.status}${e?` (${e})`:``} tenant=${c} script=${d}`)}let p=Reflect.get(f,`webSocket`);if(f.status===101||p!=null)return f;let m=new Response(f.body,f);return m.headers.set(`X-Wfp-Tenant`,c),m}}function dt(e){let t={customDomains:oe(e),cache:ce({...e.cacheName&&{cacheName:e.cacheName},...e.defaultTtlSeconds!==void 0&&{defaultTtlSeconds:e.defaultTtlSeconds},...e.keyPrefix&&{keyPrefix:e.keyPrefix}}),geo:Ue()};e.r2SqlLogs?t.logs=_e(e.r2SqlLogs):e.analyticsEngineLogs&&(t.logs=V(e.analyticsEngineLogs)),e.analyticsEngineLogs&&(t.analytics=B(e.analyticsEngineLogs),t.stats=z(e.analyticsEngineLogs)),e.analyticsEngineActionExecutions&&(t.actionExecutions=G(e.analyticsEngineActionExecutions));let n=K(e.rateLimitBindings);return n&&(t.rateLimit=n),t}exports.CloudflareApiClient=c.i,exports.CloudflareApiError=c.a,exports.CloudflareCodeExecutor=Je,exports.DispatchNamespaceCodeExecutor=Y,exports.WorkerLoaderCodeExecutor=$e,exports.createAnalyticsEngineActionExecutionsAdapter=G,exports.createAnalyticsEngineAnalyticsAdapter=B,exports.createAnalyticsEngineLogsAdapter=V,exports.createAnalyticsEngineOutboxMetricsSink=et,exports.createAnalyticsEngineStatsAdapter=z,exports.createCloudflareRateLimitAdapter=K,exports.createCloudflareWfpD1Provisioner=tt,exports.createR2SQLLogsAdapter=_e,exports.createR2SQLStatsAdapter=ge,exports.createWfpForwardMiddleware=ut,exports.createWfpProvisionerSteps=c.n,exports.createWfpTenantProvisioningHook=it,exports.default=dt,exports.generateWorkerScript=J,exports.syncCustomDomains=qe;
|
|
@@ -336,6 +336,84 @@ interface CloudflareConfig {
|
|
|
336
336
|
rateLimitBindings?: CloudflareRateLimitBindings;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
interface SyncCustomDomainsOptions {
|
|
340
|
+
/**
|
|
341
|
+
* Hostnames fetched per Cloudflare API call. Cloudflare accepts 5 to 1000
|
|
342
|
+
* (its own default is 20); values outside that are clamped rather than
|
|
343
|
+
* rejected, since a cron should not die on a config typo. Defaults to 50.
|
|
344
|
+
*/
|
|
345
|
+
perPage?: number;
|
|
346
|
+
/**
|
|
347
|
+
* Safety stop, in pages. A zone larger than `perPage * maxPages` is swept
|
|
348
|
+
* partially rather than looping forever on a paginating API that never
|
|
349
|
+
* reports a short page. Default 200 (10 000 hostnames at the default size).
|
|
350
|
+
*/
|
|
351
|
+
maxPages?: number;
|
|
352
|
+
}
|
|
353
|
+
interface SyncCustomDomainsResult {
|
|
354
|
+
/** Hostnames returned by Cloudflare across every page. */
|
|
355
|
+
scanned: number;
|
|
356
|
+
/** Hostnames that matched a stored custom-domain row. */
|
|
357
|
+
matched: number;
|
|
358
|
+
/** Rows whose `status` or `verification` actually changed. */
|
|
359
|
+
updated: number;
|
|
360
|
+
/**
|
|
361
|
+
* Hostnames in the zone with no stored row. Expected to be 0 in a zone
|
|
362
|
+
* AuthHero owns exclusively; a non-zero count means either a hostname
|
|
363
|
+
* registered outside AuthHero or a `create` that died between the Cloudflare
|
|
364
|
+
* call and the DB write.
|
|
365
|
+
*/
|
|
366
|
+
unknown: number;
|
|
367
|
+
/**
|
|
368
|
+
* Stored rows whose `custom_domain_id` no longer matches the hostname's id
|
|
369
|
+
* at the edge — the hostname was deleted and re-registered behind our back.
|
|
370
|
+
* Left untouched: adopting the new id would rewrite a primary key that
|
|
371
|
+
* `proxy_routes` and the KV host blobs point at.
|
|
372
|
+
*/
|
|
373
|
+
mismatched: number;
|
|
374
|
+
/**
|
|
375
|
+
* Hostnames that could not be reconciled — a throw, or a merge/writeback
|
|
376
|
+
* that failed without throwing. Each one is logged. A sweep reporting
|
|
377
|
+
* `updated: 0` is only healthy when this is 0 too.
|
|
378
|
+
*/
|
|
379
|
+
errors: number;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Reconcile every custom hostname in the Cloudflare zone against the stored
|
|
383
|
+
* custom-domain rows.
|
|
384
|
+
*
|
|
385
|
+
* Without this, `status` and `verification` only ever refresh when someone
|
|
386
|
+
* reads a single domain by id (`get()`): `list()` and `getByDomain()` are
|
|
387
|
+
* deliberately DB-only so they stay fast and survive a Cloudflare outage. A
|
|
388
|
+
* hostname that finishes validation at the edge therefore stays `pending` in
|
|
389
|
+
* the database until a human happens to open its detail page. This closes that
|
|
390
|
+
* gap on a schedule.
|
|
391
|
+
*
|
|
392
|
+
* Enumerates Cloudflare-first — one paginated list call per 50 hostnames,
|
|
393
|
+
* rather than one request per stored domain — then resolves each hostname's
|
|
394
|
+
* tenant through `getByDomain`, which every adapter indexes because it is the
|
|
395
|
+
* request-routing path.
|
|
396
|
+
*
|
|
397
|
+
* One hostname's failure never aborts the sweep, and the result is returned
|
|
398
|
+
* rather than thrown: a cron that dies halfway leaves the rest of the zone
|
|
399
|
+
* stale until the next run.
|
|
400
|
+
*
|
|
401
|
+
* Deletion is deliberately out of scope. A hostname removed at the edge simply
|
|
402
|
+
* stops appearing in the listing, and "absent from a page I may have failed to
|
|
403
|
+
* fetch" is not evidence a domain is gone — removals go through `remove()`.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* export default {
|
|
408
|
+
* async scheduled(event, env) {
|
|
409
|
+
* const config = buildCloudflareConfig(env);
|
|
410
|
+
* console.log("custom domain sync", await syncCustomDomains(config));
|
|
411
|
+
* },
|
|
412
|
+
* };
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
declare function syncCustomDomains(config: CloudflareConfig, options?: SyncCustomDomainsOptions): Promise<SyncCustomDomainsResult>;
|
|
416
|
+
|
|
339
417
|
/**
|
|
340
418
|
* Cloudflare Workers for Platforms dispatch namespace binding type.
|
|
341
419
|
* This is the type of `env.DISPATCHER` when configured in wrangler.toml:
|
|
@@ -509,6 +587,67 @@ declare class WorkerLoaderCodeExecutor implements CodeExecutor {
|
|
|
509
587
|
}): Promise<CodeExecutionResult>;
|
|
510
588
|
}
|
|
511
589
|
|
|
590
|
+
/**
|
|
591
|
+
* One metric emission from the authhero outbox relay.
|
|
592
|
+
*
|
|
593
|
+
* Structurally identical to authhero's `OutboxMetric`. It is redeclared here
|
|
594
|
+
* so this adapter keeps working without `authhero` installed — it is only an
|
|
595
|
+
* optional peer dependency of this package.
|
|
596
|
+
*/
|
|
597
|
+
interface OutboxMetricRecord {
|
|
598
|
+
/**
|
|
599
|
+
* `outbox_events_processed_total`, `outbox_events_dead_lettered_total` or
|
|
600
|
+
* `outbox_retry_delay_seconds`.
|
|
601
|
+
*/
|
|
602
|
+
name: string;
|
|
603
|
+
/** Counter increment, or the observed retry delay in seconds. */
|
|
604
|
+
value: number;
|
|
605
|
+
tenantId: string;
|
|
606
|
+
eventType: string;
|
|
607
|
+
source: "request" | "cron";
|
|
608
|
+
destination?: string;
|
|
609
|
+
error?: string;
|
|
610
|
+
retryCount?: number;
|
|
611
|
+
}
|
|
612
|
+
interface AnalyticsEngineOutboxMetricsConfig {
|
|
613
|
+
/**
|
|
614
|
+
* Cloudflare Analytics Engine dataset binding (e.g. `env.OUTBOX_METRICS`).
|
|
615
|
+
* When absent the sink is a no-op, so the same wiring works locally.
|
|
616
|
+
*/
|
|
617
|
+
analyticsEngineBinding?: AnalyticsEngineDataset;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Create an Analytics Engine sink for outbox relay metrics.
|
|
621
|
+
*
|
|
622
|
+
* Pass the returned function to `init({ outbox: { metrics } })` and to
|
|
623
|
+
* `runOutboxRelay({ metrics })` so both the inline per-request relay and the
|
|
624
|
+
* cron drain report to the same dataset. Rows are indexed by tenant, matching
|
|
625
|
+
* `createAnalyticsEngineLogsAdapter`.
|
|
626
|
+
*
|
|
627
|
+
* Column layout:
|
|
628
|
+
* - blob1 `name`, blob2 `tenant_id`, blob3 `event_type`, blob4 `source`,
|
|
629
|
+
* blob5 `destination`, blob6 `error`
|
|
630
|
+
* - double1 `value`, double2 `retry_count`, double3 `timestamp` (ms)
|
|
631
|
+
* - index1 `tenant_id`
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* // wrangler.toml:
|
|
636
|
+
* // [[analytics_engine_datasets]]
|
|
637
|
+
* // binding = "OUTBOX_METRICS"
|
|
638
|
+
* // dataset = "authhero_outbox_metrics"
|
|
639
|
+
*
|
|
640
|
+
* import { createAnalyticsEngineOutboxMetricsSink } from "@authhero/cloudflare-adapter";
|
|
641
|
+
*
|
|
642
|
+
* const metrics = createAnalyticsEngineOutboxMetricsSink({
|
|
643
|
+
* analyticsEngineBinding: env.OUTBOX_METRICS,
|
|
644
|
+
* });
|
|
645
|
+
*
|
|
646
|
+
* const app = init({ dataAdapter, outbox: { enabled: true, metrics } });
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
declare function createAnalyticsEngineOutboxMetricsSink(config: AnalyticsEngineOutboxMetricsConfig): (metric: OutboxMetricRecord) => void;
|
|
650
|
+
|
|
512
651
|
/**
|
|
513
652
|
* Thin Cloudflare REST API client for the WFP+D1 provisioner.
|
|
514
653
|
*
|
|
@@ -1074,5 +1213,5 @@ interface CloudflareAdapters {
|
|
|
1074
1213
|
}
|
|
1075
1214
|
declare function createAdapters(config: CloudflareConfig): CloudflareAdapters;
|
|
1076
1215
|
|
|
1077
|
-
export { CloudflareApiClient, CloudflareApiError, CloudflareCodeExecutor, DispatchNamespaceCodeExecutor, WorkerLoaderCodeExecutor, createAnalyticsEngineActionExecutionsAdapter, createAnalyticsEngineAnalyticsAdapter, createAnalyticsEngineLogsAdapter, createAnalyticsEngineStatsAdapter, createCloudflareRateLimitAdapter, createCloudflareWfpD1Provisioner, createR2SQLLogsAdapter, createR2SQLStatsAdapter, createWfpForwardMiddleware, createWfpProvisionerSteps, createWfpTenantProvisioningHook, createAdapters as default, generateWorkerScript };
|
|
1078
|
-
export type { AnalyticsEngineActionExecutionsAdapterConfig, AnalyticsEngineDataset, AnalyticsEngineLogsAdapterConfig, CfApiClientOptions, CloudflareAdapters, CloudflareCodeExecutorConfig, CloudflareConfig, CloudflareRateLimitBinding, CloudflareRateLimitBindings, CloudflareWfpD1Provisioner, CloudflareWfpD1ProvisionerOptions, D1Database, D1QueryResult, DispatchNamespace, DispatchNamespaceCodeExecutorConfig, ProvisionResult, ProvisionerMigration, R2SQLLogsAdapterConfig, ScriptBinding, ScriptUploadOptions, TenantProvisionNames, TenantProvisionerSteps, TenantSecretsResolver, WfpForwardOptions, WfpProvisionerSteps, WfpTenantProvisioningHook, WfpTenantProvisioningHookOptions, WorkerCode, WorkerLoader, WorkerLoaderCodeExecutorOptions, WorkerStub };
|
|
1216
|
+
export { CloudflareApiClient, CloudflareApiError, CloudflareCodeExecutor, DispatchNamespaceCodeExecutor, WorkerLoaderCodeExecutor, createAnalyticsEngineActionExecutionsAdapter, createAnalyticsEngineAnalyticsAdapter, createAnalyticsEngineLogsAdapter, createAnalyticsEngineOutboxMetricsSink, createAnalyticsEngineStatsAdapter, createCloudflareRateLimitAdapter, createCloudflareWfpD1Provisioner, createR2SQLLogsAdapter, createR2SQLStatsAdapter, createWfpForwardMiddleware, createWfpProvisionerSteps, createWfpTenantProvisioningHook, createAdapters as default, generateWorkerScript, syncCustomDomains };
|
|
1217
|
+
export type { AnalyticsEngineActionExecutionsAdapterConfig, AnalyticsEngineDataset, AnalyticsEngineLogsAdapterConfig, AnalyticsEngineOutboxMetricsConfig, CfApiClientOptions, CloudflareAdapters, CloudflareCodeExecutorConfig, CloudflareConfig, CloudflareRateLimitBinding, CloudflareRateLimitBindings, CloudflareWfpD1Provisioner, CloudflareWfpD1ProvisionerOptions, D1Database, D1QueryResult, DispatchNamespace, DispatchNamespaceCodeExecutorConfig, OutboxMetricRecord, ProvisionResult, ProvisionerMigration, R2SQLLogsAdapterConfig, ScriptBinding, ScriptUploadOptions, SyncCustomDomainsOptions, SyncCustomDomainsResult, TenantProvisionNames, TenantProvisionerSteps, TenantSecretsResolver, WfpForwardOptions, WfpProvisionerSteps, WfpTenantProvisioningHook, WfpTenantProvisioningHookOptions, WorkerCode, WorkerLoader, WorkerLoaderCodeExecutorOptions, WorkerStub };
|