feat: track post creation method (WEB/API/MCP/AUTOPOST)

This commit is contained in:
Santosh Bhandari 2026-05-12 14:41:59 +05:45
parent 009bd36528
commit e153ab0a9b
13 changed files with 154 additions and 44 deletions

View file

@ -175,7 +175,7 @@ export class PostsController {
) {
console.log(JSON.stringify(rawBody, null, 2));
const body = await this._postsService.mapTypeToPost(rawBody, org.id);
return this._postsService.createPost(org.id, body);
return this._postsService.createPost(org.id, body, 'WEB');
}
@Post('/generator/draft')

View file

@ -189,7 +189,7 @@ export class PublicIntegrationsController {
}
console.log(JSON.stringify(body, null, 2));
return this._postsService.createPost(org.id, body);
return this._postsService.createPost(org.id, body, 'API');
}
@Delete('/posts/:id')

View file

@ -12,6 +12,7 @@ import { VideoOrImage } from '@gitroom/react/helpers/video.or.image';
import { CopyClient } from '@gitroom/frontend/components/preview/copy.client';
import { getT } from '@gitroom/react/translation/get.translation.service.backend';
import { RenderPreviewDateClient } from '@gitroom/frontend/components/preview/render.preview.date.client';
import { CreationMethodBadge } from '@gitroom/frontend/components/launches/creation.method.badge';
dayjs.extend(utc);
export const metadata: Metadata = {
@ -142,6 +143,12 @@ export default async function Auth(
<span className="text-sm text-gray-500">
@{post[0].integration.profile}
</span>
{index === 0 && (
<CreationMethodBadge
creationMethod={p.creationMethod}
size="md"
/>
)}
</div>
<div className="flex flex-col gap-[20px]">
<div

View file

@ -51,6 +51,7 @@ import { MissingReleaseModal } from '@gitroom/frontend/components/launches/missi
import { useT } from '@gitroom/react/translation/get.transation.service.client';
import i18next from 'i18next';
import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal';
import { CreationMethodBadge } from '@gitroom/frontend/components/launches/creation.method.badge';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import copy from 'copy-to-clipboard';
@ -1005,6 +1006,8 @@ const CalendarItem: FC<{
missingRelease,
} = props;
const { disableXAnalytics } = useVariables();
const showCreationMethodBadge =
post.creationMethod && post.creationMethod !== 'UNKNOWN';
const preview = useCallback(() => {
window.open(`/p/` + post.id + '?share=true', '_blank');
}, [post]);
@ -1044,6 +1047,14 @@ const CalendarItem: FC<{
!
</div>
)}
{showCreationMethodBadge && (
<div className="absolute -bottom-[4px] -right-[4px] z-20">
<CreationMethodBadge
creationMethod={post.creationMethod}
ringColor="var(--new-bgColor)"
/>
</div>
)}
<div
className={clsx(
'text-white text-[11px] max-h-[24px] h-[24px] min-h-[24px] w-full rounded-tr-[10px] rounded-tl-[10px] flex items-center justify-center gap-[10px] px-[5px] bg-btnPrimary'

View file

@ -0,0 +1,49 @@
import { FC } from 'react';
import clsx from 'clsx';
type CreationMethod = 'UNKNOWN' | 'WEB' | 'API' | 'MCP' | 'AUTOPOST';
interface Props {
creationMethod?: CreationMethod | string | null;
size?: 'xs' | 'sm' | 'md';
className?: string;
ringColor?: string;
}
const tooltipFor = (m: string) =>
m === 'AUTOPOST' ? 'Auto-posted by system' : `Created via ${m}`;
export const CreationMethodBadge: FC<Props> = ({
creationMethod,
size = 'xs',
className,
ringColor,
}) => {
if (!creationMethod || creationMethod === 'UNKNOWN') return null;
const sizeClasses =
size === 'xs'
? 'h-[12px] px-[4px] text-[7px]'
: size === 'md'
? 'h-[22px] px-[10px] text-[12px]'
: 'h-[18px] px-[8px] text-[10px]';
return (
<div
className={clsx(
'inline-flex items-center justify-center rounded-full text-white font-bold uppercase tracking-wide leading-none cursor-default',
sizeClasses,
creationMethod === 'WEB' && 'bg-[#6b7280]',
creationMethod === 'API' && 'bg-[#2563eb]',
creationMethod === 'MCP' && 'bg-[#9333ea]',
creationMethod === 'AUTOPOST' && 'bg-[#d97706]',
className
)}
style={ringColor ? { boxShadow: `0 0 0 2px ${ringColor}` } : undefined}
data-tooltip-id="tooltip"
data-tooltip-content={tooltipFor(creationMethod)}
>
{creationMethod}
</div>
);
};

View file

@ -33,6 +33,7 @@ import { SelectCustomer } from '@gitroom/frontend/components/launches/select.cus
import { CopilotPopup } from '@copilotkit/react-ui';
import { DummyCodeComponent } from '@gitroom/frontend/components/new-launch/dummy.code.component';
import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation';
import { CreationMethodBadge } from '@gitroom/frontend/components/launches/creation.method.badge';
import {
SettingsIcon,
ChevronDownIcon,
@ -447,8 +448,12 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
<div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col">
<div className="flex-1 flex">
<div className="flex flex-col flex-1 border-e border-newBorder">
<div className="bg-newBgColor h-[65px] rounded-s-[20px] !rounded-b-[0] flex items-center px-[20px] text-[20px] font-[600]">
<div className="bg-newBgColor h-[65px] rounded-s-[20px] !rounded-b-[0] flex items-center gap-[12px] px-[20px] text-[20px] font-[600]">
{t('create_post_title', 'Create Post')}
<CreationMethodBadge
creationMethod={existingData?.posts?.[0]?.creationMethod}
size="sm"
/>
</div>
<div className="flex-1 flex flex-col gap-[16px]">
<div

View file

@ -8,6 +8,7 @@ import { Toaster } from '@gitroom/react/toaster/toaster';
import { MantineWrapper } from '@gitroom/react/helpers/mantine.wrapper';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import { CopilotKit } from '@copilotkit/react-core';
import { ToolTip } from '@gitroom/frontend/components/layout/top.tip';
export const PreviewWrapper = ({ children }: { children: ReactNode }) => {
const fetch = useFetch();
const { backendUrl } = useVariables();
@ -30,6 +31,7 @@ export const PreviewWrapper = ({ children }: { children: ReactNode }) => {
>
<MantineWrapper>
<Toaster />
<ToolTip />
{children}
</MantineWrapper>
</CopilotKit>

View file

@ -25,6 +25,7 @@ const POST_ITEM_KEYS: Record<string, string> = {
integration: 'n',
intervalInDays: 'iv',
actualDate: 'ad',
creationMethod: 'cm',
};
const INTEGRATION_KEYS: Record<string, string> = {

View file

@ -223,7 +223,7 @@ If the tools return errors, you would need to rerun it with the right parameters
})),
},
],
});
}, 'MCP');
finalOutput.push(...output);
}

