Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/admin/admin.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class AdminService {
async listUsers(query: AdminUsersQueryDto) {
const page = query.page ?? 1;
const limit = query.limit ?? 20;
const skip = (query as any).cursor ? undefined : (page - 1) * limit;
const skip = query.cursor ? undefined : (page - 1) * limit;

const where: any = {
role: query.role,
Expand All @@ -122,8 +122,8 @@ export class AdminService {
: undefined,
};

if ((query as any).cursor) {
where.createdAt = { lt: new Date(Buffer.from((query as any).cursor, 'base64').toString()) };
if (query.cursor) {
where.createdAt = { lt: new Date(Buffer.from(query.cursor, 'base64').toString()) };
}

const [items, total] = await Promise.all([
Expand All @@ -148,10 +148,10 @@ export class AdminService {

const nextCursor =
items.length === limit
? Buffer.from((items[items.length - 1] as any).createdAt.toISOString()).toString('base64')
? Buffer.from(items[items.length - 1].createdAt.toISOString()).toString('base64')
: null;

return { total, page, limit, items, nextCursor, previousCursor: (query as any).cursor || null };
return { total, page, limit, items, nextCursor, previousCursor: query.cursor || null };
}

async updateUser(userId: string, payload: AdminUpdateUserDto, actorId?: string) {
Expand Down Expand Up @@ -240,14 +240,14 @@ export class AdminService {
async getModerationQueue(query: ModerationQueueQueryDto) {
const page = query.page ?? 1;
const limit = query.limit ?? 20;
const skip = (query as any).cursor ? undefined : (page - 1) * limit;
const skip = query.cursor ? undefined : (page - 1) * limit;

const where: any = {
status: query.status ?? PropertyStatus.PENDING,
};

if ((query as any).cursor) {
where.createdAt = { lt: new Date(Buffer.from((query as any).cursor, 'base64').toString()) };
if (query.cursor) {
where.createdAt = { lt: new Date(Buffer.from(query.cursor, 'base64').toString()) };
}

const [items, total] = await Promise.all([
Expand All @@ -272,10 +272,10 @@ export class AdminService {

const nextCursor =
items.length === limit
? Buffer.from((items[items.length - 1] as any).createdAt.toISOString()).toString('base64')
? Buffer.from(items[items.length - 1].createdAt.toISOString()).toString('base64')
: null;

return { total, page, limit, items, nextCursor, previousCursor: (query as any).cursor || null };
return { total, page, limit, items, nextCursor, previousCursor: query.cursor || null };
}

async approveProperty(propertyId: string) {
Expand Down Expand Up @@ -348,16 +348,16 @@ export class AdminService {
async monitorTransactions(query: TransactionMonitoringQueryDto) {
const page = query.page ?? 1;
const limit = query.limit ?? 20;
const skip = (query as any).cursor ? undefined : (page - 1) * limit;
const skip = query.cursor ? undefined : (page - 1) * limit;

const where: any = {
status: query.status,
type: query.type,
propertyId: query.propertyId,
};

if ((query as any).cursor) {
where.createdAt = { lt: new Date(Buffer.from((query as any).cursor, 'base64').toString()) };
if (query.cursor) {
where.createdAt = { lt: new Date(Buffer.from(query.cursor, 'base64').toString()) };
}

const [items, total] = await Promise.all([
Expand All @@ -383,10 +383,10 @@ export class AdminService {

const nextCursor =
items.length === limit
? Buffer.from((items[items.length - 1] as any).createdAt.toISOString()).toString('base64')
? Buffer.from(items[items.length - 1].createdAt.toISOString()).toString('base64')
: null;

return { total, page, limit, items, nextCursor, previousCursor: (query as any).cursor || null };
return { total, page, limit, items, nextCursor, previousCursor: query.cursor || null };
}

async transactionMonitoringSummary() {
Expand Down
12 changes: 12 additions & 0 deletions src/admin/dto/admin.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export class AdminUsersQueryDto {
@Min(1)
@Max(100)
limit: number = 20;

@IsOptional()
@IsString()
cursor?: string;
}

export class AdminUpdateUserDto {
Expand Down Expand Up @@ -88,6 +92,10 @@ export class ModerationQueueQueryDto {
@Min(1)
@Max(100)
limit: number = 20;

@IsOptional()
@IsString()
cursor?: string;
}

export class FlagPropertyDto {
Expand Down Expand Up @@ -141,6 +149,10 @@ export class TransactionMonitoringQueryDto {
@Min(1)
@Max(100)
limit: number = 20;

@IsOptional()
@IsString()
cursor?: string;
}

export class UpdateTransactionStatusDto {
Expand Down
27 changes: 21 additions & 6 deletions src/backup/backup.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,39 @@ import { PrismaService } from '../database/prisma.service';
import { ConfigService } from '@nestjs/config';
import { NotificationsService } from '../notifications/notifications.service';

interface MockPrisma {
databaseBackup: {
findMany: jest.Mock;
count: jest.Mock;
findFirst: jest.Mock;
};
backupScheduleConfig: {
findUnique: jest.Mock;
upsert: jest.Mock;
};
}

describe('BackupService', () => {
let service: BackupService;
let prisma: jest.Mocked<Partial<PrismaService>>;
let prisma: MockPrisma;

beforeEach(() => {
prisma = {
databaseBackup: {
findMany: jest.fn().mockResolvedValue([]),
count: jest.fn().mockResolvedValue(0),
findFirst: jest.fn().mockResolvedValue(null),
} as any,
},
backupScheduleConfig: {
findUnique: jest.fn().mockResolvedValue({
id: 'default', enabled: false, cronExpression: '0 2 * * *',
retentionCount: 10, lastRunAt: null,
id: 'default',
enabled: false,
cronExpression: '0 2 * * *',
retentionCount: 10,
lastRunAt: null,
}),
upsert: jest.fn().mockResolvedValue({}),
} as any,
},
};
service = new BackupService(
prisma as unknown as PrismaService,
Expand All @@ -45,4 +60,4 @@ describe('BackupService', () => {
expect(result.enabled).toBe(false);
expect(result.cronExpression).toBe('0 2 * * *');
});
});
});
24 changes: 12 additions & 12 deletions src/backup/backup.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class BackupService implements OnModuleInit {
}),
this.prisma.databaseBackup.count({
where: {
OR: [{ status: 'RUNNING' as any }, { restoreStatus: 'RUNNING' as any }],
OR: [{ status: BackupStatus.RUNNING }, { restoreStatus: RestoreStatus.RUNNING }],
},
}),
this.prisma.databaseBackup.count(),
Expand Down Expand Up @@ -107,7 +107,7 @@ export class BackupService implements OnModuleInit {
}

async createManualBackup(initiatedById?: string) {
return this.createBackup('MANUAL' as any, initiatedById);
return this.createBackup(BackupTrigger.MANUAL, initiatedById);
}

async restoreBackup(backupId: string, restoredById?: string) {
Expand All @@ -128,7 +128,7 @@ export class BackupService implements OnModuleInit {
await this.prisma.databaseBackup.update({
where: { id: backupId },
data: {
restoreStatus: 'RUNNING' as any,
restoreStatus: RestoreStatus.RUNNING,
restoreError: null,
restoredById: restoredById ?? null,
},
Expand All @@ -140,7 +140,7 @@ export class BackupService implements OnModuleInit {
const restored = await this.prisma.databaseBackup.update({
where: { id: backupId },
data: {
restoreStatus: 'COMPLETED' as any,
restoreStatus: RestoreStatus.COMPLETED,
restoredAt: new Date(),
restoreError: null,
restoredById: restoredById ?? null,
Expand All @@ -154,7 +154,7 @@ export class BackupService implements OnModuleInit {
await this.prisma.databaseBackup.update({
where: { id: backupId },
data: {
restoreStatus: 'FAILED' as any,
restoreStatus: RestoreStatus.FAILED,
restoreError: message,
restoredById: restoredById ?? null,
},
Expand Down Expand Up @@ -200,7 +200,7 @@ export class BackupService implements OnModuleInit {
data: {
filename,
filePath,
status: 'RUNNING' as any,
status: BackupStatus.RUNNING,
trigger,
initiatedById: initiatedById ?? null,
},
Expand All @@ -215,15 +215,15 @@ export class BackupService implements OnModuleInit {
const completed = await this.prisma.databaseBackup.update({
where: { id: backup.id },
data: {
status: 'COMPLETED' as any,
status: BackupStatus.COMPLETED,
completedAt: new Date(),
sizeBytes: BigInt(stats.size),
checksum,
errorMessage: null,
},
});

if (trigger === ('SCHEDULED' as any)) {
if (trigger === BackupTrigger.SCHEDULED) {
await this.prisma.backupScheduleConfig.update({
where: { id: DEFAULT_SCHEDULE_ID },
data: { lastRunAt: new Date() },
Expand All @@ -238,7 +238,7 @@ export class BackupService implements OnModuleInit {
await this.prisma.databaseBackup.update({
where: { id: backup.id },
data: {
status: 'FAILED' as any,
status: BackupStatus.FAILED,
completedAt: new Date(),
errorMessage: message,
},
Expand Down Expand Up @@ -279,7 +279,7 @@ export class BackupService implements OnModuleInit {
while (attempt < maxRetries && !success) {
attempt++;
try {
await this.createBackup('SCHEDULED' as any);
await this.createBackup(BackupTrigger.SCHEDULED);
success = true;
} catch (error) {
const errorMessage = this.toErrorMessage(error);
Expand Down Expand Up @@ -335,7 +335,7 @@ export class BackupService implements OnModuleInit {
private async enforceRetentionPolicy() {
const schedule = await this.getScheduleConfig();
const backups = await this.prisma.databaseBackup.findMany({
where: { status: 'COMPLETED' as any },
where: { status: BackupStatus.COMPLETED },
orderBy: { createdAt: 'desc' },
skip: schedule.retentionCount,
});
Expand All @@ -355,7 +355,7 @@ export class BackupService implements OnModuleInit {
const activeJobs = await this.prisma.databaseBackup.count({
where: {
id: excludedBackupId ? { not: excludedBackupId } : undefined,
OR: [{ status: 'RUNNING' as any }, { restoreStatus: 'RUNNING' as any }],
OR: [{ status: BackupStatus.RUNNING }, { restoreStatus: RestoreStatus.RUNNING }],
},
});

Expand Down
20 changes: 11 additions & 9 deletions src/search/search-autocomplete.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class SearchAutocompleteService {
*/
private async getPropertySuggestions(query: string, limit: number): Promise<Suggestion[]> {
try {
const properties = await (this.prisma as any).property.findMany({
const properties = await this.prisma.property.findMany({
where: {
deleted: false,
OR: [
Expand Down Expand Up @@ -108,7 +108,9 @@ export class SearchAutocompleteService {
*/
private async getLocationSuggestions(query: string, limit: number): Promise<Suggestion[]> {
try {
const rows = await (this.prisma as any).$queryRaw`
const rows = await this.prisma.$queryRaw<
Array<{ city: string; state: string; zip_code: string }>
>`
SELECT DISTINCT city, state, zip_code
FROM properties
WHERE deleted = false
Expand All @@ -120,7 +122,7 @@ export class SearchAutocompleteService {
LIMIT ${limit}
`;

return rows.map((r: any) => ({
return rows.map((r) => ({
text: `${r.city}, ${r.state} ${r.zip_code}`.trim(),
type: 'location' as const,
}));
Expand Down Expand Up @@ -176,7 +178,7 @@ export class SearchAutocompleteService {
if (!userId) return [];

try {
const history = await (this.prisma as any).searchHistory.findMany({
const history = await this.prisma.searchHistory.findMany({
where: {
userId,
query: { contains: query, mode: 'insensitive' },
Expand Down Expand Up @@ -206,7 +208,7 @@ export class SearchAutocompleteService {
*/
private async getPopularSearchSuggestions(query: string, limit: number): Promise<Suggestion[]> {
try {
const popular = await (this.prisma as any).popularSearch.findMany({
const popular = await this.prisma.popularSearch.findMany({
where: {
query: { contains: query, mode: 'insensitive' },
},
Expand Down Expand Up @@ -329,13 +331,13 @@ export class SearchAutocompleteService {

async recordSuggestionClick(suggestion: string, userId: string): Promise<void> {
try {
await (this.prisma as any).searchHistory.upsert({
await this.prisma.searchHistory.upsert({
where: { userId_query: { userId, query: suggestion } },
update: { frequency: { increment: 1 }, lastSearched: new Date() },
create: { userId, query: suggestion, frequency: 1 },
});

await (this.prisma as any).popularSearch.upsert({
await this.prisma.popularSearch.upsert({
where: { query: suggestion },
update: { frequency: { increment: 1 }, lastUpdated: new Date() },
create: { query: suggestion, frequency: 1, trend: 'stable' },
Expand All @@ -347,7 +349,7 @@ export class SearchAutocompleteService {

async getPopularSearches(limit: number = 10): Promise<string[]> {
try {
const rows = await (this.prisma as any).popularSearch.findMany({
const rows = await this.prisma.popularSearch.findMany({
orderBy: { frequency: 'desc' },
take: limit,
select: { query: true },
Expand All @@ -371,7 +373,7 @@ export class SearchAutocompleteService {

async getRecentSearches(userId: string, limit: number = 5): Promise<string[]> {
try {
const rows = await (this.prisma as any).searchHistory.findMany({
const rows = await this.prisma.searchHistory.findMany({
where: { userId },
orderBy: { lastSearched: 'desc' },
take: limit,
Expand Down
Loading
Loading