Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions apps/cms/src/api/faq-item/content-types/faq-item/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"kind": "collectionType",
"collectionName": "faq_items",
"info": {
"singularName": "faq-item",
"pluralName": "faq-items",
"displayName": "FAQ Items"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"question": {
"type": "string",
"required": true
},
"answer": {
"type": "richtext",
"required": true
}
}
}
3 changes: 3 additions & 0 deletions apps/cms/src/api/faq-item/controllers/faq-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { factories } from "@strapi/strapi";

export default factories.createCoreController("api::faq-item.faq-item");
3 changes: 3 additions & 0 deletions apps/cms/src/api/faq-item/routes/faq-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { factories } from "@strapi/strapi";

export default factories.createCoreRouter("api::faq-item.faq-item");
3 changes: 3 additions & 0 deletions apps/cms/src/api/faq-item/services/faq-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { factories } from "@strapi/strapi";

export default factories.createCoreService("api::faq-item.faq-item");
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
"type": "component",
"component": "shared.labels",
"repeatable": true
},
"faq_items_section": {
"type": "component",
"component": "sections.faq-items",
"repeatable": false
}
}
}
17 changes: 17 additions & 0 deletions apps/cms/src/components/sections/faq-items.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"collectionName": "components_sections_faq_items",
"info": {
"displayName": "FAQ Items"
},
"options": {},
"attributes": {
"title": {
"type": "string"
},
"items": {
"type": "relation",
"relation": "oneToMany",
"target": "api::faq-item.faq-item"
}
}
}
80 changes: 73 additions & 7 deletions apps/cms/src/migration/integrations.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,98 @@
import { createCategories, uploadFromUrl, uploadMarkdownImages } from "./utils";

const FAQ_POPULATE = "populate[slices][populate][categories][populate]=*";

const extractFaqQuestions = (slices) => {
if (!Array.isArray(slices)) return [];
return slices
.filter((s) => s.__component === "slices.faq")
.flatMap((slice) =>
(slice.categories || []).flatMap((cat) =>
(cat.questions || []).filter((q) => q.question && q.answer),
),
);
};

const getFaqSectionTitle = (slices) => {
if (!Array.isArray(slices)) return "Frequently Asked Questions";
const faqSlice = slices.find((s) => s.__component === "slices.faq");
return faqSlice?.categories?.[0]?.name || "Frequently Asked Questions";
};

const createFaqItems = async (slices) => {
const questions = extractFaqQuestions(slices);
const documentIds: string[] = [];
for (const q of questions) {
const item = await strapi.documents("api::faq-item.faq-item").create({
data: { question: q.question, answer: q.answer },
});
documentIds.push(item.documentId);
}
return documentIds;
};

