Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: profile reports on mod #4643

Merged
merged 6 commits into from
Feb 14, 2024
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
12 changes: 12 additions & 0 deletions apps/api/src/db/clickhouse.sql
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ CREATE TABLE "trusted_reports" (
actor LowCardinality(String),
publication_id LowCardinality(String),
reason String,
resolved Boolean DEFAULT 0,
created DateTime DEFAULT now()
) ENGINE = MergeTree
ORDER BY created;

-- Reports
CREATE TABLE "reports" (
id UUID DEFAULT generateUUIDv4(),
actor LowCardinality(String),
publication_id LowCardinality(String),
reason String,
resolved Boolean DEFAULT 0,
created DateTime DEFAULT now()
) ENGINE = MergeTree
ORDER BY created;
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@ import { Errors } from '@hey/data/errors';
import logger from '@hey/lib/logger';
import createClickhouseClient from 'src/lib/createClickhouseClient';

const heyTrustedReports = async (
const heyReports = async (
limit: number,
offset: number
offset: number,
isTrusted = false
): Promise<any[]> => {
if (limit > 500) {
throw new Error(Errors.Limit500);
}

try {
const client = createClickhouseClient();
const table = isTrusted ? 'trusted_reports' : 'reports';
const rows = await client.query({
format: 'JSONEachRow',
query: `
SELECT
publication_id AS id,
count(*) as count
FROM trusted_reports
FROM ${table}
WHERE resolved = 0
GROUP BY publication_id
ORDER BY count DESC
Expand All @@ -30,12 +32,12 @@ const heyTrustedReports = async (
const result = await rows.json<Array<{ id: string }>>();

const ids = result.map((r) => r.id);
logger.info(`[Hey] Trusted reports: ${ids.length} ids`);
logger.info(`[Hey] Reports: ${ids.length} ids`);

return ids;
} catch {
return [];
}
};

export default heyTrustedReports;
export default heyReports;
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { boolean, object, string } from 'zod';
type ExtensionRequest = {
id: string;
looksGood: boolean;
trusted: boolean;
};

const validationSchema = object({
id: string(),
looksGood: boolean()
looksGood: boolean(),
trusted: boolean()
});

export const post: Handler = async (req, res) => {
Expand All @@ -34,25 +36,24 @@ export const post: Handler = async (req, res) => {
return notAllowed(res);
}

const { id, looksGood } = body as ExtensionRequest;
const { id, looksGood, trusted } = body as ExtensionRequest;
const table = trusted ? 'trusted_reports' : 'reports';

try {
const client = createClickhouseClient();

if (looksGood) {
await client.command({
query: `DELETE FROM trusted_reports WHERE publication_id = '${id}';`
query: `DELETE FROM ${table} WHERE publication_id = '${id}';`
});
logger.info('Deleted report from trusted reports');

logger.info('Deleted report from reports');
} else {
await client.command({
query: `
ALTER TABLE trusted_reports
UPDATE resolved = 1
WHERE publication_id = '${id}';
`
query: `ALTER TABLE ${table} UPDATE resolved = 1 WHERE publication_id = '${id}';`
});
logger.info('Marked trusted report as resolved');

logger.info('Marked report as resolved');
}

return res.status(200).json({ success: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,29 @@ import createClickhouseClient from 'src/lib/createClickhouseClient';
import { noBody } from 'src/lib/responses';

export const get: Handler = async (req, res) => {
const { id } = req.query;
const { id, trusted } = req.query;

if (!id) {
return noBody(res);
}

const table = trusted === 'true' ? 'trusted_reports' : 'reports';

try {
const client = createClickhouseClient();
const rows = await client.query({
format: 'JSONEachRow',
query: `
SELECT
reason,
count(*) as count
FROM trusted_reports
SELECT reason, count(*) as count
FROM ${table}
WHERE publication_id = '${id}'
GROUP BY reason
ORDER BY count DESC;
`
});

const result = await rows.json<
Array<{
count: string;
reason: string;
}>
>();
logger.info(`Trusted report details fetched for ${id}`);
const result = await rows.json<Array<{ count: string; reason: string }>>();
logger.info(`Report details fetched for ${id}`);

return res.status(200).json({
result: result.map((row) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import type { Handler } from 'express';

import catchedError from 'src/lib/catchedError';
import heyTrustedReports from 'src/lib/feeds/providers/hey/algorithms/heyTrustedReports';
import heyReports from 'src/lib/feeds/providers/hey/algorithms/heyReports';

export const get: Handler = async (req, res) => {
const limit = (parseInt(req.query?.limit as string) || 50) as number;
const offset = (parseInt(req.query?.offset as string) || 0) as number;
const trusted = req.query?.trusted as string;

const isTrusted = trusted === 'true';

try {
return res
.status(200)
.json({ ids: await heyTrustedReports(limit, offset), success: true });
return res.status(200).json({
ids: await heyReports(limit, offset, isTrusted),
success: true
});
} catch (error) {
return catchedError(res, error);
}
Expand Down
79 changes: 79 additions & 0 deletions apps/api/src/routes/misc/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Handler } from 'express';

import logger from '@hey/lib/logger';
import parseJwt from '@hey/lib/parseJwt';
import catchedError from 'src/lib/catchedError';
import createClickhouseClient from 'src/lib/createClickhouseClient';
import validateLensAccount from 'src/lib/middlewares/validateLensAccount';
import { invalidBody, noBody, notAllowed } from 'src/lib/responses';
import { object, string } from 'zod';

type ExtensionRequest = {
id: string;
reason: string;
};

const validationSchema = object({
id: string(),
reason: string()
});

export const post: Handler = async (req, res) => {
const { body } = req;

if (!body) {
return noBody(res);
}

const accessToken = req.headers['x-access-token'] as string;
const validation = validationSchema.safeParse(body);

if (!validation.success) {
return invalidBody(res);
}

if (!(await validateLensAccount(req))) {
return notAllowed(res);
}

const { id, reason } = body as ExtensionRequest;

try {
const payload = parseJwt(accessToken);
const actor = payload.id;

const client = createClickhouseClient();

// Do not allow to report the same publication twice
const rows = await client.query({
format: 'JSONEachRow',
query: `
SELECT * FROM reports
WHERE publication_id = '${id}'
AND actor = '${actor}'
LIMIT 1;
`
});

const reports = await rows.json<Array<any>>();

if (reports.length > 0) {
return res.status(200).json({
message: 'You already reported this publication!',
success: false
});
}

const result = await client.insert({
format: 'JSONEachRow',
table: 'reports',
values: [{ actor, publication_id: id, reason }]
});

logger.info(`Reported ${id} by profile: ${actor}`);

return res.status(200).json({ id: result.query_id, success: true });
} catch (error) {
return catchedError(res, error);
}
};
24 changes: 14 additions & 10 deletions apps/web/src/components/Mod/Actions.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import GardenerActions from '@components/Publication/Actions/GardenerActions';
import TrustedProfilesActions from '@components/Publication/Actions/TrustedProfilesActions';
import { ModFeedType } from '@hey/data/enums';
import { type FC, useState } from 'react';
import { useFeatureFlagsStore } from 'src/store/persisted/useFeatureFlagsStore';

import TrustedReportDetails from './TrustedReportDetails';
import ReportDetails from './ReportDetails';

interface ActionsProps {
hideTrustedReport?: boolean;
publicationId: string;
type?: ModFeedType.REPORTS | ModFeedType.TRUSTED_REPORTS;
}

const Actions: FC<ActionsProps> = ({
hideTrustedReport = false,
publicationId
}) => {
const Actions: FC<ActionsProps> = ({ publicationId, type }) => {
const [expanded, setExpanded] = useState(true);
const trusted = useFeatureFlagsStore((state) => state.trusted);
const gardenerMode = useFeatureFlagsStore((state) => state.gardenerMode);

const isTrustedReport = type === ModFeedType.TRUSTED_REPORTS;
const isNormalReport = type === ModFeedType.REPORTS;

if (!expanded) {
return null;
}
Expand All @@ -30,18 +31,21 @@ const Actions: FC<ActionsProps> = ({
<div className="m-5 space-y-2">
<b>Gardener actions</b>
<GardenerActions
ableToRemoveReport={hideTrustedReport}
className="mt-3 max-w-md"
publicationId={publicationId}
setExpanded={setExpanded}
type={type}
/>
</div>
{hideTrustedReport && (
<TrustedReportDetails publicationId={publicationId} />
{(isTrustedReport || isNormalReport) && (
<ReportDetails
isTrustedReport={isTrustedReport}
publicationId={publicationId}
/>
)}
</>
)}
{trusted && !hideTrustedReport && (
{trusted && !isTrustedReport && !isNormalReport && (
<>
<div className="divider" />
<div className="m-5">
Expand Down
7 changes: 7 additions & 0 deletions apps/web/src/components/Mod/FeedType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Dispatch, FC, SetStateAction } from 'react';

import {
ClockIcon,
FlagIcon,
ShieldCheckIcon,
UsersIcon
} from '@heroicons/react/24/outline';
Expand All @@ -28,6 +29,12 @@ const FeedType: FC<FeedTypeProps> = ({ feedType, setFeedType }) => {
name="Trusted Reports"
onClick={() => setFeedType(ModFeedType.TRUSTED_REPORTS)}
/>
<TabButton
active={feedType === ModFeedType.REPORTS}
icon={<FlagIcon className="size-4" />}
name="Reports"
onClick={() => setFeedType(ModFeedType.REPORTS)}
/>
<TabButton
active={feedType === ModFeedType.PROFILES}
icon={<UsersIcon className="size-4" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { HEY_API_URL } from '@hey/data/constants';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

interface TrustedReportDetailsProps {
interface ReportDetailsProps {
isTrustedReport: boolean;
publicationId: string;
}

const TrustedReportDetails: FC<TrustedReportDetailsProps> = ({
const ReportDetails: FC<ReportDetailsProps> = ({
isTrustedReport,
publicationId
}) => {
const fetchTrustedReportDetails = async (): Promise<
{
count: number;
reason: string;
}[]
{ count: number; reason: string }[]
> => {
try {
const response = await axios.get(`${HEY_API_URL}/trusted/reportDetails`, {
params: { id: publicationId }
});
const response = await axios.get(
`${HEY_API_URL}/gardener/reportDetails`,
{ params: { id: publicationId, trusted: isTrustedReport } }
);

return response.data.result;
} catch {
Expand Down Expand Up @@ -51,4 +51,4 @@ const TrustedReportDetails: FC<TrustedReportDetailsProps> = ({
);
};

export default TrustedReportDetails;
export default ReportDetails;
Loading
Loading