diff --git a/layouts/packages.vue b/layouts/packages.vue index e6828dab..64c8238f 100644 --- a/layouts/packages.vue +++ b/layouts/packages.vue @@ -3,7 +3,7 @@ import type { Package } from '~/types/package' const { page } = useContent() -const { data: packages } = await useAsyncData('content:packages', () => queryContent('/packages/').only(['_path', 'title', 'description']).find()) +const { data: packages } = await useFetch('/api/packages') if (!packages.value) { throw createError({ @@ -34,6 +34,14 @@ const orderByOptions = [ id: 'title', label: 'Name', }, + { + id: 'stars', + label: 'Stars', + }, + { + id: 'monthlyDownloads', + label: 'Downloads', + }, ] const orderBy = ref('title') const currentOrderBy = computed(() => orderByOptions.find(option => option.id === orderBy.value)) @@ -45,13 +53,13 @@ const results = computed(() => { return currentPackages return currentPackages.sort((a, b) => { - const aTitle = a.title.toLowerCase() - const bTitle = b.title.toLowerCase() + const aValue = a[orderBy.value] + const bValue = b[orderBy.value] - if (aTitle < bTitle) + if (aValue < bValue) return -1 * order.value - if (aTitle > bTitle) + if (aValue > bValue) return 1 * order.value return 0 @@ -106,7 +114,7 @@ const results = computed(() => {
  1. - + + +
    + +

    + + + {{ item.title }} + +

    +
    + +

    + {{ item.description }} +

    + +
    +
    +
    + + Stars + + +
    +
    + {{ formatNumber(item.stars) }} +
    +
    + +
diff --git a/nuxt.config.ts b/nuxt.config.ts index 9ac536b9..e2994ef1 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -52,6 +52,11 @@ export default defineNuxtConfig({ maxAge: 60 * 60 * 24 * 7, // 7 days }, }, + '/api/packages': { + cache: { + maxAge: 60 * 60 * 24 * 7, // 7 days + }, + }, '/blog/2023-08-25-nitro-2.6': { redirect: { statusCode: 301, diff --git a/server/api/github/[owner]/[repo]/metadata.ts b/server/api/github/[owner]/[repo]/metadata.ts index 1d0c20a1..a520b058 100644 --- a/server/api/github/[owner]/[repo]/metadata.ts +++ b/server/api/github/[owner]/[repo]/metadata.ts @@ -1,5 +1,3 @@ -import { FetchError } from 'ofetch' - export default defineEventHandler(async (event) => { const owner = getRouterParam(event, 'owner') const repo = getRouterParam(event, 'repo') @@ -21,32 +19,3 @@ export default defineEventHandler(async (event) => { latestRelease, } }) - -async function fetchStars(owner: string, repo: string): Promise { - const repos = await $fetch<{ repo: { stars: number } }>(`https://ungh.cc/repos/${owner}/${repo}`) - - return repos.repo.stars -} - -// TODO: use it later -// async function fetchContributors(owner: string, repo: string): Promise<{ id: number; username: string }[]> { -// const { contributors } = await $fetch<{ contributors: { id: number; username: string; contributions: number }[] }>(`https://ungh.cc/repos/${owner}/${repo}/contributors`) - -// const filteredContributors = contributors.filter(({ username }) => !username.includes('[bot]')) -// const sortedContributors = filteredContributors.sort((a, b) => b.contributions - a.contributions) - -// return sortedContributors.map(({ id, username }) => ({ id, username })) -// } - -async function fetchLatestRelease(owner: string, repo: string): Promise { - try { - const releases = await $fetch<{ release: { tag: string } }>(`https://ungh.cc/repos/${owner}/${repo}/releases/latest`) - return releases.release.tag - } - catch (error: unknown) { - if (error instanceof FetchError && error.status === 404) - return null - - throw error - } -} diff --git a/server/api/npm/[name]/monthly-downloads.ts b/server/api/npm/[name]/monthly-downloads.ts index fe370977..67d33b44 100644 --- a/server/api/npm/[name]/monthly-downloads.ts +++ b/server/api/npm/[name]/monthly-downloads.ts @@ -12,9 +12,3 @@ export default defineEventHandler(async (event) => { return monthlyDownloads }) - -async function fetchMonthlyDownloads(name: string): Promise { - const downloads = await $fetch<{ downloads: number }>(`https://api.npmjs.org/downloads/point/last-month/${name}`) - - return downloads.downloads -} diff --git a/server/api/packages.ts b/server/api/packages.ts new file mode 100644 index 00000000..55e7032d --- /dev/null +++ b/server/api/packages.ts @@ -0,0 +1,25 @@ +import type { ParsedContent } from '@nuxt/content/dist/runtime/types' +import { serverQueryContent } from '#content/server' + +export default defineEventHandler(async (event) => { + const contentPackages = await serverQueryContent(event).where({ _path: { $regex: /^\/packages\// } }).only(['_path', 'title', 'description', 'github', 'npm']).find() + + const populatedPackages = await Promise.all(contentPackages.map(fetchPackageMetadata)) + + return populatedPackages +}) + +async function fetchPackageMetadata(contentPackage: Pick) { + const [repo, monthlyDownloads] = await Promise.all([ + fetchRepo(contentPackage.github.owner, contentPackage.github.repo), + contentPackage.npm ? fetchMonthlyDownloads(contentPackage.npm.name) : null, + ]) + + return { + ...contentPackage, + stars: repo.stars, + monthlyDownloads, + createdAt: repo.createdAt, + updatedAt: repo.updatedAt, + } +} diff --git a/server/utils/github.ts b/server/utils/github.ts new file mode 100644 index 00000000..bbabfeab --- /dev/null +++ b/server/utils/github.ts @@ -0,0 +1,36 @@ +import { FetchError } from 'ofetch' + +export async function fetchRepo(owner: string, repo: string): Promise<{ stars: number; createdAt: string; updatedAt: string }> { + const repos = await $fetch<{ repo: { stars: number; createdAt: string; updatedAt: string } }>(`https://ungh.cc/repos/${owner}/${repo}`) + + return repos.repo +} + +export async function fetchStars(owner: string, repo: string): Promise { + const repos = await fetchRepo(owner, repo) + + return repos.stars +} + +export async function fetchLatestRelease(owner: string, repo: string): Promise { + try { + const releases = await $fetch<{ release: { tag: string } }>(`https://ungh.cc/repos/${owner}/${repo}/releases/latest`) + return releases.release.tag + } + catch (error: unknown) { + if (error instanceof FetchError && error.status === 404) + return null + + throw error + } +} + +// TODO: use it later +// async function fetchContributors(owner: string, repo: string): Promise<{ id: number; username: string }[]> { +// const { contributors } = await $fetch<{ contributors: { id: number; username: string; contributions: number }[] }>(`https://ungh.cc/repos/${owner}/${repo}/contributors`) + +// const filteredContributors = contributors.filter(({ username }) => !username.includes('[bot]')) +// const sortedContributors = filteredContributors.sort((a, b) => b.contributions - a.contributions) + +// return sortedContributors.map(({ id, username }) => ({ id, username })) +// } diff --git a/server/utils/npm.ts b/server/utils/npm.ts new file mode 100644 index 00000000..5c2600f9 --- /dev/null +++ b/server/utils/npm.ts @@ -0,0 +1,5 @@ +export async function fetchMonthlyDownloads(name: string): Promise { + const downloads = await $fetch<{ downloads: number }>(`https://api.npmjs.org/downloads/point/last-month/${name}`) + + return downloads.downloads +}