Change Metabase URL from public domain to Docker container name for proper internal network communication between containers. Changes: - Update default METABASE_URL to http://metabase:3000 - Docker DNS resolution works correctly with container names - Maintains public domain fallback via environment variables This fixes authentication issues when containers communicate within the same Docker network.
129 lines
3.2 KiB
TypeScript
129 lines
3.2 KiB
TypeScript
/**
|
|
* Metabase API utility
|
|
*
|
|
* Handles authentication and requests to Metabase API
|
|
*/
|
|
|
|
const config = useRuntimeConfig()
|
|
const METABASE_URL = config.metabaseUrl || 'http://metabase:3000'
|
|
const METABASE_EMAIL = config.metabaseEmail || 'claudeCode0@nucleoriofrio.com'
|
|
const METABASE_PASSWORD = config.metabasePassword || 'vK^NyZdZDH#p'
|
|
|
|
let sessionToken: string | null = null
|
|
let tokenExpiry: number = 0
|
|
|
|
/**
|
|
* Get a valid Metabase session token
|
|
* Reuses existing token if still valid, otherwise requests a new one
|
|
*/
|
|
export async function getMetabaseToken(): Promise<string> {
|
|
// Check if we have a valid token
|
|
if (sessionToken && Date.now() < tokenExpiry) {
|
|
return sessionToken
|
|
}
|
|
|
|
// Request a new token
|
|
try {
|
|
const response = await $fetch<{ id: string }>(`${METABASE_URL}/api/session`, {
|
|
method: 'POST',
|
|
body: {
|
|
username: METABASE_EMAIL,
|
|
password: METABASE_PASSWORD
|
|
}
|
|
})
|
|
|
|
sessionToken = response.id
|
|
|
|
// Tokens expire after 14 days by default, but we'll refresh after 13 days to be safe
|
|
tokenExpiry = Date.now() + (13 * 24 * 60 * 60 * 1000)
|
|
|
|
console.log('[Metabase] New session token obtained')
|
|
return sessionToken
|
|
} catch (error) {
|
|
console.error('[Metabase] Failed to obtain session token:', error)
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: 'Failed to authenticate with Metabase'
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make an authenticated request to Metabase API
|
|
*/
|
|
export async function metabaseFetch<T = any>(
|
|
endpoint: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> {
|
|
const token = await getMetabaseToken()
|
|
|
|
try {
|
|
const response = await $fetch<T>(`${METABASE_URL}${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
...options.headers,
|
|
'X-Metabase-Session': token,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
|
|
return response
|
|
} catch (error: any) {
|
|
console.error(`[Metabase] Request failed for ${endpoint}:`, error)
|
|
|
|
// If token is invalid, clear it and retry once
|
|
if (error.statusCode === 401) {
|
|
console.log('[Metabase] Token invalid, clearing and retrying...')
|
|
sessionToken = null
|
|
tokenExpiry = 0
|
|
|
|
// Retry once with fresh token
|
|
const newToken = await getMetabaseToken()
|
|
return await $fetch<T>(`${METABASE_URL}${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
...options.headers,
|
|
'X-Metabase-Session': newToken,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
}
|
|
|
|
throw createError({
|
|
statusCode: error.statusCode || 500,
|
|
statusMessage: error.message || 'Metabase request failed'
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get database metadata
|
|
*/
|
|
export async function getMetabaseDatabases() {
|
|
return metabaseFetch('/api/database')
|
|
}
|
|
|
|
/**
|
|
* Get tables from a specific database
|
|
*/
|
|
export async function getMetabaseTables(databaseId: number) {
|
|
return metabaseFetch(`/api/database/${databaseId}/metadata`)
|
|
}
|
|
|
|
/**
|
|
* Execute a query against a table
|
|
*/
|
|
export async function queryMetabaseTable(databaseId: number, tableId: number, query: any = {}) {
|
|
return metabaseFetch('/api/dataset', {
|
|
method: 'POST',
|
|
body: {
|
|
database: databaseId,
|
|
type: 'query',
|
|
query: {
|
|
'source-table': tableId,
|
|
...query
|
|
}
|
|
}
|
|
})
|
|
}
|