Implement admin functionality for merging duplicate records with error handling and logging

This commit is contained in:
Matt 2025-07-09 12:21:41 -04:00
parent b3e7d04b86
commit 4a60782f89
3 changed files with 285 additions and 112 deletions

View File

@ -27,19 +27,28 @@ export const useAuthorization = () => {
groups: []
});
// Create a loading state
const isLoading = ref(true);
// Function to sync auth state from nuxtApp payload
const syncAuthState = () => {
const payloadAuthState = nuxtApp.payload.data?.authState as AuthState;
if (payloadAuthState) {
authState.value = payloadAuthState;
console.log('[useAuthorization] Auth state synced:', {
authenticated: payloadAuthState.authenticated,
groups: payloadAuthState.groups,
user: payloadAuthState.user?.email
});
} else {
console.log('[useAuthorization] No auth state found in payload');
try {
// Safely check if payload data exists
if (nuxtApp.payload && nuxtApp.payload.data && nuxtApp.payload.data.authState) {
const payloadAuthState = nuxtApp.payload.data.authState as AuthState;
authState.value = payloadAuthState;
isLoading.value = false;
console.log('[useAuthorization] Auth state synced from payload:', {
authenticated: payloadAuthState.authenticated,
groups: payloadAuthState.groups,
user: payloadAuthState.user?.email
});
return true;
}
} catch (error) {
console.error('[useAuthorization] Error syncing auth state:', error);
}
return false;
};
// Try to get auth state from API if not in payload
@ -47,6 +56,7 @@ export const useAuthorization = () => {
try {
const sessionData = await $fetch('/api/auth/session') as AuthState;
authState.value = sessionData;
isLoading.value = false;
console.log('[useAuthorization] Auth state loaded from API:', {
authenticated: sessionData.authenticated,
groups: sessionData.groups,
@ -57,31 +67,46 @@ export const useAuthorization = () => {
updateAuthState(sessionData);
} catch (error) {
console.error('[useAuthorization] Failed to load auth state:', error);
isLoading.value = false;
}
};
// Initialize auth state
onMounted(() => {
syncAuthState();
// Initialize auth state immediately (not just onMounted)
if (process.client) {
// Try to sync from payload first
const synced = syncAuthState();
// If no auth state in payload, try to load from API
if (!authState.value.authenticated) {
// If not synced from payload, load from API
if (!synced) {
loadAuthState();
}
});
} else {
// On server, try to get from payload
syncAuthState();
}
/**
* Get current user groups from session
*/
const getUserGroups = (): string[] => {
return authState.value.groups || [];
try {
return authState.value?.groups || [];
} catch (error) {
console.error('[useAuthorization] Error getting user groups:', error);
return [];
}
};
/**
* Get current authenticated user
*/
const getCurrentUser = (): UserWithGroups | null => {
return authState.value.user || null;
try {
return authState.value?.user || null;
} catch (error) {
console.error('[useAuthorization] Error getting current user:', error);
return null;
}
};
/**

View File

@ -72,117 +72,181 @@ definePageMeta({
const { mdAndDown } = useDisplay();
const { user, logout, authSource } = useUnifiedAuth();
const { isAdmin, getUserGroups, getCurrentUser } = useAuthorization();
const authUtils = useAuthorization();
const tags = usePortalTags();
const drawer = ref(false);
// Safe wrapper for auth functions
const safeIsAdmin = () => {
try {
return authUtils.isAdmin();
} catch (error) {
console.error('[Dashboard] Error checking admin status:', error);
return false;
}
};
const safeGetUserGroups = () => {
try {
return authUtils.getUserGroups();
} catch (error) {
console.error('[Dashboard] Error getting user groups:', error);
return [];
}
};
const safeGetCurrentUser = () => {
try {
return authUtils.getCurrentUser();
} catch (error) {
console.error('[Dashboard] Error getting current user:', error);
return null;
}
};
// Debug auth state
onMounted(() => {
console.log('[Dashboard] Auth state on mount:', {
isAdmin: isAdmin(),
userGroups: getUserGroups(),
currentUser: getCurrentUser()
nextTick(() => {
console.log('[Dashboard] Auth state on mount:', {
isAdmin: safeIsAdmin(),
userGroups: safeGetUserGroups(),
currentUser: safeGetCurrentUser()
});
});
});
const interestMenu = computed(() => {
const userIsAdmin = isAdmin();
const userGroups = getUserGroups();
try {
const userIsAdmin = safeIsAdmin();
const userGroups = safeGetUserGroups();
console.log('[Dashboard] Computing interest menu - isAdmin:', userIsAdmin, 'groups:', userGroups);
console.log('[Dashboard] Computing interest menu - isAdmin:', userIsAdmin, 'groups:', userGroups);
const baseMenu = [
//{
// to: "/dashboard/interest-eoi-queue",
// icon: "mdi-tray-full",
// title: "EOI Queue",
//},
{
to: "/dashboard/interest-analytics",
icon: "mdi-view-dashboard",
title: "Analytics",
},
{
to: "/dashboard/interest-berth-list",
icon: "mdi-table",
title: "Berth List",
},
{
to: "/dashboard/interest-berth-status",
icon: "mdi-sail-boat",
title: "Berth Status",
},
{
to: "/dashboard/interest-list",
icon: "mdi-view-list",
title: "Interest List",
},
{
to: "/dashboard/interest-status",
icon: "mdi-account-check",
title: "Interest Status",
},
{
to: "/dashboard/expenses",
icon: "mdi-receipt",
title: "Expenses",
},
{
to: "/dashboard/file-browser",
icon: "mdi-folder",
title: "File Browser",
},
];
const baseMenu = [
//{
// to: "/dashboard/interest-eoi-queue",
// icon: "mdi-tray-full",
// title: "EOI Queue",
//},
{
to: "/dashboard/interest-analytics",
icon: "mdi-view-dashboard",
title: "Analytics",
},
{
to: "/dashboard/interest-berth-list",
icon: "mdi-table",
title: "Berth List",
},
{
to: "/dashboard/interest-berth-status",
icon: "mdi-sail-boat",
title: "Berth Status",
},
{
to: "/dashboard/interest-list",
icon: "mdi-view-list",
title: "Interest List",
},
{
to: "/dashboard/interest-status",
icon: "mdi-account-check",
title: "Interest Status",
},
{
to: "/dashboard/expenses",
icon: "mdi-receipt",
title: "Expenses",
},
{
to: "/dashboard/file-browser",
icon: "mdi-folder",
title: "File Browser",
},
];
// Add admin menu items if user is admin
if (userIsAdmin) {
console.log('[Dashboard] Adding admin console to interest menu');
baseMenu.push({
to: "/dashboard/admin",
icon: "mdi-shield-crown",
title: "Admin Console",
});
// Add admin menu items if user is admin
if (userIsAdmin) {
console.log('[Dashboard] Adding admin console to interest menu');
baseMenu.push({
to: "/dashboard/admin",
icon: "mdi-shield-crown",
title: "Admin Console",
});
}
return baseMenu;
} catch (error) {
console.error('[Dashboard] Error computing interest menu:', error);
// Return basic menu without admin items on error
return [
{
to: "/dashboard/interest-analytics",
icon: "mdi-view-dashboard",
title: "Analytics",
},
{
to: "/dashboard/interest-list",
icon: "mdi-view-list",
title: "Interest List",
},
];
}
return baseMenu;
});
const defaultMenu = computed(() => {
const userIsAdmin = isAdmin();
const userGroups = getUserGroups();
try {
const userIsAdmin = safeIsAdmin();
const userGroups = safeGetUserGroups();
console.log('[Dashboard] Computing default menu - isAdmin:', userIsAdmin, 'groups:', userGroups);
console.log('[Dashboard] Computing default menu - isAdmin:', userIsAdmin, 'groups:', userGroups);
const baseMenu = [
{
to: "/dashboard/site",
icon: "mdi-view-dashboard",
title: "Site Analytics",
},
{
to: "/dashboard/data",
icon: "mdi-finance",
title: "Data Analytics",
},
{
to: "/dashboard/file-browser",
icon: "mdi-folder",
title: "File Browser",
},
];
const baseMenu = [
{
to: "/dashboard/site",
icon: "mdi-view-dashboard",
title: "Site Analytics",
},
{
to: "/dashboard/data",
icon: "mdi-finance",
title: "Data Analytics",
},
{
to: "/dashboard/file-browser",
icon: "mdi-folder",
title: "File Browser",
},
];
// Add admin menu items if user is admin
if (userIsAdmin) {
console.log('[Dashboard] Adding admin console to default menu');
baseMenu.push({
to: "/dashboard/admin",
icon: "mdi-shield-crown",
title: "Admin Console",
});
// Add admin menu items if user is admin
if (userIsAdmin) {
console.log('[Dashboard] Adding admin console to default menu');
baseMenu.push({
to: "/dashboard/admin",
icon: "mdi-shield-crown",
title: "Admin Console",
});
}
return baseMenu;
} catch (error) {
console.error('[Dashboard] Error computing default menu:', error);
// Return basic menu without admin items on error
return [
{
to: "/dashboard/site",
icon: "mdi-view-dashboard",
title: "Site Analytics",
},
{
to: "/dashboard/data",
icon: "mdi-finance",
title: "Data Analytics",
},
];
}
return baseMenu;
});
const menu = computed(() =>

View File

@ -0,0 +1,84 @@
import { requireAdmin } from '~/server/utils/auth';
import { getNocoDbConfiguration, updateInterest, deleteInterest } from '~/server/utils/nocodb';
export default defineEventHandler(async (event) => {
console.log('[ADMIN] Merge duplicates request');
try {
// Require admin authentication
await requireAdmin(event);
const body = await readBody(event);
const { masterId, duplicateIds, mergeData } = body;
if (!masterId || !duplicateIds || !Array.isArray(duplicateIds)) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid merge request: masterId and duplicateIds array required'
});
}
console.log('[ADMIN] Merging duplicates:', {
masterId,
duplicateIds,
fieldsToMerge: Object.keys(mergeData || {})
});
// Update the master record with merged data
if (mergeData && Object.keys(mergeData).length > 0) {
console.log('[ADMIN] Updating master record with merged data');
await updateInterest(masterId.toString(), mergeData);
}
// Delete the duplicate records
const deletionResults = [];
for (const duplicateId of duplicateIds) {
try {
console.log('[ADMIN] Deleting duplicate record:', duplicateId);
await deleteInterest(duplicateId.toString());
deletionResults.push({ id: duplicateId, success: true });
} catch (error: any) {
console.error('[ADMIN] Failed to delete duplicate:', duplicateId, error);
deletionResults.push({
id: duplicateId,
success: false,
error: error.message
});
}
}
console.log('[ADMIN] Merge operation completed');
return {
success: true,
data: {
masterId,
mergedCount: duplicateIds.length,
deletionResults,
updatedFields: Object.keys(mergeData || {})
}
};
} catch (error: any) {
console.error('[ADMIN] Failed to merge duplicates:', error);
if (error.statusCode === 403) {
return {
success: false,
error: 'Insufficient permissions. Admin access required.'
};
}
if (error.statusCode === 400) {
return {
success: false,
error: error.statusMessage
};
}
return {
success: false,
error: 'Failed to merge duplicate records'
};
}
});