Discover NocoDB table IDs dynamically instead of hardcoding them
All checks were successful
Deploy HiHala Dashboard / deploy (push) Successful in 7s
All checks were successful
Deploy HiHala Dashboard / deploy (push) Successful in 7s
Table IDs are now fetched at runtime via the NocoDB meta API using VITE_NOCODB_BASE_ID, so the same code works against any NocoDB instance (local or Cloudron). Also adds a migration script for moving data between instances with correct FK remapping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,14 +19,37 @@ import type {
|
||||
|
||||
const NOCODB_URL = import.meta.env.VITE_NOCODB_URL || '';
|
||||
const NOCODB_TOKEN = import.meta.env.VITE_NOCODB_TOKEN || '';
|
||||
const NOCODB_BASE_ID = import.meta.env.VITE_NOCODB_BASE_ID || '';
|
||||
|
||||
// Table IDs (Cloudron NocoDB)
|
||||
const NOCODB_TABLES = {
|
||||
districts: 'mddorhm0boab99m',
|
||||
museums: 'm1os227987acanj',
|
||||
dailyStats: 'mbp0qntf9h6qth1',
|
||||
pilgrimStats: 'mi90dy6w7mt0vp0'
|
||||
};
|
||||
// Table IDs discovered dynamically from NocoDB meta API
|
||||
let discoveredTables: Record<string, string> | null = null;
|
||||
|
||||
async function discoverTableIds(): Promise<Record<string, string>> {
|
||||
if (discoveredTables) return discoveredTables;
|
||||
|
||||
if (!NOCODB_BASE_ID) throw new Error('VITE_NOCODB_BASE_ID not configured');
|
||||
|
||||
const res = await fetch(
|
||||
`${NOCODB_URL}/api/v2/meta/bases/${NOCODB_BASE_ID}/tables`,
|
||||
{ headers: { 'xc-token': NOCODB_TOKEN } }
|
||||
);
|
||||
if (!res.ok) throw new Error(`Failed to discover tables: HTTP ${res.status}`);
|
||||
|
||||
const json = await res.json();
|
||||
const tables: Record<string, string> = {};
|
||||
for (const t of json.list) {
|
||||
tables[t.title] = t.id;
|
||||
}
|
||||
|
||||
const required = ['Districts', 'Museums', 'DailyStats'];
|
||||
for (const name of required) {
|
||||
if (!tables[name]) throw new Error(`Required table '${name}' not found in NocoDB base`);
|
||||
}
|
||||
|
||||
discoveredTables = tables;
|
||||
console.log('Discovered NocoDB tables:', Object.keys(tables).map(k => `${k}=${tables[k]}`).join(', '));
|
||||
return tables;
|
||||
}
|
||||
|
||||
// Cache keys
|
||||
const CACHE_KEY = 'hihala_data_cache';
|
||||
@@ -42,7 +65,12 @@ export let umrahData: UmrahData = {
|
||||
// Fetch pilgrim stats from NocoDB and update umrahData
|
||||
export async function fetchPilgrimStats(): Promise<UmrahData> {
|
||||
try {
|
||||
const url = `${NOCODB_URL}/api/v2/tables/${NOCODB_TABLES.pilgrimStats}/records?limit=50`;
|
||||
const tables = await discoverTableIds();
|
||||
if (!tables['PilgrimStats']) {
|
||||
console.warn('PilgrimStats table not found, using defaults');
|
||||
return umrahData;
|
||||
}
|
||||
const url = `${NOCODB_URL}/api/v2/tables/${tables['PilgrimStats']}/records?limit=50`;
|
||||
const res = await fetch(url, { headers: { 'xc-token': NOCODB_TOKEN } });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json = await res.json();
|
||||
@@ -165,12 +193,14 @@ interface MuseumMapEntry {
|
||||
|
||||
async function fetchFromNocoDB(): Promise<MuseumRecord[]> {
|
||||
console.log('Fetching from NocoDB...');
|
||||
|
||||
|
||||
const tables = await discoverTableIds();
|
||||
|
||||
// Fetch all three tables in parallel
|
||||
const [districts, museums, dailyStats] = await Promise.all([
|
||||
fetchNocoDBTable<NocoDBDistrict>(NOCODB_TABLES.districts),
|
||||
fetchNocoDBTable<NocoDBMuseum>(NOCODB_TABLES.museums),
|
||||
fetchNocoDBTable<NocoDBDailyStat>(NOCODB_TABLES.dailyStats)
|
||||
fetchNocoDBTable<NocoDBDistrict>(tables['Districts']),
|
||||
fetchNocoDBTable<NocoDBMuseum>(tables['Museums']),
|
||||
fetchNocoDBTable<NocoDBDailyStat>(tables['DailyStats'])
|
||||
]);
|
||||
|
||||
// Build lookup maps
|
||||
@@ -223,14 +253,14 @@ async function fetchFromNocoDB(): Promise<MuseumRecord[]> {
|
||||
|
||||
export async function fetchData(): Promise<FetchResult> {
|
||||
// Check if NocoDB is configured
|
||||
if (!NOCODB_URL || !NOCODB_TOKEN) {
|
||||
if (!NOCODB_URL || !NOCODB_TOKEN || !NOCODB_BASE_ID) {
|
||||
// Try cache
|
||||
const cached = loadFromCache();
|
||||
if (cached) {
|
||||
console.warn('NocoDB not configured, using cached data');
|
||||
return { data: cached.data, fromCache: true, cacheTimestamp: cached.timestamp };
|
||||
}
|
||||
throw new Error('NocoDB not configured and no cached data available. Set VITE_NOCODB_URL and VITE_NOCODB_TOKEN in .env.local');
|
||||
throw new Error('NocoDB not configured and no cached data available. Set VITE_NOCODB_URL, VITE_NOCODB_TOKEN, and VITE_NOCODB_BASE_ID in .env.local');
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -257,7 +287,7 @@ export async function fetchData(): Promise<FetchResult> {
|
||||
|
||||
// Force refresh (bypass cache read, but still write to cache)
|
||||
export async function refreshData(): Promise<FetchResult> {
|
||||
if (!NOCODB_URL || !NOCODB_TOKEN) {
|
||||
if (!NOCODB_URL || !NOCODB_TOKEN || !NOCODB_BASE_ID) {
|
||||
throw new Error('NocoDB not configured');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user