postio / apps /backend /src /api /routes /posts.controller.ts
Leon4gr45's picture
Upload folder using huggingface_hub (part 4)
3722089 verified
Raw
History Blame Contribute Delete
8.72 kB
import {
Body,
Controller,
Delete,
Get,
HttpException,
Param,
Post,
Put,
Query,
Res,
} from '@nestjs/common';
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
import { Organization, User } from '@prisma/client';
import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto';
import { GetPostsListDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.list.dto';
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
import { ApiTags } from '@nestjs/swagger';
import { GeneratorDto } from '@gitroom/nestjs-libraries/dtos/generator/generator.dto';
import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generator/create.generated.posts.dto';
import { AgentGraphService } from '@gitroom/nestjs-libraries/agent/agent.graph.service';
import { Response } from 'express';
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service';
import { CreateTagDto } from '@gitroom/nestjs-libraries/dtos/posts/create.tag.dto';
import {
AuthorizationActions,
Sections,
} from '@gitroom/backend/services/auth/permissions/permission.exception.class';
import { PostValidationException } from '@gitroom/backend/api/routes/posts.validation.exception';
@ApiTags('Posts')
@Controller('/posts')
export class PostsController {
constructor(
private _postsService: PostsService,
private _agentGraphService: AgentGraphService,
private _shortLinkService: ShortLinkService
) {}
@Get('/:id/statistics')
async getStatistics(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string
) {
return this._postsService.getStatistics(org.id, id);
}
@Get('/:id/missing')
async getMissingContent(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string
) {
return this._postsService.getMissingContent(org.id, id);
}
@Put('/:id/release-id')
async updateReleaseId(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string,
@Body('releaseId') releaseId: string
) {
return this._postsService.updateReleaseId(org.id, id, releaseId);
}
@Post('/should-shortlink')
async shouldShortlink(@Body() body: { messages: string[] }) {
return { ask: this._shortLinkService.askShortLinkedin(body.messages) };
}
@Post('/:id/comments')
async createComment(
@GetOrgFromRequest() org: Organization,
@GetUserFromRequest() user: User,
@Param('id') id: string,
@Body() body: { comment: string }
) {
return this._postsService.createComment(org.id, user.id, id, body.comment);
}
@Get('/tags')
async getTags(@GetOrgFromRequest() org: Organization) {
return { tags: await this._postsService.getTags(org.id) };
}
@Post('/tags')
async createTag(
@GetOrgFromRequest() org: Organization,
@Body() body: CreateTagDto
) {
return this._postsService.createTag(org.id, body);
}
@Put('/tags/:id')
async editTag(
@GetOrgFromRequest() org: Organization,
@Body() body: CreateTagDto,
@Param('id') id: string
) {
return this._postsService.editTag(id, org.id, body);
}
@Delete('/tags/:id')
async deleteTag(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string
) {
return this._postsService.deleteTag(id, org.id);
}
@Get('/')
async getPosts(
@GetOrgFromRequest() org: Organization,
@Query() query: GetPostsDto
) {
return this._postsService.getPostsMinified(org.id, query);
}
@Get('/find-slot')
async findSlot(@GetOrgFromRequest() org: Organization) {
return { date: await this._postsService.findFreeDateTime(org.id) };
}
@Get('/find-slot/:id')
async findSlotIntegration(
@GetOrgFromRequest() org: Organization,
@Param('id') id?: string
) {
return { date: await this._postsService.findFreeDateTime(org.id, id) };
}
@Get('/list')
async getPostsList(
@GetOrgFromRequest() org: Organization,
@Query() query: GetPostsListDto
) {
return this._postsService.getPostsList(org.id, query);
}
@Get('/old')
oldPosts(
@GetOrgFromRequest() org: Organization,
@Query('date') date: string
) {
return this._postsService.getOldPosts(org.id, date);
}
@Get('/group/:group/debug-export')
async getPostGroupDebugExport(
@GetOrgFromRequest() org: Organization,
@GetUserFromRequest() user: User,
@Param('group') group: string
) {
if (!user.isSuperAdmin) {
throw new HttpException('Forbidden', 403);
}
return this._postsService.getPostGroupDebugExport(org.id, group);
}
@Get('/group/:group')
getPostsByGroup(@GetOrgFromRequest() org: Organization, @Param('group') group: string) {
return this._postsService.getPostsByGroup(org.id, group);
}
@Get('/:id')
getPost(@GetOrgFromRequest() org: Organization, @Param('id') id: string) {
return this._postsService.getPost(org.id, id);
}
@Post('/valid')
async validatePosts(
@GetOrgFromRequest() org: Organization,
@Body() rawBody: any
) {
return this._postsService.validatePosts(org.id, rawBody?.posts || []);
}
@Post('/')
@CheckPolicies([AuthorizationActions.Create, Sections.POSTS_PER_MONTH])
async createPost(
@GetOrgFromRequest() org: Organization,
@Body() rawBody: any
) {
// Server-side validation — never trust the client to have validated.
const validation = await this._postsService.validatePosts(
org.id,
rawBody?.posts || []
);
const fail = (item: (typeof validation)[number], error: string) => {
throw new PostValidationException({
provider: item.identifier,
name: item.name,
error,
});
};
for (const item of validation) {
if (item.emptyContent) {
fail(
item,
'Your post should have at least one character or one image.'
);
}
}
if (rawBody?.type !== 'draft') {
for (const item of validation) {
if (!item.valid) {
fail(item, item.settingsError || 'Please fix your settings');
}
if (item.errors !== true) {
fail(item, item.errors as string);
}
if (item.tooLong) {
fail(item, 'post is too long, please fix it');
}
}
}
const body = await this._postsService.mapTypeToPost(rawBody, org.id);
return this._postsService.createPost(org.id, body, 'WEB');
}
@Post('/generator/draft')
@CheckPolicies([AuthorizationActions.Create, Sections.POSTS_PER_MONTH])
generatePostsDraft(
@GetOrgFromRequest() org: Organization,
@Body() body: CreateGeneratedPostsDto
) {
return this._postsService.generatePostsDraft(org.id, body);
}
@Post('/generator')
@CheckPolicies([AuthorizationActions.Create, Sections.POSTS_PER_MONTH])
async generatePosts(
@GetOrgFromRequest() org: Organization,
@Body() body: GeneratorDto,
@Res({ passthrough: false }) res: Response
) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
try {
for await (const event of this._agentGraphService.start(org.id, body)) {
res.write(JSON.stringify(event) + '\n');
}
} catch (err) {
// The stream has already started, so we cannot surface a normal HTTP
// error here. Emit a final error event on the open stream instead, so the
// client can stop and show the message rather than hang on a truncated
// stream. HttpExceptions carry a curated, user-facing message (e.g. the
// AI safety rejection); anything else gets a generic message.
const message =
err instanceof HttpException
? err.message
: 'Something went wrong while generating your posts, please try again.';
res.write(JSON.stringify({ name: 'error', error: true, message }) + '\n');
}
res.end();
}
@Delete('/:group')
deletePost(
@GetOrgFromRequest() org: Organization,
@Param('group') group: string
) {
return this._postsService.deletePost(org.id, group);
}
@Put('/:id/date')
changeDate(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string,
@Body('date') date: string,
@Body('action') action: 'schedule' | 'update' = 'schedule'
) {
return this._postsService.changeDate(org.id, id, date, action);
}
@Post('/separate-posts')
async separatePosts(
@GetOrgFromRequest() org: Organization,
@Body() body: { content: string; len: number }
) {
return this._postsService.separatePosts(body.content, body.len);
}
}