@@ -66,11 +66,54 @@ function Diagnostics({ client, onNavigate, docsBase }: DiagnosticsProps) {
6666 const [ testJobId , setTestJobId ] = useState < string | null > ( null ) ;
6767 const [ testStatus , setTestStatus ] = useState < string | null > ( null ) ;
6868 const [ expandedSections , setExpandedSections ] = useState < string [ ] > ( [ 'summary' ] ) ;
69+ const [ anchors , setAnchors ] = useState < Record < string , string > > ( { } ) ;
6970
7071 const theme = useTheme ( ) ;
7172 const isMobile = useMediaQuery ( theme . breakpoints . down ( 'md' ) ) ;
7273 const isSmallMobile = useMediaQuery ( theme . breakpoints . down ( 'sm' ) ) ;
7374 const { active, registry } = useTraits ( ) ;
75+ const thirdParty : Record < string , string > = {
76+ 'sinch-build-access-keys-location' : 'https://dashboard.sinch.com/settings/access-keys' ,
77+ 'sinch-oauth-client-credentials-flow' : 'https://developers.sinch.com/docs/fax/api-reference/authentication/oauth/' ,
78+ 'sinch-regional-base-url' : 'https://developers.sinch.com/docs/fax/api-reference/#global-url' ,
79+ 'sinch-inbound-webhook-url' : 'https://developers.sinch.com/docs/fax/api-reference/fax/tag/Notifications/#incoming-fax-event-webhook' ,
80+ 'sinch-inbound-basic-auth' : 'https://developers.sinch.com/docs/fax/api-reference/fax/tag/Notifications/#incoming-fax-event-webhook' ,
81+ 'sinch-register-webhook-limitations' : 'https://developers.sinch.com/docs/fax/api-reference/fax/tag/Services/#create-a-service' ,
82+ 'sinch-troubleshoot-auth-fail' : 'https://developers.sinch.com/docs/fax/api-reference/fax/tag/Error-Messages/#http-error-codes' ,
83+ 'sinch-troubleshoot-inbound-fail' : 'https://developers.sinch.com/docs/fax/api-reference/fax/tag/Services/#create-a-service' ,
84+ 'enforce-https-phi' : 'https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-C/part-164/subpart-C/section-164.312' ,
85+ 'require-api-key-production' : 'https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#https' ,
86+ 'audit-logging-hipaa' : 'https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-C/part-164/subpart-C/section-164.312' ,
87+ 'phaxio-webhook-hmac' : 'https://www.phaxio.com/docs/security/callbacks' ,
88+ 'phaxio-status-callback-url' : 'https://www.phaxio.com/docs/api/v1/send/sendCallback' ,
89+ } ;
90+
91+ const hrefFor = ( topic : string ) : string | undefined => ( anchors [ topic ] || thirdParty [ topic ] ) ;
92+
93+ // Load anchor mapping from docs site for precise deep links
94+ useEffect ( ( ) => {
95+ const loadAnchors = async ( ) => {
96+ try {
97+ const base = docsBase || 'https://dmontgomery40.github.io/Faxbot' ;
98+ const topics : string [ ] = [ 'security' , 'diagnostics' , 'inbound' , 'storage' , 'plugins' , 'mcp' ] ;
99+ const provs = [ active ?. outbound , active ?. inbound ] . filter ( Boolean ) as string [ ] ;
100+ for ( const p of provs ) { if ( ! topics . includes ( p ) ) topics . push ( p ) ; }
101+ topics . push ( 'all' ) ;
102+ for ( const t of Array . from ( new Set ( topics ) ) ) {
103+ try {
104+ const res = await fetch ( `${ base } /anchors/${ t } .json` , { cache : 'no-store' } ) ;
105+ if ( res . ok ) {
106+ const js = await res . json ( ) ;
107+ setAnchors ( prev => ( { ...prev , ...js } ) ) ;
108+ }
109+ } catch { /* ignore per-scope failure */ }
110+ }
111+ } catch {
112+ /* ignore */
113+ }
114+ } ;
115+ loadAnchors ( ) ;
116+ } , [ docsBase , active ?. outbound , active ?. inbound ] ) ;
74117
75118 const runDiagnostics = async ( ) => {
76119 try {
@@ -203,9 +246,9 @@ function Diagnostics({ client, onNavigate, docsBase }: DiagnosticsProps) {
203246 if ( t . includes ( 'phaxio' ) ) {
204247 docs . push ( { text : 'Phaxio Setup Guide' , href : `${ docsBase || 'https://dmontgomery40.github.io/Faxbot' } /backends/phaxio-setup.html` } ) ;
205248 docs . push ( { text : 'Phaxio Console' , href : 'https://console.phaxio.com' } ) ;
206- if ( key === 'public_url_https' || key === 'callback_url_set' ) {
207- docs . push ( { text : 'Webhook security requires HTTPS for PHI transmission.' } ) ;
208- }
249+ const add = ( topic : string , text : string ) => { const href = anchors [ topic ] || thirdParty [ topic ] ; if ( href ) docs . push ( { text , href } ) ; } ;
250+ add ( 'phaxio-webhook-hmac' , 'Verify Phaxio inbound HMAC signatures' ) ;
251+ add ( 'phaxio-status-callback-url' , 'Set status callback URL (HTTPS required)' ) ;
209252 }
210253
211254 if ( t . includes ( 'sip' ) ) {
@@ -217,16 +260,26 @@ function Diagnostics({ client, onNavigate, docsBase }: DiagnosticsProps) {
217260
218261 if ( t . includes ( 'security' ) ) {
219262 docs . push ( { text : 'Security Guide' , href : `${ docsBase || 'https://dmontgomery40.github.io/Faxbot' } /security/` } ) ;
220- if ( key === 'enforce_https' ) {
221- docs . push ( { text : 'HIPAA requires encryption in transit. Enable ENFORCE_PUBLIC_HTTPS=true.' } ) ;
222- }
263+ const addSec = ( topic : string , text : string ) => { const href = anchors [ topic ] || thirdParty [ topic ] ; if ( href ) docs . push ( { text, href } ) ; } ;
264+ addSec ( 'enforce-https-phi' , 'Enforce HTTPS for PHI (ENFORCE_PUBLIC_HTTPS)' ) ;
265+ addSec ( 'require-api-key-production' , 'Require API keys (REQUIRE_API_KEY)' ) ;
266+ addSec ( 'audit-logging-hipaa' , 'Enable audit logging' ) ;
223267 }
224268
225269 if ( t . includes ( 'sinch' ) ) {
226270 docs . push ( { text : 'Faxbot: Sinch Setup' , href : `${ docsBase || 'https://dmontgomery40.github.io/Faxbot' } /backends/sinch-setup.html` } ) ;
227271 docs . push ( { text : 'Sinch Fax API' , href : 'https://developers.sinch.com/docs/fax/api-reference/' } ) ;
228272 docs . push ( { text : 'OAuth 2.0 for Fax API' , href : 'https://developers.sinch.com/docs/fax/api-reference/authentication/oauth/' } ) ;
229273 docs . push ( { text : 'Sinch Customer Dashboard (Access Keys – Build)' , href : 'https://dashboard.sinch.com/settings/access-keys' } ) ;
274+ const add = ( topic : string , text : string ) => { const href = anchors [ topic ] || thirdParty [ topic ] ; if ( href ) docs . push ( { text, href } ) ; } ;
275+ add ( 'sinch-build-access-keys-location' , 'Where to find Sinch Fax access keys (Build)' ) ;
276+ add ( 'sinch-oauth-client-credentials-flow' , 'How Faxbot mints OAuth2 access tokens' ) ;
277+ add ( 'sinch-regional-base-url' , 'Regional base URL (SINCH_BASE_URL)' ) ;
278+ add ( 'sinch-inbound-webhook-url' , 'Set the inbound webhook URL' ) ;
279+ add ( 'sinch-inbound-basic-auth' , 'Enforce Basic auth for inbound webhooks' ) ;
280+ add ( 'sinch-troubleshoot-auth-fail' , 'Troubleshooting auth failures in Diagnostics' ) ;
281+ add ( 'sinch-troubleshoot-inbound-fail' , 'Troubleshooting inbound failures' ) ;
282+ add ( 'sinch-register-webhook-limitations' , 'Limitations of auto “Register with Sinch”' ) ;
230283 }
231284
232285 return docs ;
0 commit comments