View file

@ -303,7 +303,7 @@ export class AutopostService {
},
],
})),
});
}, 'AUTOPOST');
}
async updateUrl(state: WorkflowChannelsState) {

View file

@ -1,7 +1,12 @@
import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service';
import { Injectable } from '@nestjs/common';
import { Post as PostBody } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto';
import { APPROVED_SUBMIT_FOR_ORDER, Post, State } from '@prisma/client';
import {
APPROVED_SUBMIT_FOR_ORDER,
CreationMethod,
Post,
State,
} 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 dayjs from 'dayjs';
@ -173,6 +178,7 @@ export class PostsRepository {
state: true,
intervalInDays: true,
group: true,
creationMethod: true,
tags: {
select: {
tag: true,
@ -260,6 +266,7 @@ export class PostsRepository {
releaseId: true,
state: true,
group: true,
creationMethod: true,
tags: {
select: {
tag: true,
@ -483,6 +490,7 @@ export class PostsRepository {
date: string,
body: PostBody,
tags: { value: string; label: string }[],
creationMethod: CreationMethod,
inter?: number
) {
const posts: Post[] = [];
@ -517,6 +525,7 @@ export class PostsRepository {
group: uuid,
intervalInDays: inter ? +inter : null,
approvedSubmitForOrder: APPROVED_SUBMIT_FOR_ORDER.NO,
...(type === 'create' ? { creationMethod } : {}),
...(state === 'update'
? {}
: {

View file

@ -7,7 +7,14 @@ import { PostsRepository } from '@gitroom/nestjs-libraries/database/prisma/posts
import { CreatePostDto } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto';
import dayjs from 'dayjs';
import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager';
import { Integration, Post, Media, From, State } from '@prisma/client';
import {
Integration,
Post,
Media,
From,
CreationMethod,
State,
} 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 { shuffle } from 'lodash';
@ -731,7 +738,11 @@ export class PostsService {
} catch (err) {}
}
async createPost(orgId: string, body: CreatePostDto): Promise<any[]> {
async createPost(
orgId: string,
body: CreatePostDto,
creationMethod: CreationMethod
): Promise<any[]> {
const postList = [];
for (const post of body.posts) {
const messages = (post.value || []).map((p) => p.content);
@ -750,6 +761,7 @@ export class PostsService {
body.type === 'now' ? dayjs().format('YYYY-MM-DDTHH:mm:00') : body.date,
post,
body.tags,
creationMethod,
body.inter
);
@ -881,43 +893,47 @@ export class PostsService {
const group = makeId(10);
const randomDate = findTime();
await this.createPost(orgId, {
type: 'draft',
date: randomDate,
order: '',
shortLink: false,
tags: [],
posts: [
{
group,
integration: {
id: integration.id,
},
settings: {
__type: integration.providerIdentifier as any,
title: '',
tags: [],
subreddit: [],
},
value: [
...toPost.list.map((l) => ({
id: '',
content: l.post,
delay: 0,
image: [],
})),
{
id: '',
delay: 0,
content: `Check out the full story here:\n${
body.postId || body.url
}`,
image: [],
await this.createPost(
orgId,
{
type: 'draft',
date: randomDate,
order: '',
shortLink: false,
tags: [],
posts: [
{
group,
integration: {
id: integration.id,
},
],
},
],
});
settings: {
__type: integration.providerIdentifier as any,
title: '',
tags: [],
subreddit: [],
},
value: [
...toPost.list.map((l) => ({
id: '',
content: l.post,
delay: 0,
image: [],
})),
{
id: '',
delay: 0,
content: `Check out the full story here:\n${
body.postId || body.url
}`,
image: [],
},
],
},
],
},
'WEB'
);
}
}
}

View file

@ -409,6 +409,7 @@ model Post {
submittedForOrderId String?
submittedForOrganizationId String?
approvedSubmitForOrder APPROVED_SUBMIT_FOR_ORDER @default(NO)
creationMethod CreationMethod @default(UNKNOWN)
lastMessageId String?
intervalInDays Int?
error String?
@ -436,6 +437,7 @@ model Post {
@@index([submittedForOrderId])
@@index([intervalInDays])
@@index([approvedSubmitForOrder])
@@index([creationMethod])
@@index([lastMessageId])
@@index([createdAt])
@@index([updatedAt])
@ -938,6 +940,14 @@ enum APPROVED_SUBMIT_FOR_ORDER {
YES
}
enum CreationMethod {
UNKNOWN
WEB
MCP
API
AUTOPOST
}
enum ShortLinkPreference {
ASK
YES