Skip to content
160 changes: 102 additions & 58 deletions controller/authController.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
const { authAndIdentity } = require('../services');
const authService = require('../services/authService');
const {
createSuccessResponse,
createErrorResponse,
formatProfile,
formatSession
} = require('../services/apiResponseService');
const { isServiceError } = require('../services/serviceError');
const logger = require('../utils/logger');

const { authService, userProfileService, serviceError } = authAndIdentity;
const { isServiceError, ServiceError } = serviceError;

const TRUSTED_DEVICE_COOKIE = authService.trustedDeviceCookieName || 'trusted_device';

function getDeviceInfo(req) {
Expand All @@ -28,59 +32,93 @@ function clearTrustedDeviceCookie(res) {
});
}

function handleServiceError(res, error, fallbackStatus, fallbackLogLabel) {
function handleServiceError(res, error, fallbackStatus, fallbackCode, label, context = {}) {
if (isServiceError(error)) {
return res.status(error.statusCode).json({
success: false,
error: error.message
});
return res.status(error.statusCode).json(
createErrorResponse(error.message, fallbackCode, error.details || undefined)
);
}

logger.error(fallbackLogLabel, { error: error.message });
return res.status(fallbackStatus).json({
success: false,
error: error.message || 'Internal server error'
});
logger.error(label, { error: error.message, ...context });
return res.status(fallbackStatus).json(
createErrorResponse(error.message || 'Internal server error', fallbackCode)
);
}

exports.register = async (req, res) => {
try {
const { name, email, password, first_name, last_name } = req.body;

if (!name || !email || !password) {
return res.status(400).json(
createErrorResponse('Name, email, and password are required', 'VALIDATION_ERROR')
);
}

const result = await authService.register({
name: req.body.name,
email: req.body.email,
password: req.body.password,
first_name: req.body.first_name,
last_name: req.body.last_name
name,
email,
password,
first_name,
last_name
});

return res.status(201).json(result);
return res.status(201).json(createSuccessResponse({
user: {
id: result.user?.user_id || null,
email: result.user?.email || email,
name: result.user?.name || name
}
}, {
message: result.message || 'User registered successfully'
}));
} catch (error) {
logger.error('Registration error', { error: error.message, email: req.body.email });
return handleServiceError(res, error, 400, 'Registration error:');
return handleServiceError(res, error, 400, 'REGISTER_FAILED', 'Registration error', {
email: req.body.email
});
}
};

exports.login = async (req, res) => {
try {
const result = await authService.login({
email: req.body.email,
password: req.body.password
}, getDeviceInfo(req));
const { email, password } = req.body;

return res.json(result);
if (!email || !password) {
return res.status(400).json(
createErrorResponse('Email and password are required', 'VALIDATION_ERROR')
);
}

const result = await authService.login({ email, password }, getDeviceInfo(req));

return res.json(createSuccessResponse({
user: result.user,
session: formatSession(result)
}));
} catch (error) {
logger.error('Login error', { error: error.message, email: req.body.email });
return handleServiceError(res, error, 401, 'Login error:');
return handleServiceError(res, error, 401, 'AUTHENTICATION_FAILED', 'Login error', {
email: req.body.email
});
}
};

exports.refreshToken = async (req, res) => {
try {
const result = await authService.refreshAccessToken(req.body.refreshToken, getDeviceInfo(req));
return res.json(result);
const { refreshToken } = req.body;

if (!refreshToken) {
return res.status(400).json(
createErrorResponse('Refresh token is required', 'VALIDATION_ERROR')
);
}

const result = await authService.refreshAccessToken(refreshToken, getDeviceInfo(req));

return res.json(createSuccessResponse({
session: formatSession(result)
}));
} catch (error) {
logger.error('Token refresh error', { error: error.message });
return handleServiceError(res, error, 401, 'Token refresh error:');
return handleServiceError(res, error, 401, 'REFRESH_FAILED', 'Token refresh error');
}
};

Expand All @@ -104,10 +142,13 @@ exports.googleExchange = async (req, res) => {
exports.logout = async (req, res) => {
try {
const result = await authService.logout(req.body.refreshToken);
return res.json(result);
return res.json(createSuccessResponse(null, {
message: result.message
}));
} catch (error) {
logger.error('Logout error', { error: error.message, userId: req.user?.userId });
return handleServiceError(res, error, 500, 'Logout error:');
return handleServiceError(res, error, 500, 'LOGOUT_FAILED', 'Logout error', {
userId: req.user?.userId
});
}
};

Expand All @@ -119,10 +160,13 @@ exports.logoutAll = async (req, res) => {
});

clearTrustedDeviceCookie(res);
return res.json(result);
return res.json(createSuccessResponse(null, {
message: result.message
}));
} catch (error) {
logger.error('Logout all error', { error: error.message, userId: req.user?.userId });
return handleServiceError(res, error, 500, 'Logout all error:');
return handleServiceError(res, error, 500, 'LOGOUT_ALL_FAILED', 'Logout all error', {
userId: req.user?.userId
});
}
};

