fix: scan button stuck, CPR dedup crash, role scope filter, profile race conditions; add auto-email toggle and route integration tests
This commit is contained in:
1 parent
7c1afca80b
commit
c350014b16
19 files changed
+1001
-17
No files matched your search
@@ -22,6 +22,13 @@ Never revert to `!!window._googleConnected` / `_fileSources.length > 0` — thos
|
||||
|
||||
`_PHASE_SOURCE_MAP` ordering matters — `Google Workspace` must appear before `Gmail` in the map. The email regex uses `/iu` flags — do not drop the `i`.
|
||||
|
||||
## Profile startup race conditions — profiles.js + users.js
|
||||
|
||||
`loadProfiles()` (fast, local file) resolves before `loadUsers()` (slow, Graph API). The user can select a profile before `S._allUsers` or the sources panel is populated.
|
||||
|
||||
- **`user_ids = "all"` must be deferred** — if `S._allUsers` is empty when `_applyProfile()` runs, set `window._pendingProfileAllUsers = true` instead of calling `.forEach()` on an empty array. `loadUsers()` checks this flag after populating `S._allUsers` and selects everyone. Do not remove this — reverting will silently leave all accounts unchecked whenever a profile is chosen on a fast machine before the user list loads.
|
||||
- **Source checkboxes may not exist yet** — `_applyProfile()` calls `renderSourcesPanel()` first if `#sourcesPanel` contains no `input[data-source-id]` nodes. Same guard used in `loadUsers()`. Without it, `querySelectorAll` returns nothing and the profile's source selection is discarded; the next `renderSourcesPanel()` call re-renders all sources as checked (their default).
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Profile editor accounts** — default to unchecked. Only explicitly saved `user_ids` are checked.
|
||||
|
||||
+12
-2
@@ -69,6 +69,11 @@ function _applyProfile(profile) {
|
||||
// File sources may not be rendered yet (they load async), so store their IDs
|
||||
// in S._pendingProfileSources for renderSourcesPanel() to apply after re-render.
|
||||
const profileSources = profile.sources || [];
|
||||
// Ensure at least M365 source checkboxes are present before reading the DOM.
|
||||
// renderSourcesPanel() is idempotent and fast — safe to call here.
|
||||
if (!document.querySelector('#sourcesPanel input[data-source-id]') && typeof renderSourcesPanel === 'function') {
|
||||
renderSourcesPanel();
|
||||
}
|
||||
document.querySelectorAll('#sourcesPanel input[data-source-id]').forEach(function(cb) {
|
||||
cb.checked = profileSources.includes(cb.dataset.sourceId);
|
||||
});
|
||||
@@ -181,8 +186,13 @@ function _applyProfile(profile) {
|
||||
|
||||
// ── User selection ────────────────────────────────────────────────────────
|
||||
if (profile.user_ids === 'all') {
|
||||
S._allUsers.forEach(u => { u.selected = true; });
|
||||
if (S._allUsers.length) renderAccountList();
|
||||
if (S._allUsers.length) {
|
||||
S._allUsers.forEach(u => { u.selected = true; });
|
||||
renderAccountList();
|
||||
} else {
|
||||
// Users not loaded yet — defer until loadUsers() resolves
|
||||
window._pendingProfileAllUsers = true;
|
||||
}
|
||||
} else if (Array.isArray(profile.user_ids) && profile.user_ids.length) {
|
||||
window._pendingProfileUserIds = profile.user_ids.map(u => u.id || u);
|
||||
_applyPendingProfileUsers();
|
||||
|
||||
@@ -300,6 +300,8 @@ function stLoadSmtp() {
|
||||
if (tls) tls.checked = d.starttls !== false;
|
||||
const pw = document.getElementById('st-smtpPw');
|
||||
if (pw) pw.value = d.has_password ? '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022' : '';
|
||||
const ae = document.getElementById('st-smtpAutoEmail');
|
||||
if (ae) ae.checked = !!d.auto_email_manual;
|
||||
}).catch(function(){});
|
||||
}
|
||||
|
||||
@@ -313,7 +315,8 @@ async function stSmtpSave() {
|
||||
user: document.getElementById('st-smtpUser').value.trim(),
|
||||
from_addr: document.getElementById('st-smtpFrom').value.trim(),
|
||||
recipients: document.getElementById('st-smtpTo').value.split(/[,;]/).map(function(s){return s.trim();}).filter(Boolean),
|
||||
starttls: document.getElementById('st-smtpTls').checked,
|
||||
starttls: document.getElementById('st-smtpTls').checked,
|
||||
auto_email_manual: !!(document.getElementById('st-smtpAutoEmail') || {}).checked,
|
||||
};
|
||||
if (pw !== null) body.password = pw;
|
||||
st.style.color = 'var(--muted)'; st.textContent = t('m365_smtp_saving','Saving...');
|
||||
|
||||
@@ -28,6 +28,11 @@ async function loadUsers() {
|
||||
u.selected = prevSelected.has(u.id) ? prevSelected.get(u.id) : false;
|
||||
});
|
||||
S._allUsers = [...fetched, ...toAdd];
|
||||
// Apply deferred "select all" from a profile chosen before users loaded
|
||||
if (window._pendingProfileAllUsers) {
|
||||
S._allUsers.forEach(u => { u.selected = true; });
|
||||
window._pendingProfileAllUsers = false;
|
||||
}
|
||||
renderAccountList(fetched.length <= 1);
|
||||
// Merge Google users separately so they're not blocked by M365 auth timing
|
||||
_mergeGoogleUsers();
|
||||
|
||||
Reference in new issue
Block a user