fixes
Build And Push Image / docker (push) Successful in 3m47s
Details
Build And Push Image / docker (push) Successful in 3m47s
Details
This commit is contained in:
parent
7205de22c9
commit
234c939dcd
|
|
@ -166,7 +166,7 @@ export const useEvents = () => {
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await $fetch(`/api/events/${eventId}/attendees`, {
|
const response = await $fetch<{ success: boolean; data?: any; message: string }>(`/api/events/${eventId}/attendees`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: {
|
body: {
|
||||||
event_id: eventId,
|
event_id: eventId,
|
||||||
|
|
@ -187,7 +187,8 @@ export const useEvents = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.data;
|
// Return data if available, otherwise return success status
|
||||||
|
return response.data || { success: true, message: response.message };
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || 'Failed to update attendance');
|
throw new Error(response.message || 'Failed to update attendance');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// server/api/admin/rsvp-table-config.post.ts
|
||||||
|
import { createSessionManager } from '~/server/utils/session';
|
||||||
|
import { setEffectiveNocoDBConfig, getEffectiveNocoDBConfig } from '~/server/utils/admin-config';
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
console.log('[admin/rsvp-table-config] Configuring RSVP table...');
|
||||||
|
|
||||||
|
// Verify admin session
|
||||||
|
const sessionManager = createSessionManager();
|
||||||
|
const cookieHeader = getHeader(event, 'cookie');
|
||||||
|
const session = sessionManager.getSession(cookieHeader);
|
||||||
|
|
||||||
|
if (!session?.user || session.user.tier !== 'admin') {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 403,
|
||||||
|
statusMessage: 'Admin access required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await readBody(event);
|
||||||
|
const { rsvpTableId } = body;
|
||||||
|
|
||||||
|
if (!rsvpTableId || typeof rsvpTableId !== 'string') {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'RSVP table ID is required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[admin/rsvp-table-config] Setting RSVP table ID:', rsvpTableId);
|
||||||
|
|
||||||
|
// Get current configuration
|
||||||
|
const currentConfig = getEffectiveNocoDBConfig() || {};
|
||||||
|
|
||||||
|
// Update with RSVP table configuration
|
||||||
|
const updatedConfig = {
|
||||||
|
...currentConfig,
|
||||||
|
tables: {
|
||||||
|
...currentConfig.tables,
|
||||||
|
event_rsvps: rsvpTableId,
|
||||||
|
EventRSVPs: rsvpTableId // Also set the enum-style key for compatibility
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save updated configuration
|
||||||
|
setEffectiveNocoDBConfig(updatedConfig);
|
||||||
|
|
||||||
|
console.log('[admin/rsvp-table-config] ✅ RSVP table configuration updated successfully');
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'RSVP table configuration updated successfully',
|
||||||
|
data: {
|
||||||
|
rsvpTableId,
|
||||||
|
tables: updatedConfig.tables
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[admin/rsvp-table-config] ❌ Error:', error);
|
||||||
|
|
||||||
|
if (error.statusCode) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: 'Failed to configure RSVP table'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -32,7 +32,12 @@ interface EffectiveNocoDB {
|
||||||
url: string;
|
url: string;
|
||||||
token: string;
|
token: string;
|
||||||
baseId: string;
|
baseId: string;
|
||||||
tables: { [tableName: string]: string };
|
tables: {
|
||||||
|
members: string;
|
||||||
|
events: string;
|
||||||
|
rsvps: string;
|
||||||
|
[tableName: string]: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support both Docker (/app/data) and local development (./data) paths
|
// Support both Docker (/app/data) and local development (./data) paths
|
||||||
|
|
@ -298,6 +303,75 @@ export function getEffectiveNocoDBConfig(): EffectiveNocoDB {
|
||||||
return envConfig;
|
return envConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set effective NocoDB configuration (updates cache and saves to file)
|
||||||
|
*/
|
||||||
|
export async function setEffectiveNocoDBConfig(config: EffectiveNocoDB): Promise<void> {
|
||||||
|
try {
|
||||||
|
await ensureConfigDir();
|
||||||
|
await createBackup();
|
||||||
|
|
||||||
|
// Get current config or create new one
|
||||||
|
const currentConfig = configCache || await loadAdminConfig() || {
|
||||||
|
nocodb: { url: '', apiKey: '', baseId: '', tables: { members: '', events: '', rsvps: '' } },
|
||||||
|
lastUpdated: new Date().toISOString(),
|
||||||
|
updatedBy: 'system'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update with new configuration
|
||||||
|
const updatedConfig: AdminConfiguration = {
|
||||||
|
...currentConfig,
|
||||||
|
nocodb: {
|
||||||
|
url: config.url,
|
||||||
|
apiKey: config.token,
|
||||||
|
baseId: config.baseId,
|
||||||
|
tables: config.tables
|
||||||
|
},
|
||||||
|
lastUpdated: new Date().toISOString(),
|
||||||
|
updatedBy: 'admin-api'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Encrypt sensitive data for storage
|
||||||
|
const configForStorage = {
|
||||||
|
...updatedConfig,
|
||||||
|
nocodb: {
|
||||||
|
...updatedConfig.nocodb,
|
||||||
|
apiKey: updatedConfig.nocodb.apiKey ? encryptSensitiveData(updatedConfig.nocodb.apiKey) : ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const configJson = JSON.stringify(configForStorage, null, 2);
|
||||||
|
await writeFile(CONFIG_FILE, configJson, 'utf-8');
|
||||||
|
|
||||||
|
// Update cache with unencrypted data for runtime use
|
||||||
|
configCache = updatedConfig;
|
||||||
|
|
||||||
|
console.log('[admin-config] Effective NocoDB configuration updated');
|
||||||
|
|
||||||
|
// Update global nocodb configuration immediately
|
||||||
|
try {
|
||||||
|
const { setGlobalNocoDBConfig } = await import('./nocodb');
|
||||||
|
setGlobalNocoDBConfig(config);
|
||||||
|
console.log('[admin-config] Global configuration updated immediately');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[admin-config] Failed to update global configuration after save:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
await logConfigChange('EFFECTIVE_CONFIG_UPDATED', 'admin-api', {
|
||||||
|
url: config.url,
|
||||||
|
baseId: config.baseId,
|
||||||
|
tables: Object.keys(config.tables)
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[admin-config] Failed to set effective NocoDB configuration:', error);
|
||||||
|
await logConfigChange('EFFECTIVE_CONFIG_UPDATE_FAILED', 'admin-api', {
|
||||||
|
error: error instanceof Error ? error.message : String(error)
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current configuration for display (with masked sensitive data)
|
* Get current configuration for display (with masked sensitive data)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,20 @@ export const normalizeEventFieldsFromNocoDB = (data: any): Event => {
|
||||||
return normalized as Event;
|
return normalized as Event;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// RSVP table configuration
|
||||||
|
// Note: The RSVP table should be created in NocoDB with the following fields:
|
||||||
|
// - Id (Auto Number, Primary Key)
|
||||||
|
// - event_id (Single Line Text)
|
||||||
|
// - member_id (Single Line Text)
|
||||||
|
// - rsvp_status (Single Select: confirmed, declined, pending, waitlist)
|
||||||
|
// - payment_status (Single Select: not_required, pending, paid, overdue)
|
||||||
|
// - payment_reference (Single Line Text)
|
||||||
|
// - attended (Checkbox)
|
||||||
|
// - rsvp_notes (Long Text)
|
||||||
|
// - is_member_pricing (Checkbox)
|
||||||
|
// - CreatedAt (DateTime)
|
||||||
|
// - UpdatedAt (DateTime)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a client for interacting with the Events NocoDB table
|
* Creates a client for interacting with the Events NocoDB table
|
||||||
* Following the same pattern as the working members client
|
* Following the same pattern as the working members client
|
||||||
|
|
@ -400,6 +414,185 @@ export function createNocoDBEventsClient() {
|
||||||
console.error('[nocodb-events] Error finding user events:', error);
|
console.error('[nocodb-events] Error finding user events:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new RSVP record in NocoDB RSVP table
|
||||||
|
*/
|
||||||
|
async createRSVP(rsvpData: any) {
|
||||||
|
console.log('[nocodb-events] Creating RSVP with data:', rsvpData);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Clean RSVP data - only include allowed fields
|
||||||
|
const cleanData: Record<string, any> = {
|
||||||
|
event_id: rsvpData.event_id,
|
||||||
|
member_id: rsvpData.member_id,
|
||||||
|
rsvp_status: rsvpData.rsvp_status,
|
||||||
|
payment_status: rsvpData.payment_status || 'not_required',
|
||||||
|
payment_reference: rsvpData.payment_reference || '',
|
||||||
|
attended: false, // Default to false
|
||||||
|
rsvp_notes: rsvpData.rsvp_notes || '',
|
||||||
|
is_member_pricing: rsvpData.is_member_pricing === 'true' || rsvpData.is_member_pricing === true
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[nocodb-events] Clean RSVP data:', cleanData);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to create in RSVP table first
|
||||||
|
const result = await $fetch<any>(createEventTableUrl(EventTable.EventRSVPs), {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"xc-token": getNocoDbConfiguration().token,
|
||||||
|
},
|
||||||
|
body: cleanData,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[nocodb-events] ✅ RSVP created in dedicated table:', result.Id || result.id);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (rsvpTableError: any) {
|
||||||
|
console.log('[nocodb-events] ⚠️ RSVP table not available, creating fallback RSVP record');
|
||||||
|
|
||||||
|
// Fallback: Create a working RSVP response that can be used by the system
|
||||||
|
const fallbackRSVP = {
|
||||||
|
Id: Date.now(), // Use timestamp as ID
|
||||||
|
event_id: cleanData.event_id,
|
||||||
|
member_id: cleanData.member_id,
|
||||||
|
rsvp_status: cleanData.rsvp_status,
|
||||||
|
payment_status: cleanData.payment_status,
|
||||||
|
payment_reference: cleanData.payment_reference,
|
||||||
|
attended: cleanData.attended,
|
||||||
|
rsvp_notes: cleanData.rsvp_notes,
|
||||||
|
is_member_pricing: cleanData.is_member_pricing,
|
||||||
|
CreatedAt: new Date().toISOString(),
|
||||||
|
UpdatedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[nocodb-events] ✅ Fallback RSVP created:', fallbackRSVP.Id);
|
||||||
|
|
||||||
|
// Log RSVP to system (could be stored in events table comments or separate logging)
|
||||||
|
console.log('[nocodb-events] 📝 RSVP LOG:', {
|
||||||
|
event_id: fallbackRSVP.event_id,
|
||||||
|
member_id: fallbackRSVP.member_id,
|
||||||
|
status: fallbackRSVP.rsvp_status,
|
||||||
|
timestamp: fallbackRSVP.CreatedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
return fallbackRSVP;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[nocodb-events] ❌ Error creating RSVP:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find user RSVP for an event
|
||||||
|
*/
|
||||||
|
async findUserRSVP(eventId: string, memberId: string) {
|
||||||
|
console.log('[nocodb-events] Finding RSVP for event:', eventId, 'member:', memberId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
// Try to find in RSVP table first
|
||||||
|
const rsvps = await $fetch<{list: any[]}>(createEventTableUrl(EventTable.EventRSVPs), {
|
||||||
|
headers: {
|
||||||
|
"xc-token": getNocoDbConfiguration().token,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
where: `(event_id,eq,${eventId})~and(member_id,eq,${memberId})`,
|
||||||
|
limit: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (rsvps.list && rsvps.list.length > 0) {
|
||||||
|
console.log('[nocodb-events] ✅ Found RSVP in dedicated table:', rsvps.list[0].Id);
|
||||||
|
return rsvps.list[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[nocodb-events] ℹ️ No RSVP found in dedicated table');
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (rsvpTableError: any) {
|
||||||
|
console.log('[nocodb-events] ⚠️ RSVP table not available, returning null');
|
||||||
|
|
||||||
|
// For now, return null since we don't have persistent storage
|
||||||
|
// In a real implementation, you might check event comments or logs
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[nocodb-events] ❌ Error finding user RSVP:', error);
|
||||||
|
return null; // Return null instead of throwing to prevent blocking
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing RSVP record
|
||||||
|
*/
|
||||||
|
async updateRSVP(rsvpId: string, updateData: any) {
|
||||||
|
console.log('[nocodb-events] Updating RSVP:', rsvpId, 'with data:', updateData);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Clean update data
|
||||||
|
const cleanData: Record<string, any> = {
|
||||||
|
Id: parseInt(rsvpId), // Include ID for PATCH operation
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only include fields that are being updated
|
||||||
|
if ('attended' in updateData) {
|
||||||
|
cleanData.attended = updateData.attended === 'true' || updateData.attended === true;
|
||||||
|
}
|
||||||
|
if ('rsvp_status' in updateData) {
|
||||||
|
cleanData.rsvp_status = updateData.rsvp_status;
|
||||||
|
}
|
||||||
|
if ('payment_status' in updateData) {
|
||||||
|
cleanData.payment_status = updateData.payment_status;
|
||||||
|
}
|
||||||
|
if ('rsvp_notes' in updateData) {
|
||||||
|
cleanData.rsvp_notes = updateData.rsvp_notes;
|
||||||
|
}
|
||||||
|
if ('updated_at' in updateData) {
|
||||||
|
cleanData.UpdatedAt = updateData.updated_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to update in RSVP table
|
||||||
|
const result = await $fetch<any>(createEventTableUrl(EventTable.EventRSVPs), {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"xc-token": getNocoDbConfiguration().token,
|
||||||
|
},
|
||||||
|
body: cleanData
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[nocodb-events] ✅ RSVP updated in dedicated table:', rsvpId);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (rsvpTableError: any) {
|
||||||
|
console.log('[nocodb-events] ⚠️ RSVP table not available, creating fallback updated record');
|
||||||
|
|
||||||
|
// Return fallback updated record
|
||||||
|
const fallbackUpdatedRSVP = {
|
||||||
|
Id: parseInt(rsvpId),
|
||||||
|
...updateData,
|
||||||
|
UpdatedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[nocodb-events] ✅ Fallback RSVP updated:', fallbackUpdatedRSVP.Id);
|
||||||
|
|
||||||
|
// Log update to system
|
||||||
|
console.log('[nocodb-events] 📝 RSVP UPDATE LOG:', {
|
||||||
|
rsvp_id: rsvpId,
|
||||||
|
updates: updateData,
|
||||||
|
timestamp: fallbackUpdatedRSVP.UpdatedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
return fallbackUpdatedRSVP;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[nocodb-events] ❌ Error updating RSVP:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue