230 lines
7.5 KiB
JavaScript
230 lines
7.5 KiB
JavaScript
const axios = require('axios');
|
|
const config = require('../config');
|
|
const logger = require('../utils/logger');
|
|
|
|
class NocoDBService {
|
|
constructor() {
|
|
this.apiUrl = config.nocodb.apiUrl;
|
|
this.apiToken = config.nocodb.apiToken;
|
|
this.projectId = config.nocodb.projectId;
|
|
this.timeout = 10000; // 10 seconds
|
|
|
|
// Create axios instance with defaults
|
|
this.client = axios.create({
|
|
baseURL: this.apiUrl,
|
|
timeout: this.timeout,
|
|
headers: {
|
|
'xc-token': this.apiToken,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
// Add response interceptor for error handling
|
|
this.client.interceptors.response.use(
|
|
response => response,
|
|
error => {
|
|
logger.error('NocoDB API Error:', {
|
|
message: error.message,
|
|
url: error.config?.url,
|
|
method: error.config?.method,
|
|
status: error.response?.status,
|
|
data: error.response?.data
|
|
});
|
|
throw error;
|
|
}
|
|
);
|
|
}
|
|
|
|
// Build table URL
|
|
getTableUrl(tableId) {
|
|
return `/db/data/v1/${this.projectId}/${tableId}`;
|
|
}
|
|
|
|
// Get all records from a table
|
|
async getAll(tableId, params = {}) {
|
|
const url = this.getTableUrl(tableId);
|
|
const response = await this.client.get(url, { params });
|
|
return response.data;
|
|
}
|
|
|
|
// Get ALL records from a table using pagination
|
|
async getAllPaginated(tableId, params = {}) {
|
|
try {
|
|
let allRecords = [];
|
|
let offset = 0;
|
|
const limit = params.limit || 100;
|
|
let hasMore = true;
|
|
|
|
while (hasMore) {
|
|
const response = await this.getAll(tableId, {
|
|
...params,
|
|
limit: limit,
|
|
offset: offset
|
|
});
|
|
|
|
const records = response.list || [];
|
|
allRecords = allRecords.concat(records);
|
|
|
|
// Check if there are more records
|
|
hasMore = records.length === limit;
|
|
offset += limit;
|
|
|
|
// Safety check to prevent infinite loops
|
|
if (offset > 10000) {
|
|
logger.warn(`Reached maximum offset limit while fetching records from table ${tableId}`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
logger.info(`Fetched ${allRecords.length} total records from table ${tableId}`);
|
|
|
|
return {
|
|
list: allRecords,
|
|
pageInfo: {
|
|
totalRows: allRecords.length,
|
|
page: 1,
|
|
pageSize: allRecords.length,
|
|
isFirstPage: true,
|
|
isLastPage: true
|
|
}
|
|
};
|
|
} catch (error) {
|
|
logger.error('Error fetching paginated records:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Get single record
|
|
async getById(tableId, recordId) {
|
|
const url = `${this.getTableUrl(tableId)}/${recordId}`;
|
|
const response = await this.client.get(url);
|
|
return response.data;
|
|
}
|
|
|
|
// Create record
|
|
async create(sheetId, data) {
|
|
try {
|
|
// Explicitly remove any ID field to prevent NocoDB conflicts
|
|
const cleanData = { ...data };
|
|
|
|
// Remove all possible ID field variations
|
|
delete cleanData.ID;
|
|
delete cleanData.id;
|
|
delete cleanData.Id;
|
|
delete cleanData.iD;
|
|
|
|
// Remove any undefined values to prevent issues
|
|
Object.keys(cleanData).forEach(key => {
|
|
if (cleanData[key] === undefined) {
|
|
delete cleanData[key];
|
|
}
|
|
});
|
|
|
|
logger.info(`Creating record in sheet ${sheetId}`);
|
|
logger.info(`Data being sent to NocoDB:`, JSON.stringify(cleanData, null, 2));
|
|
|
|
const url = this.getTableUrl(sheetId);
|
|
const response = await this.client.post(url, cleanData);
|
|
|
|
logger.info(`Create response status: ${response.status}`);
|
|
logger.info(`Create response:`, JSON.stringify(response.data, null, 2));
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
logger.error('Error creating record:', error);
|
|
logger.error('Error response data:', JSON.stringify(error.response?.data, null, 2));
|
|
logger.error('Error response status:', error.response?.status);
|
|
logger.error('Error response headers:', JSON.stringify(error.response?.headers, null, 2));
|
|
logger.error('Request URL:', error.config?.url);
|
|
logger.error('Request method:', error.config?.method);
|
|
logger.error('Request data:', JSON.stringify(error.config?.data, null, 2));
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Update record
|
|
async update(tableId, recordId, data) {
|
|
const url = `${this.getTableUrl(tableId)}/${recordId}`;
|
|
const response = await this.client.patch(url, data);
|
|
return response.data;
|
|
}
|
|
|
|
// Delete record
|
|
async delete(tableId, recordId) {
|
|
const url = `${this.getTableUrl(tableId)}/${recordId}`;
|
|
const response = await this.client.delete(url);
|
|
return response.data;
|
|
}
|
|
|
|
// Get locations with proper filtering
|
|
async getLocations(params = {}) {
|
|
// For locations, we want all records by default, so use getAllPaginated
|
|
// unless specific limit/offset are provided
|
|
if (!params.limit && !params.offset) {
|
|
return this.getAllPaginated(config.nocodb.tableId, params);
|
|
}
|
|
|
|
const defaultParams = {
|
|
limit: 1000,
|
|
offset: 0,
|
|
...params
|
|
};
|
|
|
|
return this.getAll(config.nocodb.tableId, defaultParams);
|
|
}
|
|
|
|
// Get user by email
|
|
async getUserByEmail(email) {
|
|
if (!config.nocodb.loginSheetId) {
|
|
throw new Error('Login sheet not configured');
|
|
}
|
|
|
|
const response = await this.getAll(config.nocodb.loginSheetId, {
|
|
where: `(Email,eq,${email})`,
|
|
limit: 1
|
|
});
|
|
|
|
return response.list?.[0] || null;
|
|
}
|
|
|
|
// Get latest settings
|
|
async getLatestSettings() {
|
|
if (!config.nocodb.settingsSheetId) {
|
|
return null;
|
|
}
|
|
|
|
const response = await this.getAll(config.nocodb.settingsSheetId, {
|
|
sort: '-created_at',
|
|
limit: 1
|
|
});
|
|
|
|
return response.list?.[0] || null;
|
|
}
|
|
|
|
// Get settings with walk sheet data
|
|
async getWalkSheetSettings() {
|
|
if (!config.nocodb.settingsSheetId) {
|
|
return null;
|
|
}
|
|
|
|
const response = await this.getAll(config.nocodb.settingsSheetId, {
|
|
sort: '-created_at',
|
|
limit: 20
|
|
});
|
|
|
|
// Find first row with walk sheet data
|
|
const settings = response.list?.find(row =>
|
|
row.walk_sheet_title ||
|
|
row.walk_sheet_subtitle ||
|
|
row.walk_sheet_footer ||
|
|
row.qr_code_1_url ||
|
|
row.qr_code_2_url ||
|
|
row.qr_code_3_url
|
|
) || response.list?.[0];
|
|
|
|
return settings || null;
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
module.exports = new NocoDBService(); |