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
6 changes: 6 additions & 0 deletions chart/templates/microblog-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ spec:
value: {{ quote .Values.microblogService.deployment.container.env.REDIS_SERVICE_ADDRESS }}
- name: USER_AUTH_SERVICE_ADDRESS
value: {{ quote .Values.microblogService.deployment.container.env.USER_AUTH_SERVICE_ADDRESS }}
- name: RAG_SERVICE_ADDRESS
value: {{ quote .Values.microblogService.deployment.container.env.RAG_SERVICE_ADDRESS }}
- name: RAG_SERVICE_PORT
value: {{ quote .Values.microblogService.deployment.container.env.RAG_SERVICE_PORT }}
- name: RAG_SERVICE_ENABLED
value: {{ .Values.ragService.enabled | quote }}
- name: OPENTRACING_JAEGER_ENABLED
value: {{ quote .Values.microblogService.deployment.container.env.OPENTRACING_JAEGER_ENABLED }}
- name: JAEGER_SERVICE_NAME
Expand Down
3 changes: 3 additions & 0 deletions chart/templates/rag-service-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ data:
USE_DATA_POISONING_DETECTION: {{ quote .Values.ragService.deployment.container.env.USE_DATA_POISONING_DETECTION }}
DATA_POISONING_DETECTION_STRATEGY: {{ quote .Values.ragService.deployment.container.env.DATA_POISONING_DETECTION_STRATEGY }}
LABEL_CONSISTENCY_DETECTION_DECISION_VARIANT: {{ quote .Values.ragService.deployment.container.env.LABEL_CONSISTENCY_DETECTION_DECISION_VARIANT }}
{{- with .Values.ragService.deployment.container.env.LANGDOCK_API_KEY }}
LANGDOCK_API_KEY: {{ quote . }}
{{- end }}
{{- end }}
2 changes: 2 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ microblogService:
OPENTRACING_JAEGER_ENABLED: false
REDIS_SERVICE_ADDRESS: unguard-redis
USER_AUTH_SERVICE_ADDRESS: unguard-user-auth-service
RAG_SERVICE_ADDRESS: unguard-rag-service
RAG_SERVICE_PORT: 8000

# Status Service
statusService:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NextResponse } from 'next/server';

import { addSpamPredictionUserRatingDownvote } from '@/services/api/SpamPredictionVotesService';

/**
* @swagger
* /ui/api/post/{postId}/spam-prediction-user-rating/downvote:
* post:
* description: Downvote the Spam Prediction.
*/

export type PostParams = {
postId: string;
};

export async function POST(req: Request, { params }: { params: Promise<PostParams> }): Promise<NextResponse> {
const { postId } = await params;
const res = await addSpamPredictionUserRatingDownvote(postId);

return NextResponse.json(res.data, { status: res.status });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NextResponse } from 'next/server';

import { fetchSpamPredictionUserRating } from '@/services/api/SpamPredictionVotesService';
import { PostParams } from '@/app/api/like/[postId]/route';

/**
* @swagger
* /ui/api/post/{postId}/spam-prediction-user-rating:
* get:
* description: Get the spam prediction user ratings for a post by its ID.
*/

export async function GET(req: Request, { params }: { params: Promise<PostParams> }): Promise<NextResponse> {
const { postId } = await params;
const res = await fetchSpamPredictionUserRating(postId);

return NextResponse.json(res.data, { status: res.status });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NextResponse } from 'next/server';

import { addSpamPredictionUserRatingUpvote } from '@/services/api/SpamPredictionVotesService';

/**
* @swagger
* /ui/api/post/{postId}/spam-prediction-user-rating/upvote:
* post:
* description: Handle spam prediction upvote action
*/

export type PostParams = {
postId: string;
};

