Release 202507031117
This commit is contained in:
@@ -275,7 +275,7 @@ CREATE OR REPLACE FUNCTION handle_suggestion_status_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
service_name TEXT;
|
||||
service_visibility "ServiceVisibility";
|
||||
service_visibility "serviceVisibility";
|
||||
is_user_admin_or_moderator BOOLEAN;
|
||||
BEGIN
|
||||
-- Award karma for first approval
|
||||
@@ -283,7 +283,7 @@ BEGIN
|
||||
-- and ensure it wasn't already APPROVED.
|
||||
IF OLD.status IS DISTINCT FROM 'APPROVED' AND NEW.status = 'APPROVED' THEN
|
||||
-- Fetch service details for the description
|
||||
SELECT name, visibility INTO service_name, service_visibility FROM "Service" WHERE id = NEW."serviceId";
|
||||
SELECT name, serviceVisibility INTO service_name, service_visibility FROM "Service" WHERE id = NEW."serviceId";
|
||||
|
||||
-- Only award karma if the service is public
|
||||
IF service_visibility = 'PUBLIC' THEN
|
||||
|
||||
@@ -7,186 +7,184 @@ import { makeSearchFiltersOptions } from '../../lib/searchFiltersOptions'
|
||||
import type { APIRoute } from 'astro'
|
||||
|
||||
export const GET: APIRoute = async ({ site }) => {
|
||||
if (!site) {
|
||||
return new Response('Site URL not configured', { status: 500 })
|
||||
}
|
||||
if (!site) return new Response('Site URL not configured', { status: 500 })
|
||||
|
||||
const searchUrls = await generateSEOSitemapUrls(site.href)
|
||||
try {
|
||||
const searchUrls = await generateSEOSitemapUrls(site.href)
|
||||
|
||||
const result = `
|
||||
const result = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${searchUrls.map((url) => `<url><loc>${he.encode(url)}</loc></url>`).join('\n')}
|
||||
</urlset>
|
||||
`.trim()
|
||||
|
||||
return new Response(result, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
})
|
||||
return new Response(result, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to generate SEO sitemap URLs:', error)
|
||||
return new Response('Failed to generate SEO sitemap URLs', { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
async function generateSEOSitemapUrls(siteUrl: string) {
|
||||
try {
|
||||
const [categories, attributes] = await Promise.all([
|
||||
prisma.category.findMany({
|
||||
select: {
|
||||
name: true,
|
||||
namePluralLong: true,
|
||||
slug: true,
|
||||
icon: true,
|
||||
_count: {
|
||||
select: {
|
||||
services: {
|
||||
where: {
|
||||
const [categories, attributes] = await Promise.all([
|
||||
prisma.category.findMany({
|
||||
select: {
|
||||
name: true,
|
||||
namePluralLong: true,
|
||||
slug: true,
|
||||
icon: true,
|
||||
_count: {
|
||||
select: {
|
||||
services: {
|
||||
where: {
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.attribute.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
category: true,
|
||||
type: true,
|
||||
_count: {
|
||||
select: {
|
||||
services: {
|
||||
where: {
|
||||
service: {
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.attribute.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
category: true,
|
||||
type: true,
|
||||
_count: {
|
||||
select: {
|
||||
services: {
|
||||
where: {
|
||||
service: {
|
||||
serviceVisibility: { in: ['PUBLIC', 'ARCHIVED'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: [{ category: 'asc' }, { type: 'asc' }, { title: 'asc' }],
|
||||
}),
|
||||
])
|
||||
},
|
||||
orderBy: [{ category: 'asc' }, { type: 'asc' }, { title: 'asc' }],
|
||||
}),
|
||||
])
|
||||
|
||||
const filtersOptions = makeSearchFiltersOptions({
|
||||
filters: null,
|
||||
categories,
|
||||
attributes,
|
||||
})
|
||||
const filtersOptions = makeSearchFiltersOptions({
|
||||
filters: null,
|
||||
categories,
|
||||
attributes,
|
||||
})
|
||||
|
||||
const byCategory = filtersOptions.categories.map(
|
||||
(category) =>
|
||||
const byCategory = filtersOptions.categories.map(
|
||||
(category) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
})
|
||||
)
|
||||
|
||||
const byVerificationStatus = filtersOptions.verification.map(
|
||||
(status) =>
|
||||
new URLSearchParams({
|
||||
verification: status.slug,
|
||||
})
|
||||
)
|
||||
|
||||
const byKycLevel = filtersOptions.kycLevels.map(
|
||||
(level) =>
|
||||
new URLSearchParams({
|
||||
'max-kyc': level.id,
|
||||
})
|
||||
)
|
||||
|
||||
const byCurrency = filtersOptions.currencies.map(
|
||||
(currency) =>
|
||||
new URLSearchParams({
|
||||
currencies: currency.slug,
|
||||
})
|
||||
)
|
||||
|
||||
const withOneAttribute = filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.map(
|
||||
(attribute) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
[`attr-${attribute.id.toString()}`]: 'yes',
|
||||
})
|
||||
)
|
||||
const withoutOneAttribute = filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.map(
|
||||
(attribute) =>
|
||||
new URLSearchParams({
|
||||
[`attr-${attribute.id.toString()}`]: 'no',
|
||||
})
|
||||
)
|
||||
|
||||
const byVerificationStatus = filtersOptions.verification.map(
|
||||
(status) =>
|
||||
new URLSearchParams({
|
||||
verification: status.slug,
|
||||
})
|
||||
)
|
||||
|
||||
const byKycLevel = filtersOptions.kycLevels.map(
|
||||
(level) =>
|
||||
new URLSearchParams({
|
||||
'max-kyc': level.id,
|
||||
})
|
||||
)
|
||||
|
||||
const byCurrency = filtersOptions.currencies.map(
|
||||
const byCategoryAndCurrency = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.currencies.map(
|
||||
(currency) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
currencies: currency.slug,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const withOneAttribute = filtersOptions.attributesByCategory
|
||||
const byCategoryAndAttributes = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.map(
|
||||
(attribute) =>
|
||||
new URLSearchParams({
|
||||
[`attr-${attribute.id.toString()}`]: 'yes',
|
||||
})
|
||||
)
|
||||
const withoutOneAttribute = filtersOptions.attributesByCategory
|
||||
.flatMap((attribute) => [
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
[`attr-${attribute.id.toString()}`]: 'yes',
|
||||
}),
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
[`attr-${attribute.id.toString()}`]: 'no',
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const relevantCurrencies = [
|
||||
'xmr',
|
||||
'btc',
|
||||
] as const satisfies (typeof filtersOptions.currencies)[number]['slug'][]
|
||||
|
||||
const byCategoryAndAttributesAndRelevantCurrency = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.map(
|
||||
(attribute) =>
|
||||
new URLSearchParams({
|
||||
[`attr-${attribute.id.toString()}`]: 'no',
|
||||
})
|
||||
)
|
||||
|
||||
const byCategoryAndCurrency = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.currencies.map(
|
||||
(currency) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
currencies: currency.slug,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const byCategoryAndAttributes = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.flatMap((attribute) => [
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
[`attr-${attribute.id.toString()}`]: 'yes',
|
||||
}),
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
[`attr-${attribute.id.toString()}`]: 'no',
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const relevantCurrencies = [
|
||||
'xmr',
|
||||
'btc',
|
||||
] as const satisfies (typeof filtersOptions.currencies)[number]['slug'][]
|
||||
|
||||
const byCategoryAndAttributesAndRelevantCurrency = filtersOptions.categories.flatMap((category) =>
|
||||
filtersOptions.attributesByCategory
|
||||
.flatMap(({ attributes }) => attributes)
|
||||
.flatMap((attribute) =>
|
||||
relevantCurrencies.map(
|
||||
(currency) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
currencies: currency,
|
||||
[`attr-${attribute.id.toString()}`]:
|
||||
attribute.type === 'GOOD' || attribute.type === 'INFO' ? 'yes' : 'no',
|
||||
})
|
||||
)
|
||||
.flatMap((attribute) =>
|
||||
relevantCurrencies.map(
|
||||
(currency) =>
|
||||
new URLSearchParams({
|
||||
categories: category.slug,
|
||||
currencies: currency,
|
||||
[`attr-${attribute.id.toString()}`]:
|
||||
attribute.type === 'GOOD' || attribute.type === 'INFO' ? 'yes' : 'no',
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const allQueryParams = [
|
||||
...byCategory,
|
||||
...byVerificationStatus,
|
||||
...byKycLevel,
|
||||
...byCurrency,
|
||||
...withOneAttribute,
|
||||
...withoutOneAttribute,
|
||||
const allQueryParams = [
|
||||
...byCategory,
|
||||
...byVerificationStatus,
|
||||
...byKycLevel,
|
||||
...byCurrency,
|
||||
...withOneAttribute,
|
||||
...withoutOneAttribute,
|
||||
|
||||
...byCategoryAndCurrency,
|
||||
...byCategoryAndAttributes,
|
||||
...byCategoryAndAttributesAndRelevantCurrency,
|
||||
] satisfies URLSearchParams[]
|
||||
...byCategoryAndCurrency,
|
||||
...byCategoryAndAttributes,
|
||||
...byCategoryAndAttributesAndRelevantCurrency,
|
||||
] satisfies URLSearchParams[]
|
||||
|
||||
return allQueryParams.map((queryParams) => {
|
||||
const url = new URL(siteUrl)
|
||||
url.search = queryParams.toString()
|
||||
return url.href
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to generate SEO sitemap URLs:', error)
|
||||
return []
|
||||
}
|
||||
return allQueryParams.map((queryParams) => {
|
||||
const url = new URL(siteUrl)
|
||||
url.search = queryParams.toString()
|
||||
return url.href
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user