mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-12 06:40:58 +04:30
Initial commit: Meshkee CMS API
NestJS backend with Prisma, Docker Compose for Postgres/Redis, and deploy docs for the production VM.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { Controller, Get, Param } from '@nestjs/common';
|
||||
import { TenantService } from './tenant.service';
|
||||
|
||||
@Controller('tenants')
|
||||
export class TenantController {
|
||||
constructor(private readonly tenant: TenantService) {}
|
||||
|
||||
/** Resolve business from website/dashboard domain (e.g. sanihome.ir). */
|
||||
@Get(':host')
|
||||
resolve(@Param('host') host: string) {
|
||||
return this.tenant.resolvePublicBusinessByDomain(host);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TenantController } from './tenant.controller';
|
||||
import { TenantService } from './tenant.service';
|
||||
|
||||
@Module({
|
||||
controllers: [TenantController],
|
||||
providers: [TenantService],
|
||||
exports: [TenantService],
|
||||
})
|
||||
export class TenantModule {}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { normalizeBusinessSettings } from '../business-settings/business-settings.util';
|
||||
|
||||
const DASHBOARD_SUBDOMAIN_PREFIXES = ['customer.', 'business.'] as const;
|
||||
|
||||
@Injectable()
|
||||
export class TenantService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private normalizeTenantHost(host: string) {
|
||||
const normalizedHost = host.toLowerCase().trim();
|
||||
|
||||
for (const prefix of DASHBOARD_SUBDOMAIN_PREFIXES) {
|
||||
if (normalizedHost.startsWith(prefix)) {
|
||||
return normalizedHost.slice(prefix.length);
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedHost;
|
||||
}
|
||||
|
||||
async resolveBusinessByDomain(host: string) {
|
||||
const normalizedHost = this.normalizeTenantHost(host);
|
||||
|
||||
const domain = await this.prisma.domain.findUnique({
|
||||
where: { host: normalizedHost },
|
||||
include: { business: true },
|
||||
});
|
||||
|
||||
if (!domain || !domain.business.isActive) {
|
||||
throw new NotFoundException(`No business found for domain: ${host}`);
|
||||
}
|
||||
|
||||
return domain.business;
|
||||
}
|
||||
|
||||
async resolvePublicBusinessByDomain(host: string) {
|
||||
const business = await this.resolveBusinessByDomain(host);
|
||||
const normalizedHost = this.normalizeTenantHost(host);
|
||||
const settings = normalizeBusinessSettings(business.settings);
|
||||
|
||||
const media = await this.prisma.business.findUnique({
|
||||
where: { id: business.id },
|
||||
select: {
|
||||
logoMedia: { select: { publicUrl: true } },
|
||||
faviconMedia: { select: { publicUrl: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: business.id,
|
||||
name: business.name,
|
||||
nameFa: business.nameFa,
|
||||
slug: business.slug,
|
||||
domain: normalizedHost,
|
||||
primaryColor: settings.branding.primaryColor,
|
||||
logoUrl: media?.logoMedia?.publicUrl ?? null,
|
||||
faviconUrl:
|
||||
media?.faviconMedia?.publicUrl ?? media?.logoMedia?.publicUrl ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user