Expand All @@ -135,33 +179,33 @@ exports.revokeTrustedDevices = async (req, res) => {
);

clearTrustedDeviceCookie(res);
return res.json({
success: true,
message: 'Trusted devices revoked successfully',
return res.json(createSuccessResponse({
revokedCount: result.revokedCount
});
}, {
message: 'Trusted devices revoked successfully'
}));
} catch (error) {
logger.error('Revoke trusted devices error', { error: error.message, userId: req.user?.userId });
return handleServiceError(res, error, 500, 'Revoke trusted devices error:');
return handleServiceError(
res,
error,
500,
'TRUSTED_DEVICE_REVOKE_FAILED',
'Revoke trusted devices error',
{ userId: req.user?.userId }
);
}
};

exports.getProfile = async (req, res) => {
try {
const result = await userProfileService.getCanonicalProfile({ userId: req.user.userId });
return res.json(result);
const result = await authService.getProfile(req.user.userId);
return res.json(createSuccessResponse({
user: formatProfile(result.user)
}));
} catch (error) {
if (error instanceof ServiceError) {
return res.status(error.statusCode).json({
success: false,
error: error.message
});
}

logger.error('Get profile error', { error: error.message, userId: req.user?.userId });
return res.status(500).json({
success: false,
error: 'Internal server error'
const code = error.statusCode === 404 ? 'USER_NOT_FOUND' : 'PROFILE_LOAD_FAILED';
return handleServiceError(res, error, error.statusCode || 500, code, 'Get profile error', {
userId: req.user?.userId
});
}
};
Expand Down
23 changes: 17 additions & 6 deletions controller/recommendationController.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const { coreApp, shared } = require('../services');

const { recommendationService } = coreApp;
const { createErrorResponse } = shared.apiResponse;
const { generateRecommendations } = require('../services/recommendationService');
const {
createErrorResponse,
createSuccessResponse,
formatRecommendations
} = require('../services/apiResponseService');

function isPlainObject(value) {
return value != null && typeof value === 'object' && !Array.isArray(value);
Expand Down Expand Up @@ -39,7 +41,7 @@ async function getRecommendations(req, res) {
try {
validateRecommendationRequest(req.body || {});

const result = await recommendationService.generateRecommendations({
const result = await generateRecommendations({
userId: req.user?.userId || req.body?.userId,
email: req.user?.email || req.body?.email,
healthGoals: req.body?.healthGoals || {},
Expand All @@ -51,7 +53,16 @@ async function getRecommendations(req, res) {
refreshCache: req.body?.refreshCache === true
});

return res.status(200).json(result);
return res.status(200).json(createSuccessResponse({
items: formatRecommendations(result.recommendations || [])
}, {
count: (result.recommendations || []).length,
generatedAt: result.generatedAt,
contractVersion: result.contractVersion,
source: result.source,
cache: result.cache,
input: result.input
}));
} catch (error) {
console.error('[recommendationController] error:', error);
const statusCode = error.statusCode || 500;
Expand Down
8 changes: 1 addition & 7 deletions dbConnection.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@

require('dotenv').config();

// TEMPORARY DEBUG — remove after fixing
console.log('🔍 Supabase env check:', {
url: process.env.SUPABASE_URL?.slice(0, 30) + '...',
keyLoaded: !!process.env.SUPABASE_SERVICE_ROLE_KEY,
keyPrefix: process.env.SUPABASE_SERVICE_ROLE_KEY?.slice(0, 10)
});
const { createClient } = require('@supabase/supabase-js');

// Check if environment variables are loaded
Expand All @@ -21,4 +15,4 @@ if (!process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_ROLE_KEY) {
process.exit(1);
}

module.exports = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY);
module.exports = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY);
Loading
Loading