Initial SuperAdmin API (NestJS + Prisma)
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { SettingsProxyService } from './settings-proxy.service';
|
||||
|
||||
@Controller('tenants/:tenantId/settings')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class SettingsProxyController {
|
||||
constructor(private readonly settingsProxy: SettingsProxyService) {}
|
||||
|
||||
@Get()
|
||||
getSettings(@Param('tenantId') tenantId: string) {
|
||||
return this.settingsProxy.getSettings(tenantId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
updateSettings(
|
||||
@Param('tenantId') tenantId: string,
|
||||
@Body() body: Record<string, unknown>,
|
||||
@CurrentUser() user: { id: string },
|
||||
) {
|
||||
return this.settingsProxy.updateSettings(tenantId, body, user.id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SettingsProxyController } from './settings-proxy.controller';
|
||||
import { SettingsProxyService } from './settings-proxy.service';
|
||||
|
||||
@Module({
|
||||
controllers: [SettingsProxyController],
|
||||
providers: [SettingsProxyService],
|
||||
})
|
||||
export class SettingsProxyModule {}
|
||||
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
BadGatewayException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import axios from 'axios';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
|
||||
@Injectable()
|
||||
export class SettingsProxyService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly audit: AuditService,
|
||||
) {}
|
||||
|
||||
async getSettings(tenantId: string) {
|
||||
const tenant = await this.requireTenant(tenantId);
|
||||
|
||||
if (!tenant.remoteApiToken) {
|
||||
return {
|
||||
stub: true,
|
||||
message:
|
||||
'remoteApiToken تنظیم نشده. فعلاً stub است تا endpoint سوپر روی Laravel اضافه شود.',
|
||||
tenant: {
|
||||
id: tenant.id,
|
||||
slug: tenant.slug,
|
||||
nodeServer: tenant.nodeServer,
|
||||
},
|
||||
settings: [],
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
this.joinUrl(tenant.nodeServer, '/super/settings'),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tenant.remoteApiToken}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
timeout: 15000,
|
||||
},
|
||||
);
|
||||
return { stub: false, settings: data };
|
||||
} catch (error) {
|
||||
throw new BadGatewayException(
|
||||
this.axiosMessage(error, 'خواندن تنظیمات از سرور نسخه ناموفق بود'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async updateSettings(
|
||||
tenantId: string,
|
||||
payload: Record<string, unknown>,
|
||||
userId?: string,
|
||||
) {
|
||||
const tenant = await this.requireTenant(tenantId);
|
||||
|
||||
if (!tenant.remoteApiToken) {
|
||||
await this.audit.log({
|
||||
action: 'settings.proxy.stub_update',
|
||||
entityType: 'tenant_settings',
|
||||
entityId: tenantId,
|
||||
tenantId,
|
||||
userId,
|
||||
after: payload as Prisma.InputJsonValue,
|
||||
meta: { stub: true },
|
||||
});
|
||||
|
||||
return {
|
||||
stub: true,
|
||||
message:
|
||||
'remoteApiToken تنظیم نشده؛ تغییر فقط در audit ثبت شد (stub).',
|
||||
accepted: payload,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
this.joinUrl(tenant.nodeServer, '/super/settings'),
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tenant.remoteApiToken}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
timeout: 20000,
|
||||
},
|
||||
);
|
||||
|
||||
await this.audit.log({
|
||||
action: 'settings.proxy.update',
|
||||
entityType: 'tenant_settings',
|
||||
entityId: tenantId,
|
||||
tenantId,
|
||||
userId,
|
||||
after: payload as Prisma.InputJsonValue,
|
||||
});
|
||||
|
||||
return { stub: false, result: data };
|
||||
} catch (error) {
|
||||
throw new BadGatewayException(
|
||||
this.axiosMessage(error, 'ذخیره تنظیمات روی سرور نسخه ناموفق بود'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async requireTenant(tenantId: string) {
|
||||
const tenant = await this.prisma.tenant.findUnique({
|
||||
where: { id: tenantId },
|
||||
});
|
||||
if (!tenant) {
|
||||
throw new NotFoundException('نسخه یافت نشد');
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
|
||||
private joinUrl(base: string, path: string) {
|
||||
return `${base.replace(/\/$/, '')}${path}`;
|
||||
}
|
||||
|
||||
private axiosMessage(error: unknown, fallback: string) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const status = error.response?.status;
|
||||
const body = error.response?.data;
|
||||
return `${fallback}${status ? ` (HTTP ${status})` : ''}${
|
||||
body ? `: ${JSON.stringify(body)}` : ''
|
||||
}`;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user