export async function POST(req: Request, { params }: { params: Promise<PostParams> }): Promise<NextResponse> {
const { postId } = await params;
const res = await addSpamPredictionUserRatingUpvote(postId);

return NextResponse.json(res.data, { status: res.status });
}
1 change: 1 addition & 0 deletions src/frontend-nextjs/app/api/post/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createNewPost } from '@/services/api/CreatePostService';
export async function POST(request: Request): Promise<NextResponse> {
const body = await request.json();
let header = request.headers.get('header');

if (header) {
header = Buffer.from(header, 'latin1').toString('utf-8');
}
Expand Down
1 change: 1 addition & 0 deletions src/frontend-nextjs/app/post/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function SinglePost() {
<PostComponent
body={postData.body}
imageUrl={postData.imageUrl}
isSpamPredictedLabel={postData.isSpamPredictedLabel}
postId={postData.postId}
timestamp={postData.timestamp}
username={postData.username}
Expand Down
1 change: 1 addition & 0 deletions src/frontend-nextjs/components/SwaggerUIReact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const HideTryItOutPlugin = () => ({

function ReactSwagger({ spec }: Props) {
const specWithoutServers = { ...spec };

delete specWithoutServers.servers;

return <SwaggerUI spec={specWithoutServers} plugins={[HideAuthorizePlugin, HideTryItOutPlugin]} />;
Expand Down
8 changes: 7 additions & 1 deletion src/frontend-nextjs/components/Timeline/Post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import { BASE_PATH } from '@/constants';
import { useCheckLogin } from '@/hooks/queries/useCheckLogin';
import { LikeButton } from '@/components/Timeline/LikeButton';
import { ErrorCard } from '@/components/ErrorCard';
import { PostSpamPrediction } from '@/components/Timeline/PostSpamPrediction';

export interface PostProps {
username: string;
timestamp: string;
body: string;
imageUrl?: string;
postId: string;
isSpamPredictedLabel?: boolean;
}

export function Post(props: PostProps) {
Expand Down Expand Up @@ -69,7 +71,11 @@ export function Post(props: PostProps) {
)}
<p>{props.body}</p>
</CardBody>
<CardFooter className='gap-3 justify-end px-3'>
<CardFooter className='gap-3 justify-between px-3'>
<div className='flex items-center'>
<PostSpamPrediction isSpamPredictedLabel={props.isSpamPredictedLabel} postId={props.postId} />
</div>

{isLoggedIn && (
<div className='flex gap-1'>
<ErrorBoundary fallbackRender={(props) => <ErrorCard message={props.error.message} />}>
Expand Down
46 changes: 46 additions & 0 deletions src/frontend-nextjs/components/Timeline/PostSpamPrediction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import React from 'react';
import { Alert } from '@heroui/react';
import { ErrorBoundary } from 'react-error-boundary';

import { SpamPredictionUserRating } from '@/components/Timeline/SpamPredictionUserRating';
import { useCheckLogin } from '@/hooks/queries/useCheckLogin';
import { ErrorCard } from '@/components/ErrorCard';

export interface PostSpamPredictionProps {
isSpamPredictedLabel?: boolean | null;
postId: string;
}

export function PostSpamPrediction(props: Readonly<PostSpamPredictionProps>) {
const { isLoggedIn } = useCheckLogin();

if (props.isSpamPredictedLabel == null) return null;

const color = props.isSpamPredictedLabel ? 'danger' : 'primary';

return (
<div key={color} className='w-full flex items-center my-3'>
<div className='w-full'>
<Alert color={color}>
<div className='flex w-full items-center justify-between gap-3'>
<div className='font-semibold'>
{props.isSpamPredictedLabel ? 'Potential Spam Detected' : 'No Spam Detected'}
</div>
{isLoggedIn && (
<ErrorBoundary
fallbackRender={(errorProps) => <ErrorCard message={errorProps.error.message} />}
>
<SpamPredictionUserRating
isSpamPredictedLabel={props.isSpamPredictedLabel}
postId={props.postId}
/>
</ErrorBoundary>
)}
</div>
</Alert>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client';

import React from 'react';
import { BsHandThumbsDown, BsHandThumbsDownFill, BsHandThumbsUp, BsHandThumbsUpFill } from 'react-icons/bs';
import { Button, Spinner } from '@heroui/react';

import { useSpamPredictionUserRating } from '@/hooks/queries/useSpamPredictionUserRating';
import { useRateSpamPrediction } from '@/hooks/mutations/useRateSpamPrediction';

export interface SpamPredictionUserRatingProps {
isSpamPredictedLabel?: boolean | null;
postId: string;
}

export function SpamPredictionUserRating(props: Readonly<SpamPredictionUserRatingProps>) {
const { data: spamPredictionUserRatingData, isLoading } = useSpamPredictionUserRating(props.postId);
const { handleSpamPredictionUpvote, handleSpamPredictionDownvote } = useRateSpamPrediction(props.postId);

if (isLoading) {
return <Spinner />;
}

return (
<div>
<Button
className='bg-transparent text-default-600 px-2 min-w-0 gap-1'
name='upvoteSpamRating'
onPress={() => handleSpamPredictionUpvote()}
>
<p>{spamPredictionUserRatingData?.spamPredictionUserUpvotes}</p>
{spamPredictionUserRatingData?.isUpvotedByUser ? <BsHandThumbsUpFill /> : <BsHandThumbsUp />}
</Button>

<Button
className='bg-transparent text-default-600 px-2 min-w-0 gap-1'
name='downvoteSpamRating'
onPress={() => handleSpamPredictionDownvote()}
>
<p>{spamPredictionUserRatingData?.spamPredictionUserDownvotes}</p>
{spamPredictionUserRatingData?.isDownvotedByUser ? <BsHandThumbsDownFill /> : <BsHandThumbsDown />}
</Button>
</div>
);
}
4 changes: 2 additions & 2 deletions src/frontend-nextjs/components/Timeline/Timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client';
import { Card, Spacer, Spinner } from '@heroui/react';

import { Post } from '@/components/Timeline/Post';
import { PostProps } from '@/components/Timeline/Post';
import { Post, PostProps } from '@/components/Timeline/Post';

interface TimelineProps {
posts: PostProps[] | undefined;
Expand All @@ -28,6 +27,7 @@ export function Timeline({ posts, isLoading }: TimelineProps) {
<Post
body={post.body}
imageUrl={post.imageUrl}
isSpamPredictedLabel={post.isSpamPredictedLabel}
postId={post.postId}
timestamp={post.timestamp}
username={post.username}
Expand Down
1 change: 1 addition & 0 deletions src/frontend-nextjs/enums/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export enum QUERY_KEYS {
ad_manager = 'ad-manager',
ad_list = 'ad-list',
deployment_health = 'deployment-health',
spam_prediction_user_rating = 'spam-prediction-user-rating',
}
27 changes: 27 additions & 0 deletions src/frontend-nextjs/hooks/mutations/useRateSpamPrediction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { handleSpamPredictionDownvote, handleSpamPredictionUpvote } from '@/services/SpamPredictionVotingService';
import { QUERY_KEYS } from '@/enums/queryKeys';

export function useRateSpamPrediction(postId: string) {
const queryClient = useQueryClient();

const handleUpvoteMutation = useMutation({
mutationFn: () => handleSpamPredictionUpvote(postId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.spam_prediction_user_rating, postId] });
},
});

const handleDownvoteMutation = useMutation({
mutationFn: () => handleSpamPredictionDownvote(postId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.spam_prediction_user_rating, postId] });
},
});

return {
handleSpamPredictionUpvote: handleUpvoteMutation.mutate,
handleSpamPredictionDownvote: handleDownvoteMutation.mutate,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from 'path';

import { useQuery } from '@tanstack/react-query';

import { QUERY_KEYS } from '@/enums/queryKeys';
import { BASE_PATH } from '@/constants';

type SpamPredictionUserRating = {
spamPredictionUserUpvotes: number;
spamPredictionUserDownvotes: number;
isUpvotedByUser?: boolean;
isDownvotedByUser?: boolean;
};

async function fetchSpamPredictionUserRatings(postId: string): Promise<SpamPredictionUserRating> {
const res = await fetch(path.join(BASE_PATH, `/api/post/${postId}/spam-prediction-user-rating/`));

if (!res.ok) {
throw new Error('Failed to fetch spam prediction user ratings');
}

return await res.json();
}

export function useSpamPredictionUserRating(postId: string) {
return useQuery({
queryKey: [QUERY_KEYS.spam_prediction_user_rating, postId],
queryFn: () => fetchSpamPredictionUserRatings(postId),
throwOnError: true,
});
}
17 changes: 17 additions & 0 deletions src/frontend-nextjs/services/SpamPredictionVotingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from 'path';

import { BASE_PATH } from '@/constants';

export async function handleSpamPredictionDownvote(postId: string): Promise<Response> {
return await fetch(path.join(BASE_PATH, `/api/post/${postId}/spam-prediction-user-rating/downvote/`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
}

export async function handleSpamPredictionUpvote(postId: string): Promise<Response> {
return await fetch(path.join(BASE_PATH, `/api/post/${postId}/spam-prediction-user-rating/upvote/`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
}
47 changes: 47 additions & 0 deletions src/frontend-nextjs/services/api/SpamPredictionVotesService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AxiosHeaders, AxiosRequestConfig } from 'axios';

import { getMicroblogApi } from '@/axios';
import { getJwtFromCookie } from '@/services/api/AuthService';

async function withJwtCookie(): Promise<AxiosRequestConfig> {
const jwt = await getJwtFromCookie();

const headers = new AxiosHeaders();

headers.set('Cookie', `jwt=${jwt}`);

return { headers };
}

export async function fetchSpamPredictionUserRating(postId: string): Promise<any> {
return await getMicroblogApi()
.get(`/spam-prediction-user-rating/${postId}`, await withJwtCookie())
.then((response) => {
return response;
})
.catch((error) => {
return error.response;
});
}

export async function addSpamPredictionUserRatingUpvote(postId: string): Promise<any> {
return await getMicroblogApi()
.post(`/spam-prediction-user-rating/${postId}/upvote`, {}, await withJwtCookie())
.then((response) => {
return response;
})
.catch((error) => {
return error.response;
});
}

export async function addSpamPredictionUserRatingDownvote(postId: string): Promise<any> {
return await getMicroblogApi()
.post(`/spam-prediction-user-rating/${postId}/downvote`, {}, await withJwtCookie())
.then((response) => {
return response;
})
.catch((error) => {
return error.response;
});
}
2 changes: 1 addition & 1 deletion src/microblog-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM gradle:6.9.1-jdk11 as builder
FROM gradle:6.9.1-jdk11 AS builder

COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
Expand Down
Loading
Loading