Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/slimy-singers-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
23 changes: 14 additions & 9 deletions server/public/admin-settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ <h3>Billing Notifications Channel</h3>
<select id="billingChannel" disabled>
<option value="">Loading channels...</option>
</select>
<small>Only public channels the bot has access to are shown</small>
<small>Only private channels are shown. Invite Addie to your billing channel first.</small>
</div>
<button class="btn btn-primary" id="saveBtn" onclick="saveBillingChannel()" disabled>
Save
Expand Down Expand Up @@ -243,14 +243,19 @@ <h3>Billing Notifications Channel</h3>

// Build options
let html = '<option value="">-- No channel (disabled) --</option>';
slackChannels.forEach(ch => {
const selected = currentSettings?.billing_channel?.channel_id === ch.id ? 'selected' : '';
html += `<option value="${ch.id}" ${selected}>#${escapeHtml(ch.name)}</option>`;
});

select.innerHTML = html;
select.disabled = false;
document.getElementById('saveBtn').disabled = false;
if (slackChannels.length === 0) {
html = '<option value="">No private channels found - invite Addie first</option>';
select.innerHTML = html;
showStatusMessage('Addie is not in any private channels. Invite @Addie to your billing channel and refresh.', 'error');
} else {
slackChannels.forEach(ch => {
const selected = currentSettings?.billing_channel?.channel_id === ch.id ? 'selected' : '';
html += `<option value="${ch.id}" ${selected}>#${escapeHtml(ch.name)}</option>`;
});
select.innerHTML = html;
select.disabled = false;
document.getElementById('saveBtn').disabled = false;
}
} catch (error) {
console.error('Error loading Slack channels:', error);
select.innerHTML = '<option value="">Error loading channels</option>';
Expand Down
21 changes: 18 additions & 3 deletions server/src/routes/admin/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
getBillingChannel,
setBillingChannel,
} from '../../db/system-settings-db.js';
import { getSlackChannels, isSlackConfigured } from '../../slack/client.js';
import { getSlackChannels, getChannelInfo, isSlackConfigured } from '../../slack/client.js';

const logger = createLogger('admin-settings');

Expand Down Expand Up @@ -51,8 +51,11 @@ export function createAdminSettingsRouter(): Router {
return;
}

// Get public channels the bot can see
const channels = await getSlackChannels({ types: 'public_channel', exclude_archived: true });
// Only private channels - billing info should not go to public channels
const channels = await getSlackChannels({
types: 'private_channel',
exclude_archived: true,
});

// Sort by name and return minimal info
const sorted = channels
Expand Down Expand Up @@ -89,6 +92,18 @@ export function createAdminSettingsRouter(): Router {
});
return;
}

// Verify the channel is private (billing info should not go to public channels)
if (isSlackConfigured()) {
const channelInfo = await getChannelInfo(channel_id);
if (channelInfo && !channelInfo.is_private) {
res.status(400).json({
error: 'Invalid channel',
message: 'Only private channels are allowed for billing notifications',
});
return;
}
}
}

// Validate channel name if provided
Expand Down