export const migrateIntegrations = async () => {
strapi.log.info("Starting integrations migration...");
let migrated = 0;
let updated = 0;
let skipped = 0;
let failed = 0;

const firstPage = (await fetch(
"https://api-prod.strapi.io/api/integrations?pagination[pageSize]=100",
`https://api-prod.strapi.io/api/integrations?pagination[pageSize]=100&${FAQ_POPULATE}`,
).then((res) => res.json())) as any;

const allIntegrations = [...firstPage.data];

for (let page = 2; page <= firstPage.meta.pagination.pageCount; page++) {
const nextPage = (await fetch(
`https://api-prod.strapi.io/api/integrations?pagination[pageSize]=100&pagination[page]=${page}`,
`https://api-prod.strapi.io/api/integrations?pagination[pageSize]=100&pagination[page]=${page}&${FAQ_POPULATE}`,
).then((res) => res.json())) as any;
allIntegrations.push(...nextPage.data);
}

for (const integration of allIntegrations) {
try {
let icon = null;
let image = null;

const existing = await strapi
.documents("api::integration.integration")
.findFirst({
filters: { slug: integration.attributes.slug },
populate: { faq_items_section: { populate: { items: true } } },
});

const slices = integration.attributes.slices || [];

if (existing) {
skipped++;
// Already migrated — update FAQ items if missing
const hasFaqItems = existing.faq_items_section?.items?.length > 0;

if (hasFaqItems) {
skipped++;
continue;
}

const faqItemIds = await createFaqItems(slices);
if (faqItemIds.length > 0) {
await strapi.documents("api::integration.integration").update({
documentId: existing.documentId,
status: "published",
data: {
faq_items_section: {
title: getFaqSectionTitle(slices),
items: faqItemIds,
},
},
});
updated++;
} else {
skipped++;
}
continue;
}

let icon = null;
let image = null;

if (integration.attributes.logo?.media?.data?.attributes?.url) {
icon = await uploadFromUrl(
integration.attributes.logo.media.data.attributes.url,
Expand All @@ -58,6 +116,8 @@ export const migrateIntegrations = async () => {
integration.attributes.content || "",
);

const faqItemIds = await createFaqItems(slices);

await strapi.documents("api::integration.integration").create({
status: "published",
data: {
Expand All @@ -78,6 +138,12 @@ export const migrateIntegrations = async () => {
keywords: integration.attributes.seo?.keywords,
metaImage: image?.id,
},
...(faqItemIds.length > 0 && {
faq_items_section: {
title: getFaqSectionTitle(slices),
items: faqItemIds,
},
}),
},
});
migrated++;
Expand All @@ -91,6 +157,6 @@ export const migrateIntegrations = async () => {
}

strapi.log.info(
`Integrations migration finished. Migrated: ${migrated}, Skipped: ${skipped}, Failed: ${failed}`,
`Integrations migration finished. Migrated: ${migrated}, Updated (FAQ): ${updated}, Skipped: ${skipped}, Failed: ${failed}`,
);
};
22 changes: 16 additions & 6 deletions apps/cms/src/plugins/package-info/server/src/cron/sync-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ export async function syncStats(): Promise<{
const [packages, templates] = await Promise.all([
strapi.documents("api::package.package").findMany({
status: "published",
fields: ["documentId", "package_location", "git_repository"],
fields: [
"documentId",
"package_location",
"git_repository",
"stars",
"monthly_downloads",
],
pagination: { pageSize: 500 },
}),
strapi.documents("api::template.template").findMany({
status: "published",
fields: ["documentId", "git_repository"],
fields: ["documentId", "git_repository", "stars", "readme"],
pagination: { pageSize: 500 },
}),
]);
Expand All @@ -33,8 +39,12 @@ export async function syncStats(): Promise<{
if (!info) continue;

const patch: Record<string, unknown> = {};
if (info.stars !== null) patch.stars = info.stars;
if (info.downloads?.monthly !== null)
if (info.stars !== null && info.stars !== pkg.stars)
patch.stars = info.stars;
if (
info.downloads?.monthly !== null &&
info.downloads?.monthly !== pkg.monthly_downloads
)
patch.monthly_downloads = info.downloads?.monthly ?? null;

if (Object.keys(patch).length === 0) continue;
Expand Down Expand Up @@ -64,8 +74,8 @@ export async function syncStats(): Promise<{
]);

const patch: Record<string, unknown> = {};
if (stars !== null) patch.stars = stars;
if (readme !== null) patch.readme = readme;
if (stars !== null && stars !== template.stars) patch.stars = stars;
if (readme !== null && readme !== template.readme) patch.readme = readme;

if (Object.keys(patch).length === 0) continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export async function syncVersionInfo(): Promise<{
}> {
const packages = await strapi.documents("api::package.package").findMany({
status: "published",
fields: ["documentId", "package_location", "git_repository"],
fields: ["documentId", "package_location", "git_repository", "readme"],
populate: { version_info: true },
pagination: { pageSize: 500 },
});

Expand All @@ -23,15 +24,28 @@ export async function syncVersionInfo(): Promise<{
);
if (!info) continue;

const newReadme = info.readme ?? null;
const newVersion = info.version ?? null;
const newPublishedAt = info.publishedAt ?? null;
const newInstallCommand = info.installCommand ?? null;

if (
newReadme === pkg.readme &&
newVersion === (pkg.version_info?.version ?? null) &&
newPublishedAt === (pkg.version_info?.published_at ?? null) &&
newInstallCommand === (pkg.version_info?.install_command ?? null)
)
continue;

await strapi.documents("api::package.package").update({
status: "published",
documentId: pkg.documentId,
data: {
readme: info.readme ?? null,
readme: newReadme,
version_info: {
version: info.version ?? null,
published_at: info.publishedAt ?? null,
install_command: info.installCommand ?? null,
version: newVersion,
published_at: newPublishedAt,
install_command: newInstallCommand,
},
},
});
Expand Down
12 changes: 12 additions & 0 deletions apps/cms/types/generated/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ export interface SectionsCta extends Struct.ComponentSchema {
};
}

export interface SectionsFaqItems extends Struct.ComponentSchema {
collectionName: 'components_sections_faq_items';
info: {
displayName: 'FAQ Items';
};
attributes: {
items: Schema.Attribute.Relation<'oneToMany', 'api::faq-item.faq-item'>;
title: Schema.Attribute.String;
};
}

export interface SectionsHighlights extends Struct.ComponentSchema {
collectionName: 'components_sections_highlights';
info: {
Expand Down Expand Up @@ -215,6 +226,7 @@ declare module '@strapi/strapi' {
'sections.card-grid': SectionsCardGrid;
'sections.card-grid-item': SectionsCardGridItem;
'sections.cta': SectionsCta;
'sections.faq-items': SectionsFaqItems;
'sections.highlights': SectionsHighlights;
'sections.search': SectionsSearch;
'shared.button': SharedButton;
Expand Down
31 changes: 31 additions & 0 deletions apps/cms/types/generated/contentTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,35 @@ export interface ApiEmailTemplateEmailTemplate
};
}

export interface ApiFaqItemFaqItem extends Struct.CollectionTypeSchema {
collectionName: 'faq_items';
info: {
displayName: 'FAQ Items';
pluralName: 'faq-items';
singularName: 'faq-item';
};
options: {
draftAndPublish: false;
};
attributes: {
answer: Schema.Attribute.RichText & Schema.Attribute.Required;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::faq-item.faq-item'
> &
Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime;
question: Schema.Attribute.String & Schema.Attribute.Required;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
};
}

export interface ApiHelpPageHelpPage extends Struct.CollectionTypeSchema {
collectionName: 'help_pages';
info: {
Expand Down Expand Up @@ -714,6 +743,7 @@ export interface ApiIntegrationIntegration extends Struct.CollectionTypeSchema {
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
description: Schema.Attribute.Text & Schema.Attribute.Required;
faq_items_section: Schema.Attribute.Component<'sections.faq-items', false>;
image: Schema.Attribute.Media<'images'>;
labels: Schema.Attribute.Component<'shared.labels', true>;
locale: Schema.Attribute.String & Schema.Attribute.Private;
Expand Down Expand Up @@ -2554,6 +2584,7 @@ declare module '@strapi/strapi' {
'api::country.country': ApiCountryCountry;
'api::cta.cta': ApiCtaCta;
'api::email-template.email-template': ApiEmailTemplateEmailTemplate;
'api::faq-item.faq-item': ApiFaqItemFaqItem;
'api::help-page.help-page': ApiHelpPageHelpPage;
'api::home.home': ApiHomeHome;
'api::integration-category.integration-category': ApiIntegrationCategoryIntegrationCategory;
Expand Down
Loading
Loading