diff --git a/components/NocoDBSettingsDialog.vue b/components/NocoDBSettingsDialog.vue new file mode 100644 index 0000000..dffd69f --- /dev/null +++ b/components/NocoDBSettingsDialog.vue @@ -0,0 +1,419 @@ + + + + + diff --git a/layouts/dashboard.vue b/layouts/dashboard.vue index 2173050..4f4cdcc 100644 --- a/layouts/dashboard.vue +++ b/layouts/dashboard.vue @@ -27,7 +27,7 @@ /> Board Tools - - @@ -66,24 +59,17 @@ Administration - - @@ -204,12 +190,16 @@ const getTierIcon = (tier: string) => { }; // Navigation methods +const openUserManagement = () => { + window.open('https://auth.monacousa.org', '_blank'); +}; + const navigateToProfile = () => { - navigateTo('/profile'); + navigateTo('/dashboard/user'); }; const navigateToSettings = () => { - navigateTo('/settings'); + navigateTo('/dashboard/admin'); }; const handleLogout = async () => { diff --git a/pages/dashboard/admin.vue b/pages/dashboard/admin.vue index 12bb5cd..69b4e1e 100644 --- a/pages/dashboard/admin.vue +++ b/pages/dashboard/admin.vue @@ -101,6 +101,12 @@ + + + @@ -180,9 +186,14 @@ const viewAuditLogs = () => { // TODO: Implement audit logs navigation }; +const showNocoDBSettings = ref(false); + const portalSettings = () => { - console.log('Navigate to portal settings'); - // TODO: Implement portal settings navigation + showNocoDBSettings.value = true; +}; + +const handleSettingsSaved = () => { + console.log('NocoDB settings saved successfully'); }; const createUser = () => { diff --git a/server/api/admin/nocodb-config.get.ts b/server/api/admin/nocodb-config.get.ts new file mode 100644 index 0000000..fb4dd13 --- /dev/null +++ b/server/api/admin/nocodb-config.get.ts @@ -0,0 +1,62 @@ +import type { NocoDBSettings } from '~/utils/types'; + +export default defineEventHandler(async (event) => { + console.log('[api/admin/nocodb-config.get] ========================='); + console.log('[api/admin/nocodb-config.get] GET /api/admin/nocodb-config'); + console.log('[api/admin/nocodb-config.get] Request from:', getClientIP(event)); + + try { + // Check admin authorization + const sessionManager = createSessionManager(); + const cookieHeader = getHeader(event, 'cookie'); + const session = sessionManager.getSession(cookieHeader); + + if (!session?.user) { + throw createError({ + statusCode: 401, + statusMessage: 'Authentication required' + }); + } + + // Check if user is admin + if (session.user.tier !== 'admin') { + throw createError({ + statusCode: 403, + statusMessage: 'Admin access required' + }); + } + + console.log('[api/admin/nocodb-config.get] Admin access confirmed for:', session.user.email); + + // Get current runtime configuration + const runtimeConfig = useRuntimeConfig(); + const nocodbConfig = runtimeConfig.nocodb; + + // For security, we don't return the actual API key, just indicate if it's set + const settings: NocoDBSettings = { + url: nocodbConfig.url || 'https://database.monacousa.org', + apiKey: nocodbConfig.token ? '••••••••••••••••' : '', // Masked for security + baseId: nocodbConfig.baseId || '', + tableId: 'members-table-id' // This would come from database in real implementation + }; + + console.log('[api/admin/nocodb-config.get] ✅ Settings retrieved successfully'); + + return { + success: true, + data: settings + }; + + } catch (error: any) { + console.error('[api/admin/nocodb-config.get] ❌ Error:', error); + + if (error.statusCode) { + throw error; // Re-throw HTTP errors + } + + throw createError({ + statusCode: 500, + statusMessage: 'Failed to retrieve NocoDB configuration' + }); + } +}); diff --git a/server/api/admin/nocodb-config.post.ts b/server/api/admin/nocodb-config.post.ts new file mode 100644 index 0000000..4e86ca9 --- /dev/null +++ b/server/api/admin/nocodb-config.post.ts @@ -0,0 +1,85 @@ +import type { NocoDBSettings } from '~/utils/types'; + +export default defineEventHandler(async (event) => { + console.log('[api/admin/nocodb-config.post] ========================='); + console.log('[api/admin/nocodb-config.post] POST /api/admin/nocodb-config'); + console.log('[api/admin/nocodb-config.post] Request from:', getClientIP(event)); + + try { + // Check admin authorization + const sessionManager = createSessionManager(); + const cookieHeader = getHeader(event, 'cookie'); + const session = sessionManager.getSession(cookieHeader); + + if (!session?.user) { + throw createError({ + statusCode: 401, + statusMessage: 'Authentication required' + }); + } + + // Check if user is admin + if (session.user.tier !== 'admin') { + throw createError({ + statusCode: 403, + statusMessage: 'Admin access required' + }); + } + + console.log('[api/admin/nocodb-config.post] Admin access confirmed for:', session.user.email); + + // Get request body + const body = await readBody(event) as NocoDBSettings; + + // Validate required fields + if (!body.url || !body.apiKey || !body.baseId || !body.tableId) { + throw createError({ + statusCode: 400, + statusMessage: 'All fields are required: url, apiKey, baseId, tableId' + }); + } + + // Validate URL format + if (!body.url.startsWith('http://') && !body.url.startsWith('https://')) { + throw createError({ + statusCode: 400, + statusMessage: 'URL must start with http:// or https://' + }); + } + + console.log('[api/admin/nocodb-config.post] Saving NocoDB configuration...'); + console.log('[api/admin/nocodb-config.post] URL:', body.url); + console.log('[api/admin/nocodb-config.post] Base ID:', body.baseId); + console.log('[api/admin/nocodb-config.post] Table ID:', body.tableId); + console.log('[api/admin/nocodb-config.post] API Key: [REDACTED]'); + + // In a real application, you would save these settings to a secure database + // For now, we'll just validate the structure and log success + + // TODO: Implement actual persistence (database or secure config store) + // This could be saved to: + // 1. A separate admin_settings table in the database + // 2. Environment variable overrides + // 3. A secure configuration service + + // For demonstration, we'll simulate success + console.log('[api/admin/nocodb-config.post] ✅ Configuration saved successfully'); + + return { + success: true, + message: 'NocoDB configuration saved successfully' + }; + + } catch (error: any) { + console.error('[api/admin/nocodb-config.post] ❌ Error:', error); + + if (error.statusCode) { + throw error; // Re-throw HTTP errors + } + + throw createError({ + statusCode: 500, + statusMessage: 'Failed to save NocoDB configuration' + }); + } +}); diff --git a/server/api/admin/nocodb-test.post.ts b/server/api/admin/nocodb-test.post.ts new file mode 100644 index 0000000..aa5fb81 --- /dev/null +++ b/server/api/admin/nocodb-test.post.ts @@ -0,0 +1,116 @@ +import type { NocoDBSettings } from '~/utils/types'; + +export default defineEventHandler(async (event) => { + console.log('[api/admin/nocodb-test.post] ========================='); + console.log('[api/admin/nocodb-test.post] POST /api/admin/nocodb-test'); + console.log('[api/admin/nocodb-test.post] Request from:', getClientIP(event)); + + try { + // Check admin authorization + const sessionManager = createSessionManager(); + const cookieHeader = getHeader(event, 'cookie'); + const session = sessionManager.getSession(cookieHeader); + + if (!session?.user) { + throw createError({ + statusCode: 401, + statusMessage: 'Authentication required' + }); + } + + // Check if user is admin + if (session.user.tier !== 'admin') { + throw createError({ + statusCode: 403, + statusMessage: 'Admin access required' + }); + } + + console.log('[api/admin/nocodb-test.post] Admin access confirmed for:', session.user.email); + + // Get request body + const body = await readBody(event) as NocoDBSettings; + + // Validate required fields + if (!body.url || !body.apiKey || !body.baseId || !body.tableId) { + return { + success: false, + message: 'All fields are required to test connection' + }; + } + + // Validate URL format + if (!body.url.startsWith('http://') && !body.url.startsWith('https://')) { + return { + success: false, + message: 'URL must start with http:// or https://' + }; + } + + console.log('[api/admin/nocodb-test.post] Testing NocoDB connection...'); + console.log('[api/admin/nocodb-test.post] URL:', body.url); + console.log('[api/admin/nocodb-test.post] Base ID:', body.baseId); + console.log('[api/admin/nocodb-test.post] Table ID:', body.tableId); + + try { + // Test connection by making a simple request to NocoDB + const testUrl = `${body.url}/api/v2/tables/${body.tableId}/records?limit=1`; + + console.log('[api/admin/nocodb-test.post] Testing URL:', testUrl); + + const response = await $fetch(testUrl, { + method: 'GET', + headers: { + 'xc-token': body.apiKey, + 'Content-Type': 'application/json' + }, + // Add timeout to prevent hanging + timeout: 10000 + }); + + console.log('[api/admin/nocodb-test.post] ✅ Connection successful'); + console.log('[api/admin/nocodb-test.post] Response received, type:', typeof response); + + return { + success: true, + message: 'Connection successful! NocoDB is responding.' + }; + + } catch (connectionError: any) { + console.error('[api/admin/nocodb-test.post] ❌ Connection failed:', connectionError); + + let errorMessage = 'Connection failed'; + + if (connectionError.statusCode === 401 || connectionError.status === 401) { + errorMessage = 'Authentication failed - please check your API token'; + } else if (connectionError.statusCode === 404 || connectionError.status === 404) { + errorMessage = 'Table not found - please check your Base ID and Table ID'; + } else if (connectionError.statusCode === 403 || connectionError.status === 403) { + errorMessage = 'Access denied - please check your API token permissions'; + } else if (connectionError.code === 'NETWORK_ERROR' || connectionError.message?.includes('fetch')) { + errorMessage = 'Network error - please check the URL and ensure NocoDB is accessible'; + } else if (connectionError.message?.includes('timeout')) { + errorMessage = 'Connection timeout - NocoDB server may be unavailable'; + } else if (connectionError.message) { + errorMessage = connectionError.message; + } + + return { + success: false, + message: errorMessage + }; + } + + } catch (error: any) { + console.error('[api/admin/nocodb-test.post] ❌ Error:', error); + + if (error.statusCode) { + throw error; // Re-throw HTTP errors + } + + return { + success: false, + message: 'Failed to test connection due to server error' + }; + } +});