Add public API keys, outbound webhooks, and bulk SMS campaigns with settings UI and org modules.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { ApiKeyAuthGuard } from './api-key-auth.guard';
|
||||
|
||||
class ListQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
q?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page?: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
limit?: number = 20;
|
||||
}
|
||||
|
||||
@Controller('public/v1')
|
||||
@UseGuards(ApiKeyAuthGuard)
|
||||
export class PublicApiV1Controller {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
@Get('people')
|
||||
async listPeople(
|
||||
@CurrentUser() user: { organizationId: string },
|
||||
@Query() query: ListQueryDto,
|
||||
) {
|
||||
const page = query.page ?? 1;
|
||||
const limit = query.limit ?? 20;
|
||||
const where = {
|
||||
organizationId: user.organizationId,
|
||||
...(query.q?.trim()
|
||||
? {
|
||||
OR: [
|
||||
{ firstName: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||||
{ lastName: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||||
{ mobile: { contains: query.q.trim() } },
|
||||
{ email: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const [total, items] = await Promise.all([
|
||||
this.prisma.person.count({ where }),
|
||||
this.prisma.person.findMany({
|
||||
where,
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
mobile: true,
|
||||
phone: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
}),
|
||||
]);
|
||||
return {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
items: items.map((p) => ({
|
||||
...p,
|
||||
fullName: `${p.firstName} ${p.lastName}`.trim(),
|
||||
createdAt: p.createdAt.toISOString(),
|
||||
updatedAt: p.updatedAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@Get('people/:id')
|
||||
async getPerson(
|
||||
@CurrentUser() user: { organizationId: string },
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
const p = await this.prisma.person.findFirst({
|
||||
where: { id, organizationId: user.organizationId },
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
mobile: true,
|
||||
phone: true,
|
||||
notes: true,
|
||||
tags: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
if (!p) return null;
|
||||
return {
|
||||
...p,
|
||||
fullName: `${p.firstName} ${p.lastName}`.trim(),
|
||||
createdAt: p.createdAt.toISOString(),
|
||||
updatedAt: p.updatedAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
@Get('deals')
|
||||
async listDeals(
|
||||
@CurrentUser() user: { organizationId: string },
|
||||
@Query() query: ListQueryDto,
|
||||
) {
|
||||
const page = query.page ?? 1;
|
||||
const limit = query.limit ?? 20;
|
||||
const where = {
|
||||
organizationId: user.organizationId,
|
||||
...(query.q?.trim()
|
||||
? {
|
||||
title: { contains: query.q.trim(), mode: 'insensitive' as const },
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const [total, items] = await Promise.all([
|
||||
this.prisma.deal.count({ where }),
|
||||
this.prisma.deal.findMany({
|
||||
where,
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
status: true,
|
||||
amount: true,
|
||||
currency: true,
|
||||
source: true,
|
||||
stageId: true,
|
||||
pipelineId: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
}),
|
||||
]);
|
||||
return {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
items: items.map((d) => ({
|
||||
...d,
|
||||
amount: d.amount == null ? null : Number(d.amount),
|
||||
createdAt: d.createdAt.toISOString(),
|
||||
updatedAt: d.updatedAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user