Add public user-products storefront API and website docs.

Expose published customer listings under /tenants/:host/user-products (list, search, details, technical-info) and document them in the website API pack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-10 00:19:13 +03:30
co-authored by Cursor
parent 158523df7b
commit 953b87b616
32 changed files with 3326 additions and 138 deletions
+35 -9
View File
@@ -26,25 +26,51 @@ export class CitiesService {
...(query.level ? { level: query.level } : {}),
};
if (query.parentId) {
where.parentId = BigInt(query.parentId);
} else if (query.parentSlug) {
const parent = await this.prisma.city.findFirst({
where: { slug: query.parentSlug, isActive: true },
select: { id: true },
});
let parent:
| { id: bigint; level: CityLevel }
| null = null;
if (query.parentId) {
parent = await this.prisma.city.findFirst({
where: { id: BigInt(query.parentId), isActive: true },
select: { id: true, level: true },
});
if (!parent) {
return { items: [] };
}
} else if (query.parentSlug) {
parent = await this.prisma.city.findFirst({
where: { slug: query.parentSlug, isActive: true },
select: { id: true, level: true },
});
if (!parent) {
return { items: [] };
}
where.parentId = parent.id;
} else if (query.level === CityLevel.province || query.level === CityLevel.city) {
throw new BadRequestException('parentId or parentSlug is required for this level');
} else {
where.level = CityLevel.country;
}
if (parent) {
// Cities under a country: direct children + cities under that country's provinces.
if (query.level === CityLevel.city && parent.level === CityLevel.country) {
const provinces = await this.prisma.city.findMany({
where: {
parentId: parent.id,
level: CityLevel.province,
isActive: true,
},
select: { id: true },
});
where.parentId = {
in: [parent.id, ...provinces.map((item) => item.id)],
};
} else {
where.parentId = parent.id;
}
}
const items = await this.prisma.city.findMany({
where,
orderBy: [{ sortOrder: 'asc' }, { nameEn: 'asc' }],