diff --git a/package.json b/package.json index 99017c4..dbbc0ef 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "types:generate": "openapi-typescript http://localhost:1337/swagger-spec.json -o ./src/api/paths.ts", "graphql:generate": "graphql-codegen", "graphql:schema": "graphql-codegen -p introspect", - "endpoint:generate": "node scripts/endpoint-generate.mjs" + "endpoint:generate": "node scripts/endpoint-generate.mjs", + "seed:categories": "node scripts/seed-strapi-categories.mjs" }, "dependencies": { "@astrojs/react": "^4.4.2", diff --git a/scripts/seed-strapi-categories.mjs b/scripts/seed-strapi-categories.mjs new file mode 100644 index 0000000..b311fee --- /dev/null +++ b/scripts/seed-strapi-categories.mjs @@ -0,0 +1,171 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, ".."); +const categoriesDir = path.join(root, "src", "content", "categories"); + +const defaultEndpoint = "http://localhost:1337/graphql"; +const endpoint = + process.env.STRAPI_GRAPHQL_URL ?? + process.env.PUBLIC_GRAPHQL_URL ?? + defaultEndpoint; + +const token = + process.env.STRAPI_TOKEN ?? + process.env.STRAPI_API_TOKEN ?? + process.env.AUTH_TOKEN; + +function readCategorySource(slug) { + const filePath = path.join(categoriesDir, `${slug}.json`); + if (!fs.existsSync(filePath)) { + throw new Error(`Missing source file: ${path.relative(root, filePath)}`); + } + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +function discoverCategorySlugs() { + if (!fs.existsSync(categoriesDir)) { + throw new Error(`Categories directory missing: ${path.relative(root, categoriesDir)}`); + } + + const slugs = []; + for (const dirent of fs.readdirSync(categoriesDir, { withFileTypes: true })) { + if (!dirent.isFile() || !dirent.name.endsWith(".json")) continue; + const slug = path.basename(dirent.name, ".json"); + const source = readCategorySource(slug); + const { group } = source; + + if (group !== "solution" && group !== "service") { + throw new Error( + `Category ${slug} has invalid group "${String(group)}". Expected "solution" or "service".` + ); + } + + slugs.push(slug); + } + + if (slugs.length === 0) { + throw new Error(`No *.json categories in ${path.relative(root, categoriesDir)}`); + } + + return slugs; +} + +async function gql(query, variables = {}) { + const headers = { + "Content-Type": "application/json", + Accept: "application/graphql-response+json, application/json", + }; + + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch(endpoint, { + method: "POST", + headers, + body: JSON.stringify({ query, variables }), + }); + + if (!response.ok) { + throw new Error(`GraphQL HTTP ${response.status}: ${response.statusText}`); + } + + const payload = await response.json(); + if (payload.errors?.length) { + const messages = payload.errors.map((err) => err.message).join("; "); + throw new Error(`GraphQL error: ${messages}`); + } + + return payload.data; +} + +async function categoryExists(slug) { + const query = ` + query CategoryBySlug($slug: String!) { + categories(filters: { slug: { eq: $slug } }, pagination: { limit: 1 }) { + documentId + } + } + `; + + const data = await gql(query, { slug }); + return Array.isArray(data?.categories) && data.categories.length > 0; +} + +async function createCategory(input) { + const mutation = ` + mutation CreateCategory($data: CategoryInput!) { + createCategory(data: $data) { + documentId + slug + title + group + } + } + `; + + const data = await gql(mutation, { data: input }); + return data?.createCategory; +} + +async function seedCategories(slugs) { + let createdCount = 0; + let skippedCount = 0; + + for (const slug of slugs) { + const source = readCategorySource(slug); + const { title, description = "", group } = source; + + if (group !== "solution" && group !== "service") { + throw new Error( + `Category ${slug} has invalid group "${String(group)}". Expected "solution" or "service".` + ); + } + + if (await categoryExists(slug)) { + skippedCount += 1; + console.log(`SKIP ${slug} (already exists)`); + continue; + } + + const created = await createCategory({ + slug, + title, + description, + group, + }); + + createdCount += 1; + console.log( + `CREATE ${created.slug} -> ${created.title} [group=${created.group}]` + ); + } + + return { createdCount, skippedCount }; +} + +async function main() { + console.log(`Seeding categories to ${endpoint}`); + if (!token) { + console.warn( + "No STRAPI token provided. If endpoint is protected, set STRAPI_TOKEN." + ); + } + + const slugs = discoverCategorySlugs(); + const { createdCount, skippedCount } = await seedCategories(slugs); + + console.log( + `Done. created=${createdCount}, skipped=${skippedCount}, total=${createdCount + skippedCount}` + ); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/src/content/categories/ai-solutions.json b/src/content/categories/ai-solutions.json index b6030a4..adfc238 100644 --- a/src/content/categories/ai-solutions.json +++ b/src/content/categories/ai-solutions.json @@ -1,5 +1,6 @@ { "title": "Решения на базе ИИ", + "group": "solution", "description": "Передовые технологии искусственного интеллекта и машинного обучения для автоматизации бизнес-процессов", "vendors": [ { diff --git a/src/content/categories/business.json b/src/content/categories/business.json index 4f100ce..704c608 100644 --- a/src/content/categories/business.json +++ b/src/content/categories/business.json @@ -1,5 +1,6 @@ { "title": "Решения для бизнеса", + "group": "solution", "description": "Комплексные IT-решения для автоматизации бизнес-процессов, CRM, ERP и управления предприятием", "vendors": [ { diff --git a/src/content/categories/cad-gis.json b/src/content/categories/cad-gis.json index a45f76e..cc4d785 100644 --- a/src/content/categories/cad-gis.json +++ b/src/content/categories/cad-gis.json @@ -1,5 +1,6 @@ { "title": "САПР и ГИС", + "group": "solution", "description": "Системы автоматизированного проектирования и геоинформационные системы для инженеров, архитекторов и проектировщиков", "vendors": [ { diff --git a/src/content/categories/cloud.json b/src/content/categories/cloud.json index d86c1a0..24b9af9 100644 --- a/src/content/categories/cloud.json +++ b/src/content/categories/cloud.json @@ -1,5 +1,6 @@ { "title": "Облачные решения", + "group": "solution", "description": "Российские облачные платформы для размещения инфраструктуры, приложений и данных с соблюдением требований законодательства РФ", "vendors": [ { diff --git a/src/content/categories/consulting.json b/src/content/categories/consulting.json index 2fb07af..8b5ae19 100644 --- a/src/content/categories/consulting.json +++ b/src/content/categories/consulting.json @@ -1,5 +1,6 @@ { "title": "Консалтинг и проектирование", + "group": "service", "description": "Экспертный консалтинг по построению и трансформации IT-инфраструктуры. Помогаем разработать стратегию цифровизации, выбрать оптимальные технологии и спланировать миграцию на российские решения.", "vendors": [ { diff --git a/src/content/categories/cybersecurity.json b/src/content/categories/cybersecurity.json index 1b0ddfc..a3bd49d 100644 --- a/src/content/categories/cybersecurity.json +++ b/src/content/categories/cybersecurity.json @@ -1,5 +1,6 @@ { "title": "Кибербезопасность", + "group": "solution", "description": "Комплексные решения для защиты корпоративной инфраструктуры, данных и приложений от современных киберугроз", "vendors": [ { diff --git a/src/content/categories/government.json b/src/content/categories/government.json index a1f1b17..82031f9 100644 --- a/src/content/categories/government.json +++ b/src/content/categories/government.json @@ -1,5 +1,6 @@ { "title": "Для госсектора", + "group": "solution", "description": "Специализированные решения для государственных органов с соблюдением требований ФСТЭК и ФСБ", "vendors": [ { diff --git a/src/content/categories/hardware-solutions.json b/src/content/categories/hardware-solutions.json index ec022b4..3c1e8e5 100644 --- a/src/content/categories/hardware-solutions.json +++ b/src/content/categories/hardware-solutions.json @@ -1,5 +1,6 @@ { "title": "Аппаратные решения", + "group": "solution", "description": "Комплексные аппаратные решения для построения IT-инфраструктуры: СХД, сетевое оборудование, компоненты", "vendors": [ { diff --git a/src/content/categories/implementation.json b/src/content/categories/implementation.json index e638ced..452b10a 100644 --- a/src/content/categories/implementation.json +++ b/src/content/categories/implementation.json @@ -1,5 +1,6 @@ { "title": "Поставка и внедрение решений", + "group": "service", "description": "Полный цикл поставки и внедрения IT-решений: от подбора оборудования и ПО до установки, настройки и ввода в эксплуатацию. Работаем только с сертифицированными российскими решениями.", "vendors": [ { diff --git a/src/content/categories/industrial-solutions.json b/src/content/categories/industrial-solutions.json index df29de4..d5fcbbe 100644 --- a/src/content/categories/industrial-solutions.json +++ b/src/content/categories/industrial-solutions.json @@ -1,5 +1,6 @@ { "title": "Индустриальные решения", + "group": "solution", "description": "Системы промышленной автоматизации, SCADA, MES и решения для управления технологическими процессами", "vendors": [ { diff --git a/src/content/categories/integration.json b/src/content/categories/integration.json index 3d7c28d..9097744 100644 --- a/src/content/categories/integration.json +++ b/src/content/categories/integration.json @@ -1,5 +1,6 @@ { "title": "Интеграция и разработка", + "group": "service", "description": "Разрабатываем и интегрируем ПО любой сложности. От интеграции корпоративных систем до создания уникальных решений с нуля на современных технологических стеках.", "vendors": [ { diff --git a/src/content/categories/managed-services.json b/src/content/categories/managed-services.json index 1c75eea..8552d61 100644 --- a/src/content/categories/managed-services.json +++ b/src/content/categories/managed-services.json @@ -1,5 +1,6 @@ { "title": "Управляемые сервисы", + "group": "service", "description": "Передайте управление IT-инфраструктурой профессионалам. Обеспечиваем круглосуточный мониторинг, поддержку и развитие ваших IT-систем с гарантией SLA.", "vendors": [ { diff --git a/src/content/categories/russian-hardware.json b/src/content/categories/russian-hardware.json index 4806373..e3287e4 100644 --- a/src/content/categories/russian-hardware.json +++ b/src/content/categories/russian-hardware.json @@ -1,5 +1,6 @@ { "title": "Российские ноутбуки, ПК и серверы", + "group": "solution", "description": "Отечественное компьютерное и серверное оборудование для корпоративного сектора и государственных организаций", "vendors": [ { diff --git a/src/content/categories/russian-software.json b/src/content/categories/russian-software.json index 305ff88..e910e3b 100644 --- a/src/content/categories/russian-software.json +++ b/src/content/categories/russian-software.json @@ -1,5 +1,6 @@ { "title": "Российское ПО", + "group": "solution", "description": "Комплексные программные решения от российских разработчиков для бизнеса, образования и государственного сектора", "vendors": [ { diff --git a/src/content/categories/specialized-services.json b/src/content/categories/specialized-services.json index 35bedc6..d3da97e 100644 --- a/src/content/categories/specialized-services.json +++ b/src/content/categories/specialized-services.json @@ -1,5 +1,6 @@ { "title": "Специализированные услуги по отраслям", + "group": "service", "description": "Отраслевая экспертиза в финансах, промышленности, здравоохранении, образовании, ритейле и телекоме. Понимаем специфику бизнеса и требования регуляторов.", "vendors": [ { diff --git a/src/content/categories/subscription.json b/src/content/categories/subscription.json index ba74be3..08adea5 100644 --- a/src/content/categories/subscription.json +++ b/src/content/categories/subscription.json @@ -1,5 +1,6 @@ { "title": "Услуги по подписке", + "group": "service", "description": "Современная модель потребления IT-услуг. Платите только за использованные ресурсы, получайте актуальные обновления и профессиональную поддержку без капитальных затрат.", "vendors": [ { diff --git a/src/content/categories/support.json b/src/content/categories/support.json index e3bd048..686edaf 100644 --- a/src/content/categories/support.json +++ b/src/content/categories/support.json @@ -1,5 +1,6 @@ { "title": "Обслуживание и сопровождение", + "group": "service", "description": "Надежная техническая поддержка и обслуживание IT-инфраструктуры. Гарантируем стабильную работу всех систем с минимальным временем простоя.", "vendors": [ {