feat: agent-media sso

This commit is contained in:
Nevo David 2026-03-08 22:27:06 +07:00
parent 634bee898a
commit f624321b43
3 changed files with 55 additions and 7 deletions

View file

@ -9,6 +9,7 @@ import {
Res,
} from '@nestjs/common';
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
import { sign } from 'jsonwebtoken';
import { Organization, User } from '@prisma/client';
import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service';
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
@ -42,6 +43,23 @@ export class UsersController {
private _userService: UsersService,
private _trackService: TrackService
) {}
@Get('/agent-media-sso')
async getAgentMediaSsoUrl(
@GetUserFromRequest() user: User,
@GetOrgFromRequest() organization: Organization
) {
if (!process.env.AGENT_MEDIA_SSO_KEY) {
throw new HttpException('Agent Media SSO is not configured', 400);
}
const token = sign(
{ id: organization.id, displayName: organization.name },
process.env.AGENT_MEDIA_SSO_KEY
);
return { url: `https://agent-media.ai/sso/${token}` };
}
@Get('/self')
async getSelf(
@GetUserFromRequest() user: User,

View file

@ -1,9 +1,10 @@
'use client';
import { FC, ReactNode } from 'react';
import { FC, ReactNode, useCallback } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { MenuItem } from '@gitroom/frontend/components/new-layout/menu-item';
interface MenuItemInterface {
@ -13,11 +14,25 @@ interface MenuItemInterface {
role?: string[];
hide?: boolean;
requireBilling?: boolean;
onClick?: () => void;
}
export const useMenuItem = () => {
const { isGeneral } = useVariables();
const t = useT();
const fetch = useFetch();
const handleAgentMediaClick = useCallback(async () => {
try {
const response = await fetch('/user/agent-media-sso');
const data = await response.json();
if (data.url) {
window.open(data.url, '_blank');
}
} catch (e) {
// ignore
}
}, [fetch]);
const firstMenu = [
{
@ -174,9 +189,10 @@ export const useMenuItem = () => {
</g>
</svg>
),
path: 'https://agent-media.ai',
path: '#',
role: ['ADMIN', 'SUPERADMIN', 'USER'],
requireBilling: true,
onClick: handleAgentMediaClick,
},
{
name: t('affiliate', 'Affiliate'),
@ -331,6 +347,7 @@ export const TopMenu: FC = () => {
label={item.name}
icon={item.icon}
key={item.name}
onClick={item.onClick}
/>
))
}
@ -358,6 +375,7 @@ export const TopMenu: FC = () => {
label={item.name}
icon={item.icon}
key={item.name}
onClick={item.onClick}
/>
))}
</div>

View file

@ -4,23 +4,35 @@ import { usePathname } from 'next/navigation';
import clsx from 'clsx';
import Link from 'next/link';
export const MenuItem: FC<{ label: string; icon: ReactNode; path: string }> = ({
export const MenuItem: FC<{ label: string; icon: ReactNode; path: string; onClick?: () => void }> = ({
label,
icon,
path,
onClick,
}) => {
const currentPath = usePathname();
const isActive = currentPath.indexOf(path) === 0;
const className = clsx(
'w-full minCustom:h-[54px] custom:h-[30px] py-[8px] px-[6px] gap-[4px] flex flex-col custom:flex-row text-[10px] font-[600] items-center minCustom:justify-center rounded-[12px] hover:text-textItemFocused hover:bg-boxFocused',
isActive ? 'text-textItemFocused bg-boxFocused' : 'text-textItemBlur'
);
if (onClick) {
return (
<button onClick={onClick} className={className}>
<div className="custom:hidden">{icon}</div>
<div className="text-[10px]">{label}</div>
</button>
);
}
return (
<Link
prefetch={true}
href={path}
{...path.indexOf('http') === 0 && { target: '_blank' }}
className={clsx(
'w-full minCustom:h-[54px] custom:h-[30px] py-[8px] px-[6px] gap-[4px] flex flex-col custom:flex-row text-[10px] font-[600] items-center minCustom:justify-center rounded-[12px] hover:text-textItemFocused hover:bg-boxFocused',
isActive ? 'text-textItemFocused bg-boxFocused' : 'text-textItemBlur'
)}
className={className}
>
<div className="custom:hidden">{icon}</div>
<div className="text-[10px]">{label}</div>