From 447d8c797210cb75f8b90d95dc3b1ee55d5a4176 Mon Sep 17 00:00:00 2001 From: Zakaria Mansouri Date: Tue, 17 Dec 2024 22:15:30 +0100 Subject: [PATCH] sitemap for contributors pages --- api/src/app/endpoints.ts | 4 ++ api/src/contributor/controller.ts | 10 ++++ api/src/contributor/repository.ts | 15 ++++++ api/src/contributor/types.ts | 4 ++ api/src/project/repository.ts | 2 +- .../functions/w/contributors-sitemap.xml.ts | 49 +++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 web/cloudflare/functions/w/contributors-sitemap.xml.ts diff --git a/api/src/app/endpoints.ts b/api/src/app/endpoints.ts index 59098ee6a..f1aa3db02 100644 --- a/api/src/app/endpoints.ts +++ b/api/src/app/endpoints.ts @@ -2,6 +2,7 @@ import { GetContributionsResponse } from "src/contribution/types"; import { GetContributorNameResponse, GetContributorResponse, + GetContributorsForSitemapResponse, GetContributorsResponse, } from "src/contributor/types"; import { GetMilestonesResponse } from "src/milestone/types"; @@ -35,6 +36,9 @@ export interface Endpoints { "api:Contributors": { response: GetContributorsResponse; }; + "api:contributors/for-sitemap": { + response: GetContributorsForSitemapResponse; + }; "api:Contributors/:id": { response: GetContributorResponse; params: { id: string }; diff --git a/api/src/contributor/controller.ts b/api/src/contributor/controller.ts index ba984c694..9416b0fd9 100644 --- a/api/src/contributor/controller.ts +++ b/api/src/contributor/controller.ts @@ -5,6 +5,7 @@ import { ContributorRepository } from "./repository"; import { GetContributorNameResponse, GetContributorResponse, + GetContributorsForSitemapResponse, GetContributorsResponse, } from "./types"; import { ProjectRepository } from "src/project/repository"; @@ -28,6 +29,15 @@ export class ContributorController { }; } + @Get("/for-sitemap") + public async getContributorsForSitemap(): Promise { + const contributors = await this.contributorRepository.findForSitemap(); + + return { + contributors, + }; + } + @Get("/:id") public async getContributor(@Param("id") id: string): Promise { const [contributor, projects, contributions] = await Promise.all([ diff --git a/api/src/contributor/repository.ts b/api/src/contributor/repository.ts index 803d2e7d4..619f2694b 100644 --- a/api/src/contributor/repository.ts +++ b/api/src/contributor/repository.ts @@ -94,6 +94,21 @@ export class ContributorRepository { return camelCased; } + public async findForSitemap() { + const statement = sql` + SELECT + ${contributorsTable.id} + FROM + ${contributorsTable} + `; + + const raw = await this.postgresService.db.execute(statement); + const entries = Array.from(raw); + const unStringifiedRaw = unStringifyDeep(entries); + const camelCased = camelCaseObject(unStringifiedRaw); + return camelCased; + } + public async upsert(contributor: ContributorRow) { return await this.postgresService.db .insert(contributorsTable) diff --git a/api/src/contributor/types.ts b/api/src/contributor/types.ts index 02c3d5711..09aac0425 100644 --- a/api/src/contributor/types.ts +++ b/api/src/contributor/types.ts @@ -3,6 +3,10 @@ import { ContributorEntity } from "@dzcode.io/models/dist/contributor"; import { ProjectEntity } from "@dzcode.io/models/dist/project"; import { GeneralResponse } from "src/app/types"; +export interface GetContributorsForSitemapResponse extends GeneralResponse { + contributors: Array>; +} + export interface GetContributorsResponse extends GeneralResponse { contributors: Array< Pick & { diff --git a/api/src/project/repository.ts b/api/src/project/repository.ts index ec11dfbeb..f99f43e6f 100644 --- a/api/src/project/repository.ts +++ b/api/src/project/repository.ts @@ -160,7 +160,7 @@ export class ProjectRepository { public async findForSitemap() { const statement = sql` SELECT - id, + ${projectsTable.id}, ROUND( 100 * sum(repo_with_stats.contributor_count) + 100 * sum(repo_with_stats.stars) + max(repo_with_stats.score) - sum(repo_with_stats.score) / sum(repo_with_stats.contributor_count) )::int as ranking FROM ( diff --git a/web/cloudflare/functions/w/contributors-sitemap.xml.ts b/web/cloudflare/functions/w/contributors-sitemap.xml.ts new file mode 100644 index 000000000..daf9e7420 --- /dev/null +++ b/web/cloudflare/functions/w/contributors-sitemap.xml.ts @@ -0,0 +1,49 @@ +import { Env } from "handler/contributor"; +import { environments } from "@dzcode.io/utils/dist/config/environment"; +import { allLanguages, LanguageEntity } from "@dzcode.io/models/dist/language"; +import { getContributorURL } from "@dzcode.io/web/dist/utils/contributor"; +import { fsConfig } from "@dzcode.io/utils/dist/config"; +import { fetchV2Factory } from "@dzcode.io/utils/dist/fetch/factory"; +import { Endpoints } from "@dzcode.io/api/dist/app/endpoints"; + +export const onRequest: PagesFunction = async (context) => { + let stage = context.env.STAGE; + if (!environments.includes(stage)) { + console.log(`⚠️ No STAGE provided, falling back to "development"`); + stage = "development"; + } + const fullstackConfig = fsConfig(stage); + const fetchV2 = fetchV2Factory(fullstackConfig); + + const { contributors } = await fetchV2("api:contributors/for-sitemap", {}); + + const hostname = "https://www.dzCode.io"; + const links = contributors.reduce<{ url: string; lang: LanguageEntity["code"] }[]>((pV, cV) => { + return [ + ...pV, + ...allLanguages.map(({ baseUrl, code }) => ({ + url: `${baseUrl}${getContributorURL(cV)}`, + lang: code, + })), + ]; + }, []); + + const xml = ` + + ${links + .map( + (link) => ` + + ${hostname}${link.url} + + `, + ) + .join("")} +`; + + return new Response(xml, { headers: { "content-type": "application/xml; charset=utf-8" } }); +};