diff --git a/public/locales/en/sources.json b/public/locales/en/sources.json index b1af64be5..3a19b7d6c 100644 --- a/public/locales/en/sources.json +++ b/public/locales/en/sources.json @@ -4,5 +4,6 @@ "sourceCardButton": "Access the source", "sourceCreateCTAButton": "Create source", "sourcesCreateSuccess": "Source created successfully", - "sourcesCreateError": "Error while creating the source" + "sourcesCreateError": "Error while creating the source", + "sourceInvalid": "The font is no longer available" } diff --git a/public/locales/pt/sources.json b/public/locales/pt/sources.json index dde0e8c2c..8bc75080a 100644 --- a/public/locales/pt/sources.json +++ b/public/locales/pt/sources.json @@ -4,5 +4,6 @@ "sourceCardButton": "Acesse a fonte", "sourceCreateCTAButton": "Incluir fonte", "sourcesCreateSuccess": "Fonte criado com sucesso", - "sourcesCreateError": "Erro ao criar a fonte" + "sourcesCreateError": "Erro ao criar a fonte", + "sourceInvalid": "A fonte não está mais disponível" } diff --git a/server/claim/claim.controller.ts b/server/claim/claim.controller.ts index 86905a8d6..c81ca7c41 100644 --- a/server/claim/claim.controller.ts +++ b/server/claim/claim.controller.ts @@ -142,7 +142,7 @@ export class ClaimController { : path, }; } catch (error) { - return error; + throw error; } } diff --git a/server/source/source.controller.ts b/server/source/source.controller.ts index f0c0b722a..aa8dad11e 100644 --- a/server/source/source.controller.ts +++ b/server/source/source.controller.ts @@ -10,6 +10,7 @@ import { Query, Req, Res, + UseGuards, } from "@nestjs/common"; import { SourceService } from "./source.service"; import { ApiTags } from "@nestjs/swagger"; @@ -27,6 +28,8 @@ import { HistoryService } from "../history/history.service"; import { ReviewTaskService } from "../review-task/review-task.service"; import { ClaimReviewService } from "../claim-review/claim-review.service"; import { FeatureFlagService } from "../feature-flag/feature-flag.service"; +import { AbilitiesGuard } from "../auth/ability/abilities.guard"; +import { CheckAbilities, RegularUserAbility } from "../auth/ability/ability.decorator"; @Controller(":namespace?") export class SourceController { @@ -103,6 +106,25 @@ export class SourceController { ); } + @ApiTags("source") + @Get("/check-source") + @UseGuards(AbilitiesGuard) + @CheckAbilities(new RegularUserAbility()) + async checkSource(@Query() query) { + try { + const result = await fetch(query.source, { + method: "GET", + }); + if (result.ok) { + return { status: 200, message: "Valid Source"}; + } else { + return { status: 404, message: "Invalid Source"}; + } + } catch (error) { + return { status: 500, message: "Internal Error"}; + } + } + @IsPublic() @ApiTags("source") @Get("api/source") diff --git a/server/source/source.module.ts b/server/source/source.module.ts index fea38bcac..8e8af4832 100644 --- a/server/source/source.module.ts +++ b/server/source/source.module.ts @@ -10,6 +10,7 @@ import { HistoryModule } from "../history/history.module"; import { ClaimReviewModule } from "../claim-review/claim-review.module"; import { ReviewTaskModule } from "../review-task/review-task.module"; import { FeatureFlagModule } from "../feature-flag/feature-flag.module"; +import { AbilityModule } from "../auth/ability/ability.module"; const SourceModel = MongooseModule.forFeature([ { @@ -28,6 +29,7 @@ const SourceModel = MongooseModule.forFeature([ forwardRef(() => ClaimReviewModule), ReviewTaskModule, FeatureFlagModule, + AbilityModule ], providers: [SourceService], exports: [SourceService], diff --git a/server/source/source.service.ts b/server/source/source.service.ts index 8b72fe2fe..1e1d1a597 100644 --- a/server/source/source.service.ts +++ b/server/source/source.service.ts @@ -30,16 +30,23 @@ export class SourceService { } async create(data) { - if (data?.targetId) { - data.targetId = [Types.ObjectId(data.targetId)]; - } - if (data?.props?.date) { - data.props.date = new Date(data.props.date); + try { + await this.checkSource(data.href); + + if (data?.targetId) { + data.targetId = [Types.ObjectId(data.targetId)]; + } + if (data?.props?.date) { + data.props.date = new Date(data.props.date); + } + data.data_hash = md5(data.href); + data.user = Types.ObjectId(data.user); + + return await new this.SourceModel(data).save(); + + } catch (error) { + throw error; } - data.data_hash = md5(data.href); - data.user = Types.ObjectId(data.user); - //TODO: don't create duplicate sources in one claim review task - return await new this.SourceModel(data).save(); } async updateTargetId(sourceId, newTargetId) { @@ -99,4 +106,15 @@ export class SourceService { "props.classification": { $exists: true }, }); } + + async checkSource(source) { + const result = await fetch(source, { + method: "GET" + }); + + if (!result.ok) { + throw new Error("Invalid Source"); + }; + return true; + } } diff --git a/src/api/sourceApi.ts b/src/api/sourceApi.ts index 235f965c1..118e006cc 100644 --- a/src/api/sourceApi.ts +++ b/src/api/sourceApi.ts @@ -93,11 +93,22 @@ const getById = (id, t, params = {}) => { }); }; + const checkSource = (source) => { + return axios.get('/check-source', {params: {source}}) + .then((response) => { + return response.data; + }) + .catch(() => { + message.error("error"); + }); + } + const SourceApi = { getByTargetId, createSource, get, getById, + checkSource, }; export default SourceApi; diff --git a/src/components/Claim/CreateClaim/BaseClaimForm.tsx b/src/components/Claim/CreateClaim/BaseClaimForm.tsx index a7cdd8483..f1a65d625 100644 --- a/src/components/Claim/CreateClaim/BaseClaimForm.tsx +++ b/src/components/Claim/CreateClaim/BaseClaimForm.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Checkbox, Form, Row } from "antd"; +import { Checkbox, Form, Row, message } from "antd"; import moment from "moment"; import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; @@ -46,7 +46,8 @@ const BaseClaimForm = ({ setDisableSubmit(!hasRecaptcha); }; - const onFinish = () => { + const onFinish = async () => { + const values = { title, date, diff --git a/src/components/Source/ClaimSourceListItem.tsx b/src/components/Source/ClaimSourceListItem.tsx index 7aea1abf6..1fddb5f7e 100644 --- a/src/components/Source/ClaimSourceListItem.tsx +++ b/src/components/Source/ClaimSourceListItem.tsx @@ -1,9 +1,31 @@ -import { Col, List } from "antd"; - +import { Col, List, message } from "antd"; import React from "react"; +import { useState } from "react"; +import SourceApi from "../../api/sourceApi"; +import { useTranslation } from "next-i18next"; const ClaimSourceListItem = ({ source, index }) => { const { href } = source; + const [linkValid, setLinkValid] = useState(null); + const { t } = useTranslation(); + + const checkLink = async (url) => { + + try { + const request = await SourceApi.checkSource(url); + + if (request?.status === 200) { + setLinkValid(true); + window.open(url, "_blank", "noopener noreferrer"); + } else { + setLinkValid(false); + message.error(t("sources:sourceInvalid")); + } + } catch (error) { + setLinkValid(false); + message.error(t("sources:sourceInvalid")); + } + }; return (