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 { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { iranDaysUntil } from '../common/iran-time.util';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
type UiSetting = { key: string; value: string };
|
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()
|
@Injectable()
|
||||||
export class GetUserService {
|
export class GetUserService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
@@ -22,6 +39,8 @@ export class GetUserService {
|
|||||||
throw new NotFoundException('tenant not found for domain');
|
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 ui = (tenant.appUiConfig as Record<string, any> | null) || null;
|
||||||
const isHamkarUi = tenant.product === 'hamkar' && ui?.kind === 'hamkar';
|
const isHamkarUi = tenant.product === 'hamkar' && ui?.kind === 'hamkar';
|
||||||
const hamkarBrand = isHamkarUi ? ui?.brand : null;
|
const hamkarBrand = isHamkarUi ? ui?.brand : null;
|
||||||
@@ -89,6 +108,8 @@ export class GetUserService {
|
|||||||
: undefined,
|
: undefined,
|
||||||
/** Laravel-compatible overlay; Hamkar merges only when present */
|
/** Laravel-compatible overlay; Hamkar merges only when present */
|
||||||
ui_settings: uiSettings.length ? uiSettings : undefined,
|
ui_settings: uiSettings.length ? uiSettings : undefined,
|
||||||
|
/** Admin expiry modals — near/expired server, plugin, support */
|
||||||
|
subscription_alerts,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
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
|
* Web App Manifest برای PWA / ساخت APK — برندینگ همان get_user
|
||||||
* GET /manifest.json?domain=
|
* GET /manifest.json?domain=
|
||||||
|
|||||||
Reference in New Issue
Block a user