- Next 16.4 и Payload 3.91 в одном приложении, PostgreSQL с UUID, TypeScript 6, ESLint 9, Prettier, Vitest - Payload: администраторы с ролями, multi-tenant по сайтам, локализация en/ru/de/fr/es, интерфейс админки ru/en, языки сайта (sites.locales) - Коллекции sites, domains, currencies, countries, locations, tags, tariffs, payment-systems (manual), media и начальные данные (pnpm seed) - Proxy: хост → домен, язык и валюта, внутренние маршруты по хосту; /admin и /api закрыты на доменах брендов - Токены и темы брендов, оболочки сайта и кабинета, переключатели языка, валюты и темы, заглушки страниц - /svc/health, юнит systemd veilix-dev.service - Документация: решения D-022…D-024, команды в CLAUDE.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
1.7 KiB
TypeScript
55 lines
1.7 KiB
TypeScript
import config from '@payload-config'
|
|
import { getPayload } from 'payload'
|
|
import { cache } from 'react'
|
|
|
|
import {
|
|
type DomainApp,
|
|
type DomainConfig,
|
|
DOMAINS_TTL_MS,
|
|
type SiteCode,
|
|
toDomainConfig,
|
|
} from './types'
|
|
|
|
/** Активные домены из Payload */
|
|
export const listActiveDomains = async (): Promise<DomainConfig[]> => {
|
|
const payload = await getPayload({ config })
|
|
const { docs } = await payload.find({
|
|
collection: 'domains',
|
|
where: { active: { equals: true } },
|
|
depth: 1,
|
|
pagination: false,
|
|
overrideAccess: true,
|
|
})
|
|
return docs.map(toDomainConfig).filter((domain) => domain !== null)
|
|
}
|
|
|
|
let cached: { at: number; domains: Promise<DomainConfig[]> } | null = null
|
|
|
|
const activeDomains = (): Promise<DomainConfig[]> => {
|
|
if (!cached || Date.now() - cached.at > DOMAINS_TTL_MS) {
|
|
const domains = listActiveDomains()
|
|
cached = { at: Date.now(), domains }
|
|
domains.catch(() => {
|
|
cached = null
|
|
})
|
|
}
|
|
return cached.domains
|
|
}
|
|
|
|
/** Домен по хосту для страниц; null — домена нет или он выключен */
|
|
export const getDomain = cache(async (hostname: string): Promise<DomainConfig | null> => {
|
|
const domains = await activeDomains()
|
|
return domains.find((domain) => domain.hostname === hostname) ?? null
|
|
})
|
|
|
|
/** Основной домен сайта нужного типа: на него ведут ссылки между сайтом и кабинетом */
|
|
export const getPrimaryDomain = cache(
|
|
async (site: SiteCode, app: DomainApp): Promise<DomainConfig | null> => {
|
|
const domains = await activeDomains()
|
|
return (
|
|
domains.find((domain) => domain.site === site && domain.app === app && domain.isPrimary) ??
|
|
null
|
|
)
|
|
},
|
|
)
|