739 lines
22 KiB
TypeScript
739 lines
22 KiB
TypeScript
import { fail } from '@sveltejs/kit';
|
|
import type { Actions, PageServerLoad } from './$types';
|
|
import { testSmtpConnection, sendTemplatedEmail } from '$lib/server/email';
|
|
import { testS3Connection, clearS3ClientCache } from '$lib/server/storage';
|
|
import { supabaseAdmin } from '$lib/server/supabase';
|
|
import * as poste from '$lib/server/poste';
|
|
|
|
export const load: PageServerLoad = async ({ locals }) => {
|
|
// Load all configurable data
|
|
const [
|
|
{ data: membershipStatuses },
|
|
{ data: membershipTypes },
|
|
{ data: eventTypes },
|
|
{ data: documentCategories },
|
|
{ data: appSettings },
|
|
{ data: emailTemplates }
|
|
] = await Promise.all([
|
|
locals.supabase.from('membership_statuses').select('*').order('sort_order', { ascending: true }),
|
|
locals.supabase.from('membership_types').select('*').order('sort_order', { ascending: true }),
|
|
locals.supabase.from('event_types').select('*').order('sort_order', { ascending: true }),
|
|
locals.supabase.from('document_categories').select('*').order('sort_order', { ascending: true }),
|
|
locals.supabase.from('app_settings').select('*'),
|
|
locals.supabase.from('email_templates').select('template_key, template_name, category').eq('is_active', true).order('category').order('template_name')
|
|
]);
|
|
|
|
// Convert settings to object by category
|
|
const settings: Record<string, Record<string, any>> = {};
|
|
for (const setting of appSettings || []) {
|
|
if (!settings[setting.category]) {
|
|
settings[setting.category] = {};
|
|
}
|
|
settings[setting.category][setting.setting_key] = setting.setting_value;
|
|
}
|
|
|
|
return {
|
|
membershipStatuses: membershipStatuses || [],
|
|
membershipTypes: membershipTypes || [],
|
|
eventTypes: eventTypes || [],
|
|
documentCategories: documentCategories || [],
|
|
settings,
|
|
emailTemplates: emailTemplates || []
|
|
};
|
|
};
|
|
|
|
export const actions: Actions = {
|
|
// Membership Status actions
|
|
createStatus: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const name = formData.get('name') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const color = formData.get('color') as string;
|
|
const description = formData.get('description') as string;
|
|
|
|
if (!name || !displayName) {
|
|
return fail(400, { error: 'Name and display name are required' });
|
|
}
|
|
|
|
const { error } = await locals.supabase.from('membership_statuses').insert({
|
|
name: name.toLowerCase().replace(/\s+/g, '_'),
|
|
display_name: displayName,
|
|
color: color || '#6b7280',
|
|
description: description || null
|
|
});
|
|
|
|
if (error) {
|
|
console.error('Create status error:', error);
|
|
return fail(500, { error: 'Failed to create status' });
|
|
}
|
|
|
|
return { success: 'Status created successfully!' };
|
|
},
|
|
|
|
deleteStatus: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const id = formData.get('id') as string;
|
|
|
|
const { error } = await locals.supabase.from('membership_statuses').delete().eq('id', id);
|
|
|
|
if (error) {
|
|
console.error('Delete status error:', error);
|
|
return fail(500, { error: 'Failed to delete status' });
|
|
}
|
|
|
|
return { success: 'Status deleted!' };
|
|
},
|
|
|
|
// Membership Type actions
|
|
createType: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const name = formData.get('name') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const annualDues = formData.get('annual_dues') as string;
|
|
const description = formData.get('description') as string;
|
|
|
|
if (!name || !displayName || !annualDues) {
|
|
return fail(400, { error: 'Name, display name, and annual dues are required' });
|
|
}
|
|
|
|
const { error } = await locals.supabase.from('membership_types').insert({
|
|
name: name.toLowerCase().replace(/\s+/g, '_'),
|
|
display_name: displayName,
|
|
annual_dues: parseFloat(annualDues),
|
|
description: description || null
|
|
});
|
|
|
|
if (error) {
|
|
console.error('Create type error:', error);
|
|
return fail(500, { error: 'Failed to create membership type' });
|
|
}
|
|
|
|
return { success: 'Membership type created successfully!' };
|
|
},
|
|
|
|
deleteType: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const id = formData.get('id') as string;
|
|
|
|
const { error } = await locals.supabase.from('membership_types').delete().eq('id', id);
|
|
|
|
if (error) {
|
|
console.error('Delete type error:', error);
|
|
return fail(500, { error: 'Failed to delete membership type' });
|
|
}
|
|
|
|
return { success: 'Membership type deleted!' };
|
|
},
|
|
|
|
// Event Type actions
|
|
createEventType: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const name = formData.get('name') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const color = formData.get('color') as string;
|
|
|
|
if (!name || !displayName) {
|
|
return fail(400, { error: 'Name and display name are required' });
|
|
}
|
|
|
|
const { error } = await locals.supabase.from('event_types').insert({
|
|
name: name.toLowerCase().replace(/\s+/g, '_'),
|
|
display_name: displayName,
|
|
color: color || '#3b82f6'
|
|
});
|
|
|
|
if (error) {
|
|
console.error('Create event type error:', error);
|
|
return fail(500, { error: 'Failed to create event type' });
|
|
}
|
|
|
|
return { success: 'Event type created successfully!' };
|
|
},
|
|
|
|
deleteEventType: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const id = formData.get('id') as string;
|
|
|
|
const { error } = await locals.supabase.from('event_types').delete().eq('id', id);
|
|
|
|
if (error) {
|
|
console.error('Delete event type error:', error);
|
|
return fail(500, { error: 'Failed to delete event type' });
|
|
}
|
|
|
|
return { success: 'Event type deleted!' };
|
|
},
|
|
|
|
// Document Category actions
|
|
createCategory: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const name = formData.get('name') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const description = formData.get('description') as string;
|
|
|
|
if (!name || !displayName) {
|
|
return fail(400, { error: 'Name and display name are required' });
|
|
}
|
|
|
|
const { error } = await locals.supabase.from('document_categories').insert({
|
|
name: name.toLowerCase().replace(/\s+/g, '_'),
|
|
display_name: displayName,
|
|
description: description || null
|
|
});
|
|
|
|
if (error) {
|
|
console.error('Create category error:', error);
|
|
return fail(500, { error: 'Failed to create document category' });
|
|
}
|
|
|
|
return { success: 'Document category created successfully!' };
|
|
},
|
|
|
|
deleteCategory: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const id = formData.get('id') as string;
|
|
|
|
const { error } = await locals.supabase.from('document_categories').delete().eq('id', id);
|
|
|
|
if (error) {
|
|
console.error('Delete category error:', error);
|
|
return fail(500, { error: 'Failed to delete document category' });
|
|
}
|
|
|
|
return { success: 'Document category deleted!' };
|
|
},
|
|
|
|
// Update app settings
|
|
updateSettings: async ({ request, locals }) => {
|
|
const { member } = await locals.safeGetSession();
|
|
|
|
if (!member || member.role !== 'admin') {
|
|
return fail(403, { error: 'Unauthorized' });
|
|
}
|
|
|
|
const formData = await request.formData();
|
|
const category = formData.get('category') as string;
|
|
|
|
if (!category) {
|
|
return fail(400, { error: 'Category is required' });
|
|
}
|
|
|
|
try {
|
|
// Get existing boolean settings for this category to handle unchecked checkboxes
|
|
// Use supabaseAdmin to bypass RLS
|
|
const { data: existingSettings, error: fetchError } = await supabaseAdmin
|
|
.from('app_settings')
|
|
.select('setting_key, setting_type')
|
|
.eq('category', category);
|
|
|
|
if (fetchError) {
|
|
console.error('Error fetching existing settings:', fetchError);
|
|
return fail(500, { error: 'Failed to load existing settings' });
|
|
}
|
|
|
|
const existingBooleanKeys = new Set(
|
|
(existingSettings || [])
|
|
.filter(s => s.setting_type === 'boolean')
|
|
.map(s => s.setting_key)
|
|
);
|
|
|
|
// Get all settings from form data
|
|
const settingsToUpdate: Array<{ key: string; value: any; type: string }> = [];
|
|
const processedKeys = new Set<string>();
|
|
|
|
for (const [key, value] of formData.entries()) {
|
|
if (key !== 'category' && key.startsWith('setting_')) {
|
|
const settingKey = key.replace('setting_', '');
|
|
processedKeys.add(settingKey);
|
|
// Handle checkbox values - they come as 'on' when checked
|
|
const isCheckbox = value === 'on' || existingBooleanKeys.has(settingKey);
|
|
settingsToUpdate.push({
|
|
key: settingKey,
|
|
value: isCheckbox ? (value === 'on' || value === 'true') : value,
|
|
type: isCheckbox ? 'boolean' : 'text'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Handle unchecked checkboxes - they don't send any value
|
|
// For any existing boolean setting NOT in the form data, set to false
|
|
for (const booleanKey of existingBooleanKeys) {
|
|
if (!processedKeys.has(booleanKey)) {
|
|
settingsToUpdate.push({
|
|
key: booleanKey,
|
|
value: false,
|
|
type: 'boolean'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Update or insert each setting using supabaseAdmin (bypasses RLS)
|
|
for (const setting of settingsToUpdate) {
|
|
// Try to update first
|
|
const { data: existing } = await supabaseAdmin
|
|
.from('app_settings')
|
|
.select('id')
|
|
.eq('category', category)
|
|
.eq('setting_key', setting.key)
|
|
.single();
|
|
|
|
if (existing) {
|
|
// Update existing
|
|
const { error: updateError } = await supabaseAdmin
|
|
.from('app_settings')
|
|
.update({
|
|
setting_value: setting.type === 'boolean' ? setting.value : JSON.stringify(setting.value),
|
|
updated_at: new Date().toISOString(),
|
|
updated_by: member.id
|
|
})
|
|
.eq('category', category)
|
|
.eq('setting_key', setting.key);
|
|
|
|
if (updateError) {
|
|
console.error('Error updating setting:', setting.key, updateError);
|
|
}
|
|
} else {
|
|
// Insert new setting
|
|
const { error: insertError } = await supabaseAdmin
|
|
.from('app_settings')
|
|
.insert({
|
|
category,
|
|
setting_key: setting.key,
|
|
setting_value: setting.type === 'boolean' ? setting.value : JSON.stringify(setting.value),
|
|
setting_type: setting.type,
|
|
display_name: setting.key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
|
updated_at: new Date().toISOString(),
|
|
updated_by: member.id
|
|
});
|
|
|
|
if (insertError) {
|
|
console.error('Error inserting setting:', setting.key, insertError);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear caches if storage settings were updated
|
|
if (category === 'storage') {
|
|
clearS3ClientCache();
|
|
}
|
|
|
|
return { success: 'Settings updated successfully!' };
|
|
} catch (err) {
|
|
console.error('Unexpected error updating settings:', err);
|
|
return fail(500, { error: 'An unexpected error occurred while saving settings' });
|
|
}
|
|
},
|
|
|
|
// Test SMTP connection
|
|
testSmtp: async ({ request, locals }) => {
|
|
const { member } = await locals.safeGetSession();
|
|
const formData = await request.formData();
|
|
const testEmail = formData.get('test_email') as string;
|
|
|
|
// Use the member's email if no test email is provided
|
|
const recipientEmail = testEmail || member?.email;
|
|
|
|
if (!recipientEmail) {
|
|
return fail(400, { error: 'No email address provided for test' });
|
|
}
|
|
|
|
// Test SMTP connection and send a test email
|
|
const result = await testSmtpConnection(recipientEmail, member?.id);
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error || 'SMTP test failed' });
|
|
}
|
|
|
|
return {
|
|
success: `Test email sent successfully to ${recipientEmail}! Check your inbox.`
|
|
};
|
|
},
|
|
|
|
// Test S3/MinIO connection
|
|
testS3: async () => {
|
|
// Clear cache to ensure fresh settings are used
|
|
clearS3ClientCache();
|
|
|
|
// Test S3 connection using the actual client
|
|
const result = await testS3Connection();
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error || 'S3 connection test failed' });
|
|
}
|
|
|
|
return {
|
|
success: 'S3/MinIO connection successful! Bucket is accessible.'
|
|
};
|
|
},
|
|
|
|
// Test email template
|
|
testEmailTemplate: async ({ request, locals, url }) => {
|
|
const { member } = await locals.safeGetSession();
|
|
|
|
if (!member?.email) {
|
|
return fail(400, { error: 'No email address found for your account' });
|
|
}
|
|
|
|
const formData = await request.formData();
|
|
const templateKey = formData.get('template_key') as string;
|
|
|
|
if (!templateKey) {
|
|
return fail(400, { error: 'Template key is required' });
|
|
}
|
|
|
|
// Get full member details including member_id
|
|
const { data: fullMember } = await locals.supabase
|
|
.from('members')
|
|
.select('member_id')
|
|
.eq('id', member.id)
|
|
.single();
|
|
|
|
const memberId = fullMember?.member_id || 'MUSA-0001';
|
|
|
|
// Create sample variables for each template type
|
|
const sampleVariables: Record<string, Record<string, string>> = {
|
|
// Welcome/Auth templates
|
|
welcome: {
|
|
first_name: member.first_name || 'Test',
|
|
last_name: member.last_name || 'User',
|
|
member_id: memberId,
|
|
portal_url: url.origin
|
|
},
|
|
password_reset: {
|
|
first_name: member.first_name || 'Test',
|
|
reset_link: `${url.origin}/reset-password?token=sample-token`
|
|
},
|
|
email_verification: {
|
|
first_name: member.first_name || 'Test',
|
|
verification_link: `${url.origin}/verify?token=sample-token`
|
|
},
|
|
// Event templates
|
|
rsvp_confirmation: {
|
|
first_name: member.first_name || 'Test',
|
|
event_title: 'Monaco USA Annual Gala',
|
|
event_date: 'Saturday, March 15, 2026',
|
|
event_time: '7:00 PM',
|
|
event_location: 'Hotel Hermitage, Monaco',
|
|
guest_count: '2',
|
|
portal_url: `${url.origin}/events`
|
|
},
|
|
waitlist_promotion: {
|
|
first_name: member.first_name || 'Test',
|
|
event_title: 'Monaco USA Annual Gala',
|
|
event_date: 'Saturday, March 15, 2026',
|
|
event_location: 'Hotel Hermitage, Monaco',
|
|
portal_url: `${url.origin}/events`
|
|
},
|
|
event_reminder_24hr: {
|
|
first_name: member.first_name || 'Test',
|
|
event_title: 'Monaco USA Monthly Meetup',
|
|
event_date: 'Tomorrow, January 25, 2026',
|
|
event_time: '6:30 PM',
|
|
event_location: 'Stars\'n\'Bars, Monaco',
|
|
guest_count: '1',
|
|
portal_url: `${url.origin}/events/sample-event-id`
|
|
},
|
|
// Payment/Dues templates
|
|
payment_received: {
|
|
first_name: member.first_name || 'Test',
|
|
amount: '€50.00',
|
|
payment_date: 'January 24, 2026',
|
|
payment_method: 'Bank Transfer',
|
|
new_due_date: 'January 24, 2027',
|
|
member_id: memberId
|
|
},
|
|
dues_reminder_30: {
|
|
first_name: member.first_name || 'Test',
|
|
due_date: 'February 24, 2026',
|
|
amount: '€50.00',
|
|
member_id: memberId,
|
|
account_holder: 'Monaco USA Association',
|
|
bank_name: 'CMB Monaco',
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
},
|
|
dues_reminder_7: {
|
|
first_name: member.first_name || 'Test',
|
|
due_date: 'January 31, 2026',
|
|
amount: '€50.00',
|
|
member_id: memberId,
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
},
|
|
dues_reminder_1: {
|
|
first_name: member.first_name || 'Test',
|
|
due_date: 'January 25, 2026',
|
|
amount: '€50.00',
|
|
member_id: memberId,
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
},
|
|
dues_overdue: {
|
|
first_name: member.first_name || 'Test',
|
|
due_date: 'January 15, 2026',
|
|
amount: '€50.00',
|
|
days_overdue: '9',
|
|
grace_days_remaining: '21',
|
|
member_id: memberId,
|
|
account_holder: 'Monaco USA Association',
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
},
|
|
dues_grace_warning: {
|
|
first_name: member.first_name || 'Test',
|
|
due_date: 'December 24, 2025',
|
|
amount: '€50.00',
|
|
days_overdue: '31',
|
|
grace_days_remaining: '7',
|
|
grace_end_date: 'February 1, 2026',
|
|
member_id: memberId,
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
},
|
|
dues_inactive_notice: {
|
|
first_name: member.first_name || 'Test',
|
|
amount: '€50.00',
|
|
member_id: memberId,
|
|
account_holder: 'Monaco USA Association',
|
|
iban: 'MC00 0000 0000 0000 0000 0000 000',
|
|
portal_url: `${url.origin}/payments`
|
|
}
|
|
};
|
|
|
|
// Get variables for this template, or use defaults
|
|
const variables = sampleVariables[templateKey] || {
|
|
first_name: member.first_name || 'Test',
|
|
last_name: member.last_name || 'User',
|
|
portal_url: url.origin
|
|
};
|
|
|
|
// Send test email
|
|
const result = await sendTemplatedEmail(templateKey, member.email, variables, {
|
|
recipientId: member.id,
|
|
recipientName: `${member.first_name} ${member.last_name}`,
|
|
baseUrl: url.origin
|
|
});
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error || 'Failed to send test email' });
|
|
}
|
|
|
|
return {
|
|
success: `Test email "${templateKey}" sent to ${member.email}`
|
|
};
|
|
},
|
|
|
|
// ============================================
|
|
// Poste Mail Server Actions
|
|
// ============================================
|
|
|
|
testPoste: async ({ locals }) => {
|
|
// Get Poste settings
|
|
const { data: settings } = await locals.supabase
|
|
.from('app_settings')
|
|
.select('setting_key, setting_value')
|
|
.eq('category', 'poste');
|
|
|
|
if (!settings || settings.length === 0) {
|
|
return fail(400, { error: 'Poste mail server not configured. Please save settings first.' });
|
|
}
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const s of settings) {
|
|
let value = s.setting_value;
|
|
if (typeof value === 'string') {
|
|
value = value.replace(/^"|"$/g, '');
|
|
}
|
|
config[s.setting_key] = value as string;
|
|
}
|
|
|
|
if (!config.poste_api_host || !config.poste_admin_email || !config.poste_admin_password) {
|
|
return fail(400, { error: 'Poste configuration incomplete. Host, admin email, and password are required.' });
|
|
}
|
|
|
|
const result = await poste.testConnection({
|
|
host: config.poste_api_host,
|
|
adminEmail: config.poste_admin_email,
|
|
adminPassword: config.poste_admin_password
|
|
});
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error || 'Connection test failed' });
|
|
}
|
|
|
|
return { success: 'Connection to Poste mail server successful!' };
|
|
},
|
|
|
|
listMailboxes: async ({ locals }) => {
|
|
// Get Poste settings
|
|
const { data: settings } = await locals.supabase
|
|
.from('app_settings')
|
|
.select('setting_key, setting_value')
|
|
.eq('category', 'poste');
|
|
|
|
if (!settings || settings.length === 0) {
|
|
return fail(400, { error: 'Poste not configured' });
|
|
}
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const s of settings) {
|
|
let value = s.setting_value;
|
|
if (typeof value === 'string') {
|
|
value = value.replace(/^"|"$/g, '');
|
|
}
|
|
config[s.setting_key] = value as string;
|
|
}
|
|
|
|
const result = await poste.listMailboxes({
|
|
host: config.poste_api_host,
|
|
adminEmail: config.poste_admin_email,
|
|
adminPassword: config.poste_admin_password
|
|
});
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error });
|
|
}
|
|
|
|
return { mailboxes: result.mailboxes };
|
|
},
|
|
|
|
createMailbox: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const emailPrefix = formData.get('email_prefix') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const password = formData.get('password') as string;
|
|
|
|
if (!emailPrefix || !displayName) {
|
|
return fail(400, { error: 'Email prefix and display name are required' });
|
|
}
|
|
|
|
// Get Poste settings
|
|
const { data: settings } = await locals.supabase
|
|
.from('app_settings')
|
|
.select('setting_key, setting_value')
|
|
.eq('category', 'poste');
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const s of settings || []) {
|
|
let value = s.setting_value;
|
|
if (typeof value === 'string') {
|
|
value = value.replace(/^"|"$/g, '');
|
|
}
|
|
config[s.setting_key] = value as string;
|
|
}
|
|
|
|
const domain = config.poste_domain || 'monacousa.org';
|
|
const fullEmail = `${emailPrefix}@${domain}`;
|
|
const actualPassword = password || poste.generatePassword();
|
|
|
|
const result = await poste.createMailbox(
|
|
{
|
|
host: config.poste_api_host,
|
|
adminEmail: config.poste_admin_email,
|
|
adminPassword: config.poste_admin_password
|
|
},
|
|
{
|
|
email: fullEmail,
|
|
name: displayName,
|
|
password: actualPassword
|
|
}
|
|
);
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error });
|
|
}
|
|
|
|
return {
|
|
success: `Mailbox ${fullEmail} created successfully!`,
|
|
generatedPassword: password ? undefined : actualPassword
|
|
};
|
|
},
|
|
|
|
updateMailbox: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const email = formData.get('email') as string;
|
|
const displayName = formData.get('display_name') as string;
|
|
const newPassword = formData.get('new_password') as string;
|
|
const disabled = formData.get('disabled') === 'true';
|
|
|
|
if (!email) {
|
|
return fail(400, { error: 'Email is required' });
|
|
}
|
|
|
|
// Get Poste settings
|
|
const { data: settings } = await locals.supabase
|
|
.from('app_settings')
|
|
.select('setting_key, setting_value')
|
|
.eq('category', 'poste');
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const s of settings || []) {
|
|
let value = s.setting_value;
|
|
if (typeof value === 'string') {
|
|
value = value.replace(/^"|"$/g, '');
|
|
}
|
|
config[s.setting_key] = value as string;
|
|
}
|
|
|
|
const updates: { name?: string; password?: string; disabled?: boolean } = {};
|
|
if (displayName) updates.name = displayName;
|
|
if (newPassword) updates.password = newPassword;
|
|
updates.disabled = disabled;
|
|
|
|
const result = await poste.updateMailbox(
|
|
{
|
|
host: config.poste_api_host,
|
|
adminEmail: config.poste_admin_email,
|
|
adminPassword: config.poste_admin_password
|
|
},
|
|
email,
|
|
updates
|
|
);
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error });
|
|
}
|
|
|
|
return { success: `Mailbox ${email} updated successfully!` };
|
|
},
|
|
|
|
deleteMailbox: async ({ request, locals }) => {
|
|
const formData = await request.formData();
|
|
const email = formData.get('email') as string;
|
|
|
|
if (!email) {
|
|
return fail(400, { error: 'Email is required' });
|
|
}
|
|
|
|
// Get Poste settings
|
|
const { data: settings } = await locals.supabase
|
|
.from('app_settings')
|
|
.select('setting_key, setting_value')
|
|
.eq('category', 'poste');
|
|
|
|
const config: Record<string, string> = {};
|
|
for (const s of settings || []) {
|
|
let value = s.setting_value;
|
|
if (typeof value === 'string') {
|
|
value = value.replace(/^"|"$/g, '');
|
|
}
|
|
config[s.setting_key] = value as string;
|
|
}
|
|
|
|
const result = await poste.deleteMailbox(
|
|
{
|
|
host: config.poste_api_host,
|
|
adminEmail: config.poste_admin_email,
|
|
adminPassword: config.poste_admin_password
|
|
},
|
|
email
|
|
);
|
|
|
|
if (!result.success) {
|
|
return fail(400, { error: result.error });
|
|
}
|
|
|
|
return { success: `Mailbox ${email} deleted successfully!` };
|
|
}
|
|
};
|