feat(get_user): expose subscription_alerts for admin expiry modals
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,25 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { iranDaysUntil } from '../common/iran-time.util';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
type UiSetting = { key: string; value: string };
|
||||
|
||||
type SubscriptionAlertStatus = 'expiring' | 'expired';
|
||||
|
||||
type SubscriptionAlertItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
expires_at: string;
|
||||
days_left: number;
|
||||
status: SubscriptionAlertStatus;
|
||||
};
|
||||
|
||||
type SubscriptionAlerts = {
|
||||
servers: SubscriptionAlertItem[];
|
||||
plugins: SubscriptionAlertItem[];
|
||||
supports: SubscriptionAlertItem[];
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class GetUserService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
@@ -22,6 +39,8 @@ export class GetUserService {
|
||||
throw new NotFoundException('tenant not found for domain');
|
||||
}
|
||||
|
||||
const subscription_alerts = await this.buildSubscriptionAlerts(tenant.id);
|
||||
|
||||
const ui = (tenant.appUiConfig as Record<string, any> | null) || null;
|
||||
const isHamkarUi = tenant.product === 'hamkar' && ui?.kind === 'hamkar';
|
||||
const hamkarBrand = isHamkarUi ? ui?.brand : null;
|
||||
@@ -89,6 +108,8 @@ export class GetUserService {
|
||||
: undefined,
|
||||
/** Laravel-compatible overlay; Hamkar merges only when present */
|
||||
ui_settings: uiSettings.length ? uiSettings : undefined,
|
||||
/** Admin expiry modals — near/expired server, plugin, support */
|
||||
subscription_alerts,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -97,6 +118,110 @@ export class GetUserService {
|
||||
};
|
||||
}
|
||||
|
||||
private async buildSubscriptionAlerts(tenantId: string): Promise<SubscriptionAlerts> {
|
||||
const [servers, plugins, supports] = await Promise.all([
|
||||
this.prisma.serverAccount.findMany({
|
||||
where: {
|
||||
tenantId,
|
||||
isDedicated: false,
|
||||
expiresAt: { not: null },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
serverName: true,
|
||||
expiresAt: true,
|
||||
alertDaysBefore: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.pluginSubscription.findMany({
|
||||
where: {
|
||||
tenantId,
|
||||
isActive: true,
|
||||
expiresAt: { not: null },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
expiresAt: true,
|
||||
alertDaysBefore: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.supportSubscription.findMany({
|
||||
where: {
|
||||
tenantId,
|
||||
isActive: true,
|
||||
expiresAt: { not: null },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
expiresAt: true,
|
||||
alertDaysBefore: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
servers: servers
|
||||
.map((row) =>
|
||||
this.toAlertItem({
|
||||
id: row.id,
|
||||
name: (row.serverName || '').trim() || 'سرور',
|
||||
expiresAt: row.expiresAt!,
|
||||
alertDaysBefore: row.alertDaysBefore,
|
||||
}),
|
||||
)
|
||||
.filter((item): item is SubscriptionAlertItem => item != null),
|
||||
plugins: plugins
|
||||
.map((row) =>
|
||||
this.toAlertItem({
|
||||
id: row.id,
|
||||
name: (row.name || '').trim() || 'افزونه',
|
||||
expiresAt: row.expiresAt!,
|
||||
alertDaysBefore: row.alertDaysBefore,
|
||||
}),
|
||||
)
|
||||
.filter((item): item is SubscriptionAlertItem => item != null),
|
||||
supports: supports
|
||||
.map((row) =>
|
||||
this.toAlertItem({
|
||||
id: row.id,
|
||||
name: (row.name || '').trim() || 'پشتیبانی',
|
||||
expiresAt: row.expiresAt!,
|
||||
alertDaysBefore: row.alertDaysBefore,
|
||||
}),
|
||||
)
|
||||
.filter((item): item is SubscriptionAlertItem => item != null),
|
||||
};
|
||||
}
|
||||
|
||||
private toAlertItem(input: {
|
||||
id: string;
|
||||
name: string;
|
||||
expiresAt: Date;
|
||||
alertDaysBefore: number;
|
||||
}): SubscriptionAlertItem | null {
|
||||
const daysLeft = iranDaysUntil(input.expiresAt);
|
||||
const threshold = Math.max(0, Number(input.alertDaysBefore) || 0);
|
||||
|
||||
let status: SubscriptionAlertStatus | null = null;
|
||||
if (daysLeft < 0) {
|
||||
status = 'expired';
|
||||
} else if (daysLeft <= threshold) {
|
||||
status = 'expiring';
|
||||
}
|
||||
|
||||
if (!status) return null;
|
||||
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
expires_at: input.expiresAt.toISOString(),
|
||||
days_left: daysLeft,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Web App Manifest برای PWA / ساخت APK — برندینگ همان get_user
|
||||
* GET /manifest.json?domain=
|
||||
|
||||
Reference in New Issue
Block a user