Add production-ready SuperAdmin API features for tenants, SMS, servers, and plugins.
Includes mobile auth, get_user/get_version, SMS wallet parity endpoints, appearance backfill, and related Prisma migrations. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { Controller, Get, Headers, Query } from '@nestjs/common';
|
||||
import { GetVersionService } from './get-version.service';
|
||||
|
||||
@Controller()
|
||||
export class GetVersionController {
|
||||
constructor(private readonly getVersionService: GetVersionService) {}
|
||||
|
||||
@Get('get_version')
|
||||
getVersion(
|
||||
@Headers('host') host?: string,
|
||||
@Headers('x-forwarded-host') forwardedHost?: string,
|
||||
@Query('domain') domain?: string,
|
||||
) {
|
||||
return this.getVersionService.resolveByHost(
|
||||
domain || forwardedHost || host,
|
||||
domain,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GetVersionController } from './get-version.controller';
|
||||
import { GetVersionService } from './get-version.service';
|
||||
|
||||
@Module({
|
||||
controllers: [GetVersionController],
|
||||
providers: [GetVersionService],
|
||||
})
|
||||
export class GetVersionModule {}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class GetVersionService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async resolveByHost(hostHeader?: string, domainQuery?: string) {
|
||||
const host = this.normalizeHost(domainQuery || hostHeader || '');
|
||||
if (!host) {
|
||||
throw new NotFoundException('domain required');
|
||||
}
|
||||
|
||||
const tenants = await this.prisma.tenant.findMany({
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
appVersion: true,
|
||||
domain: true,
|
||||
domains: true,
|
||||
},
|
||||
});
|
||||
|
||||
const tenant = tenants.find((item) => this.matchesHost(item, host));
|
||||
if (!tenant) {
|
||||
throw new NotFoundException('tenant not found for domain');
|
||||
}
|
||||
|
||||
return { version: tenant.appVersion || '1' };
|
||||
}
|
||||
|
||||
private matchesHost(
|
||||
tenant: {
|
||||
domain: string;
|
||||
domains: unknown;
|
||||
},
|
||||
host: string,
|
||||
) {
|
||||
const candidates = new Set<string>();
|
||||
for (const value of [tenant.domain, ...this.readDomains(tenant.domains)]) {
|
||||
const normalized = this.normalizeHost(value);
|
||||
if (!normalized) continue;
|
||||
candidates.add(normalized);
|
||||
candidates.add(normalized.replace(/^www\./, ''));
|
||||
candidates.add(`www.${normalized.replace(/^www\./, '')}`);
|
||||
}
|
||||
return candidates.has(host) || candidates.has(host.replace(/^www\./, ''));
|
||||
}
|
||||
|
||||
private readDomains(raw: unknown): string[] {
|
||||
if (!raw) return [];
|
||||
if (Array.isArray(raw)) return raw.map(String);
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed.map(String) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private normalizeHost(value: string) {
|
||||
return value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^https?:\/\//, '')
|
||||
.split('/')[0]
|
||||
.split(':')[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user