diff --git a/components/NocoDBSettingsDialog.vue b/components/NocoDBSettingsDialog.vue index dffd69f..d33d5a5 100644 --- a/components/NocoDBSettingsDialog.vue +++ b/components/NocoDBSettingsDialog.vue @@ -68,7 +68,7 @@ /> - + + + +

Table Configuration

+
- + +
+ Configure the table ID for the Members functionality +
@@ -210,7 +217,7 @@ const form = ref({ url: 'https://database.monacousa.org', apiKey: '', baseId: '', - tableId: '' + tables: { members: '' } }); // Error handling @@ -261,6 +268,10 @@ const loadSettings = async () => { if (response.success && response.data) { form.value = { ...response.data }; + // Ensure tables object exists + if (!form.value.tables) { + form.value.tables = { members: '' }; + } } } catch (error: any) { console.error('Failed to load NocoDB settings:', error); @@ -352,7 +363,7 @@ const resetForm = () => { url: 'https://database.monacousa.org', apiKey: '', baseId: '', - tableId: '' + tables: { members: '' } }; clearFieldErrors(); connectionStatus.value = null; diff --git a/server/api/admin/nocodb-config.post.ts b/server/api/admin/nocodb-config.post.ts index 402b808..40d26ac 100644 --- a/server/api/admin/nocodb-config.post.ts +++ b/server/api/admin/nocodb-config.post.ts @@ -32,10 +32,10 @@ export default defineEventHandler(async (event) => { const body = await readBody(event) as NocoDBSettings; // Validate required fields - if (!body.url || !body.apiKey || !body.baseId || !body.tableId) { + if (!body.url || !body.apiKey || !body.baseId || !body.tables) { throw createError({ statusCode: 400, - statusMessage: 'All fields are required: url, apiKey, baseId, tableId' + statusMessage: 'All fields are required: url, apiKey, baseId, tables' }); } @@ -50,7 +50,7 @@ export default defineEventHandler(async (event) => { 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] Tables:', Object.keys(body.tables)); console.log('[api/admin/nocodb-config.post] API Key: [REDACTED]'); // Save configuration using the new admin config system diff --git a/server/api/admin/nocodb-test.post.ts b/server/api/admin/nocodb-test.post.ts index d192b45..4802c19 100644 --- a/server/api/admin/nocodb-test.post.ts +++ b/server/api/admin/nocodb-test.post.ts @@ -32,10 +32,10 @@ export default defineEventHandler(async (event) => { const body = await readBody(event) as NocoDBSettings; // Validate required fields - if (!body.url || !body.apiKey || !body.baseId || !body.tableId) { + if (!body.url || !body.apiKey || !body.baseId || !body.tables || Object.keys(body.tables).length === 0) { return { success: false, - message: 'All fields are required to test connection' + message: 'All fields are required to test connection (url, apiKey, baseId, and at least one table)' }; } @@ -50,11 +50,13 @@ export default defineEventHandler(async (event) => { 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); + console.log('[api/admin/nocodb-test.post] Tables:', Object.keys(body.tables)); try { - // Test connection by making a simple request to NocoDB - const testUrl = `${body.url}/api/v2/tables/${body.tableId}/records?limit=1`; + // Test connection by making a simple request to NocoDB using the first table + const firstTableName = Object.keys(body.tables)[0]; + const firstTableId = body.tables[firstTableName]; + const testUrl = `${body.url}/api/v2/tables/${firstTableId}/records?limit=1`; console.log('[api/admin/nocodb-test.post] Testing URL:', testUrl); diff --git a/server/utils/admin-config.ts b/server/utils/admin-config.ts index 1366b6d..14363e9 100644 --- a/server/utils/admin-config.ts +++ b/server/utils/admin-config.ts @@ -1,4 +1,5 @@ import { readFile, writeFile, mkdir, access, constants } from 'fs/promises'; +import { existsSync } from 'fs'; import { join } from 'path'; import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from 'crypto'; import type { NocoDBSettings } from '~/utils/types'; @@ -13,10 +14,20 @@ interface EffectiveNocoDB { url: string; token: string; baseId: string; - tableId: string; + tables: { [tableName: string]: string }; } -const CONFIG_DIR = '/app/data'; +// Support both Docker (/app/data) and local development (./data) paths +const getConfigDir = () => { + // Check if running in Docker container + if (process.env.NODE_ENV === 'production' || existsSync('/app/data')) { + return '/app/data'; + } + // Local development + return './data'; +}; + +const CONFIG_DIR = getConfigDir(); const CONFIG_FILE = join(CONFIG_DIR, 'admin-config.json'); const BACKUP_FILE = join(CONFIG_DIR, 'admin-config.backup.json'); const LOG_FILE = join(CONFIG_DIR, 'admin-config.log'); @@ -169,6 +180,15 @@ export async function loadAdminConfig(): Promise { console.log('[admin-config] Configuration loaded from file'); configCache = config; + + // Update global nocodb configuration + try { + const { setGlobalNocoDBConfig } = await import('./nocodb'); + setGlobalNocoDBConfig(getEffectiveNocoDBConfig()); + } catch (error) { + console.error('[admin-config] Failed to update global configuration:', error); + } + return config; } catch (error) { console.log('[admin-config] No configuration file found, using defaults'); @@ -207,6 +227,15 @@ export async function saveAdminConfig(config: NocoDBSettings, updatedBy: string) console.log('[admin-config] Configuration saved to file'); await logConfigChange('CONFIG_SAVED', updatedBy, { url: config.url, baseId: config.baseId }); + // Update global nocodb configuration immediately + try { + const { setGlobalNocoDBConfig } = await import('./nocodb'); + setGlobalNocoDBConfig(getEffectiveNocoDBConfig()); + console.log('[admin-config] Global configuration updated immediately'); + } catch (error) { + console.error('[admin-config] Failed to update global configuration after save:', error); + } + } catch (error) { console.error('[admin-config] Failed to save configuration:', error); await logConfigChange('CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(error) }); @@ -225,7 +254,7 @@ export function getEffectiveNocoDBConfig(): EffectiveNocoDB { url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org', token: runtimeConfig.nocodb?.token || '', baseId: runtimeConfig.nocodb?.baseId || '', - tableId: 'members-table-id' // This would be configured + tables: { members: 'members-table-id' } // Default table mapping }; // Override with file configuration if available @@ -234,7 +263,7 @@ export function getEffectiveNocoDBConfig(): EffectiveNocoDB { url: configCache.nocodb.url || envConfig.url, token: configCache.nocodb.apiKey || envConfig.token, baseId: configCache.nocodb.baseId || envConfig.baseId, - tableId: configCache.nocodb.tableId || envConfig.tableId + tables: configCache.nocodb.tables || envConfig.tables }; } @@ -253,7 +282,7 @@ export async function getCurrentConfig(): Promise { url: config.nocodb.url || runtimeConfig.nocodb?.url || 'https://database.monacousa.org', apiKey: config.nocodb.apiKey ? '••••••••••••••••' : '', baseId: config.nocodb.baseId || runtimeConfig.nocodb?.baseId || '', - tableId: config.nocodb.tableId || 'members-table-id' + tables: config.nocodb.tables || { members: 'members-table-id' } }; } @@ -262,7 +291,7 @@ export async function getCurrentConfig(): Promise { url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org', apiKey: runtimeConfig.nocodb?.token ? '••••••••••••••••' : '', baseId: runtimeConfig.nocodb?.baseId || '', - tableId: 'members-table-id' + tables: { members: 'members-table-id' } }; } diff --git a/server/utils/nocodb.ts b/server/utils/nocodb.ts index fc3036c..41e480c 100644 --- a/server/utils/nocodb.ts +++ b/server/utils/nocodb.ts @@ -28,17 +28,13 @@ export interface EntityResponse { // Dynamic table ID getter - will use configured table ID from admin panel export const getTableId = (tableName: 'Members'): string => { - try { - // Try to get table ID from persistent configuration - const { getEffectiveNocoDBConfig } = require('./admin-config'); - const effectiveConfig = getEffectiveNocoDBConfig(); - - if (tableName === 'Members' && effectiveConfig.tableId) { - console.log(`[nocodb] Using configured table ID for ${tableName}:`, effectiveConfig.tableId); - return effectiveConfig.tableId; + // Try to get table ID from global configuration first + if (globalNocoDBConfig?.tables && tableName === 'Members') { + const tableId = globalNocoDBConfig.tables['members'] || globalNocoDBConfig.tables['Members']; + if (tableId) { + console.log(`[nocodb] Using global table ID for ${tableName}:`, tableId); + return tableId; } - } catch (error) { - console.log(`[nocodb] Admin config not available for table ID, using fallback`); } // Fallback to default @@ -102,31 +98,35 @@ export const formatNationalitiesAsString = (nationalities: string[]): string => return nationalities.filter(n => n && n.trim()).join(','); }; +// Global variable to store effective configuration +let globalNocoDBConfig: any = null; + +// Function to set the global configuration (called by the admin-config system) +export const setGlobalNocoDBConfig = (config: any) => { + globalNocoDBConfig = config; + console.log('[nocodb] Global configuration updated:', config.url); +}; + export const getNocoDbConfiguration = () => { - try { - // Try to use the persistent configuration system - const { getEffectiveNocoDBConfig } = require('./admin-config'); - const effectiveConfig = getEffectiveNocoDBConfig(); - - const config = { - url: effectiveConfig.url, - token: effectiveConfig.token, - baseId: effectiveConfig.baseId + // Try to use the global configuration first + if (globalNocoDBConfig) { + console.log('[nocodb] Using global configuration - URL:', globalNocoDBConfig.url); + return { + url: globalNocoDBConfig.url, + token: globalNocoDBConfig.token, + baseId: globalNocoDBConfig.baseId }; - - console.log('[nocodb] Using effective configuration - URL:', config.url); - return config; - } catch (error) { - // Fallback to runtime config if admin config is not available - console.log('[nocodb] Admin config not available, using runtime config'); - const config = useRuntimeConfig().nocodb; - const fallbackConfig = { - ...config, - url: config.url || 'https://database.monacousa.org' - }; - console.log('[nocodb] Fallback configuration URL:', fallbackConfig.url); - return fallbackConfig; } + + // Fallback to runtime config + console.log('[nocodb] Global config not available, using runtime config'); + const config = useRuntimeConfig().nocodb; + const fallbackConfig = { + ...config, + url: config.url || 'https://database.monacousa.org' + }; + console.log('[nocodb] Fallback configuration URL:', fallbackConfig.url); + return fallbackConfig; }; export const createTableUrl = (table: Table | string) => { diff --git a/utils/types.ts b/utils/types.ts index ebd7c8b..b47321f 100644 --- a/utils/types.ts +++ b/utils/types.ts @@ -139,10 +139,10 @@ export interface Member { // Admin-only NocoDB Configuration export interface NocoDBSettings { - tableId: string; + url: string; apiKey: string; baseId: string; - url: string; + tables: { [tableName: string]: string }; // e.g., { "members": "m2sri3jqfqutiy5", "events": "evt123abc", ... } } export interface MemberResponse {