import { getDirectusClient } from './client'; import { readItems, readItem, readSingleton, aggregate } from '@directus/sdk'; import type { DirectusSchema, Pages as Page, Posts as Post, Sites as Site, DirectusUsers as User, Globals, Navigation } from '../schemas'; const directus = getDirectusClient(); /** * Fetch a page by permalink (tenant-safe) */ export async function fetchPageByPermalink( permalink: string, siteId: string, options?: { preview?: boolean; token?: string } ): Promise { const filter: Record = { permalink: { _eq: permalink }, site_id: { _eq: siteId } }; if (!options?.preview) { filter.status = { _eq: 'published' }; } try { const pages = await directus.request( readItems('pages', { filter, limit: 1, fields: [ 'id', 'title', 'permalink', 'site_id', 'status', 'seo_title', 'seo_description', 'seo_image', 'blocks', // Fetch as simple JSON field 'schema_json' ] }) ); return pages?.[0] || null; } catch (err) { console.error('Error fetching page:', err); return null; } } /** * Fetch site globals */ export async function fetchSiteGlobals(siteId: string): Promise { try { const globals = await directus.request( readItems('globals', { filter: { site_id: { _eq: siteId } }, limit: 1, fields: ['*'] }) ); return (globals as unknown as Globals[])?.[0] || null; } catch (err) { console.error('Error fetching globals:', err); return null; } } /** * Fetch site navigation */ export async function fetchNavigation(siteId: string): Promise[]> { try { const nav = await directus.request( readItems('navigation', { filter: { site_id: { _eq: siteId } }, sort: ['sort'], fields: ['id', 'label', 'url', 'parent', 'target', 'sort'] }) ); return (nav as unknown as Navigation[]) || []; } catch (err) { console.error('Error fetching navigation:', err); return []; } } /** * Fetch posts for a site */ export async function fetchPosts( siteId: string, options?: { limit?: number; page?: number; category?: string } ): Promise<{ posts: Partial[]; total: number }> { const limit = options?.limit || 10; const page = options?.page || 1; const offset = (page - 1) * limit; const filter: Record = { site_id: { _eq: siteId }, // siteId is UUID string status: { _eq: 'published' } }; if (options?.category) { filter.category = { _eq: options.category }; } try { const [posts, countResult] = await Promise.all([ directus.request( readItems('posts', { filter, limit, offset, sort: ['-published_at'], fields: [ 'id', 'title', 'slug', 'excerpt', 'featured_image', 'published_at', 'category', 'author', 'site_id', 'status', 'content' ] }) ), directus.request( aggregate('posts', { aggregate: { count: '*' }, query: { filter } }) ) ]); return { posts: (posts as Partial[]) || [], total: Number(countResult?.[0]?.count || 0) }; } catch (err) { console.error('Error fetching posts:', err); return { posts: [], total: 0 }; } } /** * Fetch a single post by slug */ export async function fetchPostBySlug( slug: string, siteId: string ): Promise { try { const posts = await directus.request( readItems('posts', { filter: { slug: { _eq: slug }, site_id: { _eq: siteId }, status: { _eq: 'published' } }, limit: 1, fields: ['*'] }) ); return posts?.[0] || null; } catch (err) { console.error('Error fetching post:', err); return null; } } /** * Fetch generated articles for a site */ export async function fetchGeneratedArticles( siteId: string, options?: { limit?: number; page?: number } ): Promise<{ articles: any[]; total: number }> { const limit = options?.limit || 20; const page = options?.page || 1; const offset = (page - 1) * limit; try { const [articles, countResult] = await Promise.all([ directus.request( readItems('generated_articles', { filter: { site_id: { _eq: siteId } }, // UUID string limit, offset, sort: ['-date_created'], fields: ['*'] }) ), directus.request( aggregate('generated_articles', { aggregate: { count: '*' }, query: { filter: { site_id: { _eq: siteId } } } // UUID string }) ) ]); return { articles: articles || [], total: Number(countResult?.[0]?.count || 0) }; } catch (err) { console.error('Error fetching articles:', err); return { articles: [], total: 0 }; } } /** * Fetch a single generated article by slug */ export async function fetchGeneratedArticleBySlug( slug: string, siteId: string ): Promise { try { const articles = await directus.request( readItems('generated_articles', { filter: { _and: [ { slug: { _eq: slug } }, { site_id: { _eq: siteId } }, { is_published: { _eq: true } } ] }, limit: 1, fields: ['*'] }) ); return articles?.[0] || null; } catch (err) { console.error('Error fetching generated article:', err); return null; } } /** * Fetch SEO campaigns */ export async function fetchCampaigns(siteId?: string) { const filter: Record = {}; if (siteId) { filter._or = [ { site_id: { _eq: siteId } }, { site_id: { _null: true } } ]; } try { return await directus.request( readItems('campaign_masters', { filter, sort: ['-date_created'], fields: ['*'] }) ); } catch (err) { console.error('Error fetching campaigns:', err); return []; } } /** * Fetch locations (states, counties, cities) */ export async function fetchStates() { try { return await directus.request( readItems('locations_states', { sort: ['name'], fields: ['*'] }) ); } catch (err) { console.error('Error fetching states:', err); return []; } } export async function fetchCountiesByState(stateId: string) { try { return await directus.request( readItems('locations_counties', { filter: { state: { _eq: stateId } }, sort: ['name'], fields: ['*'] }) ); } catch (err) { console.error('Error fetching counties:', err); return []; } } export async function fetchCitiesByCounty(countyId: string, limit = 50) { try { return await directus.request( readItems('locations_cities', { filter: { county: { _eq: countyId } }, sort: ['-population'], limit, fields: ['*'] }) ); } catch (err) { console.error('Error fetching cities:', err); return []; } }