()
| 628 | } |
| 629 | |
| 630 | async function loadPortalSources() { |
| 631 | if (__portalSourcesLoaded) return __portalSources; |
| 632 | __portalSourcesLoaded = true; |
| 633 | |
| 634 | let sources = []; |
| 635 | try { |
| 636 | const res = await fetch('/api/pro/sources/list.php', { |
| 637 | credentials: 'include' |
| 638 | }); |
| 639 | const data = await safeJson(res); |
| 640 | if (data && data.ok && Array.isArray(data.sources)) { |
| 641 | sources = data.sources; |
| 642 | } |
| 643 | } catch (e) { |
| 644 | sources = []; |
| 645 | } |
| 646 | |
| 647 | if (!Array.isArray(sources) || !sources.length) { |
| 648 | sources = [{ id: 'local', name: 'Local', type: 'local', enabled: true }]; |
| 649 | } |
| 650 | |
| 651 | const normalized = []; |
| 652 | sources.forEach(src => { |
| 653 | if (!src || !src.id) return; |
| 654 | const id = String(src.id || '').trim(); |
| 655 | if (!id) return; |
| 656 | normalized.push({ |
| 657 | id, |
| 658 | name: String(src.name || src.id || '').trim() || id, |
| 659 | type: String(src.type || '').trim(), |
| 660 | enabled: src.enabled !== false |
| 661 | }); |
| 662 | }); |
| 663 | |
| 664 | if (!normalized.find(s => s.id === 'local')) { |
| 665 | normalized.unshift({ id: 'local', name: 'Local', type: 'local', enabled: true }); |
| 666 | } |
| 667 | |
| 668 | __portalSources = normalized; |
| 669 | __portalSourcesById = {}; |
| 670 | normalized.forEach(src => { __portalSourcesById[src.id] = src; }); |
| 671 | |
| 672 | return __portalSources; |
| 673 | } |
| 674 | |
| 675 | function renderPortalSourceOptions(selectedId) { |
| 676 | const active = normalizePortalSourceId(selectedId) || getPortalSourceFallbackId(); |
no test coverage detected