Refactor NocoDB settings to support dynamic table configuration and update related validation
Build And Push Image / docker (push) Successful in 3m13s
Details
Build And Push Image / docker (push) Successful in 3m13s
Details
This commit is contained in:
parent
d0d7a34ae7
commit
22a74c6b33
|
|
@ -68,7 +68,7 @@
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form.baseId"
|
v-model="form.baseId"
|
||||||
label="Base ID"
|
label="Base ID"
|
||||||
|
|
@ -81,17 +81,24 @@
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12">
|
||||||
|
<h3 class="text-h6 mb-4 text-primary">Table Configuration</h3>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-col cols="12">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form.tableId"
|
v-model="form.tables.members"
|
||||||
label="Members Table ID"
|
label="Members Table ID"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="[rules.required]"
|
:rules="[rules.required]"
|
||||||
required
|
required
|
||||||
placeholder="members-table-id"
|
placeholder="members-table-id"
|
||||||
:error="hasFieldError('tableId')"
|
:error="hasFieldError('tables.members')"
|
||||||
:error-messages="getFieldError('tableId')"
|
:error-messages="getFieldError('tables.members')"
|
||||||
/>
|
/>
|
||||||
|
<div class="text-caption text-medium-emphasis mt-1">
|
||||||
|
Configure the table ID for the Members functionality
|
||||||
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
|
|
@ -210,7 +217,7 @@ const form = ref<NocoDBSettings>({
|
||||||
url: 'https://database.monacousa.org',
|
url: 'https://database.monacousa.org',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseId: '',
|
baseId: '',
|
||||||
tableId: ''
|
tables: { members: '' }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Error handling
|
// Error handling
|
||||||
|
|
@ -261,6 +268,10 @@ const loadSettings = async () => {
|
||||||
|
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
form.value = { ...response.data };
|
form.value = { ...response.data };
|
||||||
|
// Ensure tables object exists
|
||||||
|
if (!form.value.tables) {
|
||||||
|
form.value.tables = { members: '' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to load NocoDB settings:', error);
|
console.error('Failed to load NocoDB settings:', error);
|
||||||
|
|
@ -352,7 +363,7 @@ const resetForm = () => {
|
||||||
url: 'https://database.monacousa.org',
|
url: 'https://database.monacousa.org',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseId: '',
|
baseId: '',
|
||||||
tableId: ''
|
tables: { members: '' }
|
||||||
};
|
};
|
||||||
clearFieldErrors();
|
clearFieldErrors();
|
||||||
connectionStatus.value = null;
|
connectionStatus.value = null;
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event) as NocoDBSettings;
|
const body = await readBody(event) as NocoDBSettings;
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (!body.url || !body.apiKey || !body.baseId || !body.tableId) {
|
if (!body.url || !body.apiKey || !body.baseId || !body.tables) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
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] Saving NocoDB configuration...');
|
||||||
console.log('[api/admin/nocodb-config.post] URL:', body.url);
|
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] 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]');
|
console.log('[api/admin/nocodb-config.post] API Key: [REDACTED]');
|
||||||
|
|
||||||
// Save configuration using the new admin config system
|
// Save configuration using the new admin config system
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event) as NocoDBSettings;
|
const body = await readBody(event) as NocoDBSettings;
|
||||||
|
|
||||||
// Validate required fields
|
// 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 {
|
return {
|
||||||
success: false,
|
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] Testing NocoDB connection...');
|
||||||
console.log('[api/admin/nocodb-test.post] URL:', body.url);
|
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] 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 {
|
try {
|
||||||
// Test connection by making a simple request to NocoDB
|
// Test connection by making a simple request to NocoDB using the first table
|
||||||
const testUrl = `${body.url}/api/v2/tables/${body.tableId}/records?limit=1`;
|
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);
|
console.log('[api/admin/nocodb-test.post] Testing URL:', testUrl);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { readFile, writeFile, mkdir, access, constants } from 'fs/promises';
|
import { readFile, writeFile, mkdir, access, constants } from 'fs/promises';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from 'crypto';
|
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from 'crypto';
|
||||||
import type { NocoDBSettings } from '~/utils/types';
|
import type { NocoDBSettings } from '~/utils/types';
|
||||||
|
|
@ -13,10 +14,20 @@ interface EffectiveNocoDB {
|
||||||
url: string;
|
url: string;
|
||||||
token: string;
|
token: string;
|
||||||
baseId: 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 CONFIG_FILE = join(CONFIG_DIR, 'admin-config.json');
|
||||||
const BACKUP_FILE = join(CONFIG_DIR, 'admin-config.backup.json');
|
const BACKUP_FILE = join(CONFIG_DIR, 'admin-config.backup.json');
|
||||||
const LOG_FILE = join(CONFIG_DIR, 'admin-config.log');
|
const LOG_FILE = join(CONFIG_DIR, 'admin-config.log');
|
||||||
|
|
@ -169,6 +180,15 @@ export async function loadAdminConfig(): Promise<AdminConfiguration | null> {
|
||||||
|
|
||||||
console.log('[admin-config] Configuration loaded from file');
|
console.log('[admin-config] Configuration loaded from file');
|
||||||
configCache = config;
|
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;
|
return config;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('[admin-config] No configuration file found, using defaults');
|
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');
|
console.log('[admin-config] Configuration saved to file');
|
||||||
await logConfigChange('CONFIG_SAVED', updatedBy, { url: config.url, baseId: config.baseId });
|
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) {
|
} catch (error) {
|
||||||
console.error('[admin-config] Failed to save configuration:', error);
|
console.error('[admin-config] Failed to save configuration:', error);
|
||||||
await logConfigChange('CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(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',
|
url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
|
||||||
token: runtimeConfig.nocodb?.token || '',
|
token: runtimeConfig.nocodb?.token || '',
|
||||||
baseId: runtimeConfig.nocodb?.baseId || '',
|
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
|
// Override with file configuration if available
|
||||||
|
|
@ -234,7 +263,7 @@ export function getEffectiveNocoDBConfig(): EffectiveNocoDB {
|
||||||
url: configCache.nocodb.url || envConfig.url,
|
url: configCache.nocodb.url || envConfig.url,
|
||||||
token: configCache.nocodb.apiKey || envConfig.token,
|
token: configCache.nocodb.apiKey || envConfig.token,
|
||||||
baseId: configCache.nocodb.baseId || envConfig.baseId,
|
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<NocoDBSettings> {
|
||||||
url: config.nocodb.url || runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
|
url: config.nocodb.url || runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
|
||||||
apiKey: config.nocodb.apiKey ? '••••••••••••••••' : '',
|
apiKey: config.nocodb.apiKey ? '••••••••••••••••' : '',
|
||||||
baseId: config.nocodb.baseId || runtimeConfig.nocodb?.baseId || '',
|
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<NocoDBSettings> {
|
||||||
url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
|
url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
|
||||||
apiKey: runtimeConfig.nocodb?.token ? '••••••••••••••••' : '',
|
apiKey: runtimeConfig.nocodb?.token ? '••••••••••••••••' : '',
|
||||||
baseId: runtimeConfig.nocodb?.baseId || '',
|
baseId: runtimeConfig.nocodb?.baseId || '',
|
||||||
tableId: 'members-table-id'
|
tables: { members: 'members-table-id' }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,13 @@ export interface EntityResponse<T> {
|
||||||
|
|
||||||
// Dynamic table ID getter - will use configured table ID from admin panel
|
// Dynamic table ID getter - will use configured table ID from admin panel
|
||||||
export const getTableId = (tableName: 'Members'): string => {
|
export const getTableId = (tableName: 'Members'): string => {
|
||||||
try {
|
// Try to get table ID from global configuration first
|
||||||
// Try to get table ID from persistent configuration
|
if (globalNocoDBConfig?.tables && tableName === 'Members') {
|
||||||
const { getEffectiveNocoDBConfig } = require('./admin-config');
|
const tableId = globalNocoDBConfig.tables['members'] || globalNocoDBConfig.tables['Members'];
|
||||||
const effectiveConfig = getEffectiveNocoDBConfig();
|
if (tableId) {
|
||||||
|
console.log(`[nocodb] Using global table ID for ${tableName}:`, tableId);
|
||||||
if (tableName === 'Members' && effectiveConfig.tableId) {
|
return tableId;
|
||||||
console.log(`[nocodb] Using configured table ID for ${tableName}:`, effectiveConfig.tableId);
|
|
||||||
return effectiveConfig.tableId;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.log(`[nocodb] Admin config not available for table ID, using fallback`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to default
|
// Fallback to default
|
||||||
|
|
@ -102,23 +98,28 @@ export const formatNationalitiesAsString = (nationalities: string[]): string =>
|
||||||
return nationalities.filter(n => n && n.trim()).join(',');
|
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 = () => {
|
export const getNocoDbConfiguration = () => {
|
||||||
try {
|
// Try to use the global configuration first
|
||||||
// Try to use the persistent configuration system
|
if (globalNocoDBConfig) {
|
||||||
const { getEffectiveNocoDBConfig } = require('./admin-config');
|
console.log('[nocodb] Using global configuration - URL:', globalNocoDBConfig.url);
|
||||||
const effectiveConfig = getEffectiveNocoDBConfig();
|
return {
|
||||||
|
url: globalNocoDBConfig.url,
|
||||||
const config = {
|
token: globalNocoDBConfig.token,
|
||||||
url: effectiveConfig.url,
|
baseId: globalNocoDBConfig.baseId
|
||||||
token: effectiveConfig.token,
|
|
||||||
baseId: effectiveConfig.baseId
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[nocodb] Using effective configuration - URL:', config.url);
|
// Fallback to runtime config
|
||||||
return config;
|
console.log('[nocodb] Global config not available, using runtime 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 config = useRuntimeConfig().nocodb;
|
||||||
const fallbackConfig = {
|
const fallbackConfig = {
|
||||||
...config,
|
...config,
|
||||||
|
|
@ -126,7 +127,6 @@ export const getNocoDbConfiguration = () => {
|
||||||
};
|
};
|
||||||
console.log('[nocodb] Fallback configuration URL:', fallbackConfig.url);
|
console.log('[nocodb] Fallback configuration URL:', fallbackConfig.url);
|
||||||
return fallbackConfig;
|
return fallbackConfig;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createTableUrl = (table: Table | string) => {
|
export const createTableUrl = (table: Table | string) => {
|
||||||
|
|
|
||||||
|
|
@ -139,10 +139,10 @@ export interface Member {
|
||||||
|
|
||||||
// Admin-only NocoDB Configuration
|
// Admin-only NocoDB Configuration
|
||||||
export interface NocoDBSettings {
|
export interface NocoDBSettings {
|
||||||
tableId: string;
|
url: string;
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
baseId: string;
|
baseId: string;
|
||||||
url: string;
|
tables: { [tableName: string]: string }; // e.g., { "members": "m2sri3jqfqutiy5", "events": "evt123abc", ... }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MemberResponse {
|
export interface MemberResponse {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue