Skip to content
Open
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
428 changes: 175 additions & 253 deletions public/about.html

Large diffs are not rendered by default.

470 changes: 0 additions & 470 deletions public/auth.html

This file was deleted.

1,374 changes: 638 additions & 736 deletions public/index.html

Large diffs are not rendered by default.

338 changes: 151 additions & 187 deletions public/login.html

Large diffs are not rendered by default.

886 changes: 0 additions & 886 deletions public/profile.html

This file was deleted.

714 changes: 714 additions & 0 deletions public/style.css

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions src/api/reminders/unsubscribe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

import { createClient } from '@supabase/supabase-js';

export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}

try {
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;

if (!supabaseUrl || !supabaseKey) {
console.error('Server configuration error: Missing Supabase credentials');
return res.status(500).json({ error: 'Server configuration error' });
}

const supabase = createClient(supabaseUrl, supabaseKey);

let body = req.body;
if (typeof body === 'string') {
try {
body = JSON.parse(body);
} catch (e) {
return res.status(400).json({ error: 'Invalid JSON body' });
}
}

const { platforms = [], user_email, email } = body;
// Prefer user_email, fallback to email
const targetEmail = (user_email || email);

if (!targetEmail || typeof targetEmail !== 'string') {
return res.status(400).json({ error: 'Missing user email in request' });
}

const updates = {};
const platformList = Array.isArray(platforms) ? platforms : [platforms];

if (platformList.includes('all')) {
updates.email_pref = false;
updates.phone_pref = false;
updates.discord_pref = false;
} else {
if (platformList.includes('email')) updates.email_pref = false;
if (platformList.includes('sms')) updates.phone_pref = false;
if (platformList.includes('discord')) updates.discord_pref = false;
}

if (Object.keys(updates).length === 0 && platformList.length > 0) {
// Platforms provided but none matched known keys?
// Or empty list?
// If empty list, do nothing?
// If platform list has items but no updates, maybe invalid items.
// But for now let's just proceed.
}

// Perform UPDATE
const { data, error } = await supabase
.from('students_duplicate')
.update(updates)
.eq('email', targetEmail)
.select();

if (error) {
console.error('Supabase update error:', error);
return res.status(500).json({ error: error.message });
}

return res.status(200).json({ success: true, message: 'Unsbscribed successfully', data });

} catch (error) {
console.error('Unsubscribe handler error:', error);
return res.status(500).json({ error: 'Internal server error', details: error.message });
}
}
48 changes: 42 additions & 6 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,53 @@ const server = http.createServer(async (req, res) => {
return;
}


// Handle /api/reminders/unsubscribe
if (urlPath === '/api/reminders/unsubscribe' && req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});

req.on('end', async () => {
try {
const { default: unsubscribeHandler } = await import('./api/reminders/unsubscribe.js');

const parsedBody = body ? JSON.parse(body) : {};

const mockReq = {
method: req.method,
body: parsedBody
};

const mockRes = {
status: (code) => {
res.statusCode = code;
return mockRes;
},
json: (data) => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(data));
}
};

await unsubscribeHandler(mockReq, mockRes);
} catch (error) {
console.error('Error handling unsubscribe:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Internal server error', details: error.message }));
}
});
return;
}

// Serve static files - use urlPath (without query string) for file path
// Since server.js is in src/, static files are in ../public/
let filePath = path.join(__dirname, '../public', urlPath);

// Handle root - redirect to login.html
// Handle root - default to index.html (Landing Page)
if (urlPath === '/' || urlPath === '/index.html') {
// If root is requested, we serve login.html by default or let the client handling redirect
// The previous logic was filePath = './login.html', but now we are mapping urlPath to filesystem
// If urlPath is /, filePath becomes .../public/
// We should probably redirect or serve login.html content
if (urlPath === '/') filePath = path.join(__dirname, '../public/login.html');
if (urlPath === '/') filePath = path.join(__dirname, '../public/index.html');
}

const extname = String(path.extname(filePath)).toLowerCase();
Expand Down