From 0ee1b4e22c88db216a1464e917eeacbda35f7f57 Mon Sep 17 00:00:00 2001 From: carrick Date: Tue, 13 Feb 2024 08:09:16 +0900 Subject: [PATCH 1/7] feat: check trust user --- src/graphql/user.ts | 5 +++++ src/services/userService.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/graphql/user.ts b/src/graphql/user.ts index 0693e4f..dd5c8e4 100644 --- a/src/graphql/user.ts +++ b/src/graphql/user.ts @@ -24,6 +24,7 @@ export const typeDef = gql` series_list: [Series] user_meta: UserMeta is_followed: Boolean + is_trusted: Boolean } type UserProfile { id: ID! @@ -122,6 +123,10 @@ export const resolvers: IResolvers = { if (!ctx.user_id) return false; return await userService.isFollowed(parent.id, ctx.user_id); }, + is_trusted: async (parent: User, _, ctx) => { + if (!parent.id) return false; + return await userService.checkTrust(parent.id); + }, }, Query: { user: async (parent: any, { id, username }: any) => { diff --git a/src/services/userService.ts b/src/services/userService.ts index d13a828..1eda417 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -11,6 +11,7 @@ import Cookies from 'cookies'; import Axios, { AxiosError, AxiosResponse } from 'axios'; import postService from './postService'; import { getEndpoint } from '../lib/getEndpoint'; +import { differenceInDays } from 'date-fns'; const { API_V3_HOST, CLIENT_V2_HOST } = process.env; @@ -222,6 +223,23 @@ const userService = { async isFollowed(followingUserId: string, signedUserId: string): Promise { return !!(await this.findFollowRelationship(followingUserId, signedUserId)); }, + async checkTrust(userId: string): Promise { + const user = await db.user.findUnique({ + where: { + id: userId, + }, + }); + + if (!user) { + throw new ApolloError('User not found', 'NOT_FOUND'); + } + + const joinDay = new Date(user.created_at); + const today = new Date(); + + const diffDays = differenceInDays(today, joinDay); + return diffDays > 20; + }, }; export default userService; From e92dea598f02c38f869c990c24b65805642f1737 Mon Sep 17 00:00:00 2001 From: carrick Date: Tue, 13 Feb 2024 17:04:30 +0900 Subject: [PATCH 2/7] feat: verify turnstile token --- src/graphql/post.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/turnstile.ts | 21 +++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/lib/turnstile.ts diff --git a/src/graphql/post.ts b/src/graphql/post.ts index 4eef461..99becdd 100644 --- a/src/graphql/post.ts +++ b/src/graphql/post.ts @@ -33,6 +33,8 @@ import imageService from '../services/imageService'; import externalInterationService from '../services/externalIntegrationService'; import postService from '../services/postService'; import { checkBlockList } from '../lib/checkBlockList'; +import userService from '../services/userService'; +import { verifyTurnstileToken } from '../lib/turnstile'; const lruCache = new LRU({ max: 150, @@ -125,6 +127,7 @@ export const typeDef = gql` thumbnail: String meta: JSON series_id: ID + token: String ): Post editPost( id: ID! @@ -138,6 +141,7 @@ export const typeDef = gql` meta: JSON is_private: Boolean series_id: ID + token: String ): Post createPostHistory( post_id: ID! @@ -173,6 +177,7 @@ type WritePostArgs = { meta: any; series_id?: string; is_private: boolean; + token: string | null; }; type CreatePostHistoryArgs = { @@ -181,6 +186,7 @@ type CreatePostHistoryArgs = { body: string; is_markdown: boolean; }; + type EditPostArgs = WritePostArgs & { id: string; }; @@ -730,11 +736,30 @@ export const resolvers: IResolvers = { } } + // check block list const isBlockList = await checkBlockList(ctx.user_id, user?.username); if (isBlockList) { post.is_private = true; } + // check bot + if (!data.is_private || !data.is_temp) { + const isTrustUser = await userService.checkTrust(ctx.user_id); + if (!isTrustUser && !data.token) { + throw new ApolloError( + 'A Turnstile token is required for users who are not trusted', + 'NO_PERMISSION' + ); + } + + if (!isTrustUser) { + const isVerified = await verifyTurnstileToken(data.token!); + if (!isVerified) { + post.is_private = true; + } + } + } + let processedUrlSlug = escapeForUrl(data.url_slug); const urlSlugDuplicate = await postRepo.findOne({ where: { @@ -878,6 +903,7 @@ export const resolvers: IResolvers = { url_slug, tags, is_private, + token, } = args as EditPostArgs; const postRepo = getRepository(Post); const seriesRepo = getRepository(Series); @@ -1027,6 +1053,24 @@ export const resolvers: IResolvers = { post.is_private = true; } + // check bot + if (!is_private || !is_temp) { + const isTrustUser = await userService.checkTrust(ctx.user_id); + if (!isTrustUser && !token) { + throw new ApolloError( + 'A Turnstile token is required for users who are not trusted', + 'NO_PERMISSION' + ); + } + + if (!isTrustUser) { + const isVerified = await verifyTurnstileToken(token!); + if (!isVerified) { + post.is_private = true; + } + } + } + const tagsData = await Promise.all(tags.map(Tag.findOrCreate)); await Promise.all([PostsTags.syncPostTags(post.id, tagsData), postRepo.save(post)]); diff --git a/src/lib/turnstile.ts b/src/lib/turnstile.ts new file mode 100644 index 0000000..ae7ecfc --- /dev/null +++ b/src/lib/turnstile.ts @@ -0,0 +1,21 @@ +import axios from 'axios'; + +const TURNSTILE_SECRET_KEY = process.env.TURNSTILE_SECRET_KEY; + +console.log('verifyTurnstileToken', TURNSTILE_SECRET_KEY); +const VERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; + +export async function verifyTurnstileToken(token: string): Promise { + try { + const res = await axios.post<{ success: boolean }>(VERIFY_URL, { + secret: TURNSTILE_SECRET_KEY, + response: token, + }); + + console.log('toklen', res); + return res.data.success; + } catch (error) { + console.log('verifyTurnstileToken error', error); + return false; + } +} From 52abaa4e161de258e7a13832ee40bf871a816ff4 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 14 Feb 2024 14:59:58 +0900 Subject: [PATCH 3/7] chore: change variable name --- src/graphql/post.ts | 36 ++++++++++++++++++------------------ src/lib/turnstile.ts | 2 -- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/graphql/post.ts b/src/graphql/post.ts index 99becdd..69ae982 100644 --- a/src/graphql/post.ts +++ b/src/graphql/post.ts @@ -743,16 +743,16 @@ export const resolvers: IResolvers = { } // check bot - if (!data.is_private || !data.is_temp) { - const isTrustUser = await userService.checkTrust(ctx.user_id); - if (!isTrustUser && !data.token) { - throw new ApolloError( - 'A Turnstile token is required for users who are not trusted', - 'NO_PERMISSION' - ); - } + if (!data.is_private && !data.is_temp) { + const isTrustedUser = await userService.checkTrust(ctx.user_id); + if (!isTrustedUser) { + if (!data.token) { + throw new ApolloError( + 'A Turnstile token is required for users who are not trusted', + 'NO_PERMISSION' + ); + } - if (!isTrustUser) { const isVerified = await verifyTurnstileToken(data.token!); if (!isVerified) { post.is_private = true; @@ -1054,16 +1054,16 @@ export const resolvers: IResolvers = { } // check bot - if (!is_private || !is_temp) { - const isTrustUser = await userService.checkTrust(ctx.user_id); - if (!isTrustUser && !token) { - throw new ApolloError( - 'A Turnstile token is required for users who are not trusted', - 'NO_PERMISSION' - ); - } + if (!is_private && !is_temp) { + const isTrustedUser = await userService.checkTrust(ctx.user_id); + if (!isTrustedUser) { + if (!token) { + throw new ApolloError( + 'A Turnstile token is required for users who are not trusted', + 'NO_PERMISSION' + ); + } - if (!isTrustUser) { const isVerified = await verifyTurnstileToken(token!); if (!isVerified) { post.is_private = true; diff --git a/src/lib/turnstile.ts b/src/lib/turnstile.ts index ae7ecfc..8a11a74 100644 --- a/src/lib/turnstile.ts +++ b/src/lib/turnstile.ts @@ -2,7 +2,6 @@ import axios from 'axios'; const TURNSTILE_SECRET_KEY = process.env.TURNSTILE_SECRET_KEY; -console.log('verifyTurnstileToken', TURNSTILE_SECRET_KEY); const VERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; export async function verifyTurnstileToken(token: string): Promise { @@ -12,7 +11,6 @@ export async function verifyTurnstileToken(token: string): Promise { response: token, }); - console.log('toklen', res); return res.data.success; } catch (error) { console.log('verifyTurnstileToken error', error); From 30fbeb11e4a6b8c98d2a2abe784c3666975ea0a4 Mon Sep 17 00:00:00 2001 From: carrick Date: Thu, 15 Feb 2024 16:06:42 +0900 Subject: [PATCH 4/7] fix: change ads query --- src/services/adService.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/services/adService.ts b/src/services/adService.ts index 2674ead..3f00236 100644 --- a/src/services/adService.ts +++ b/src/services/adService.ts @@ -10,8 +10,8 @@ if (!API_V3_HOST) { const adService = { async getBannerTypeAdList(writerUsername: string) { const GET_AD_LIST = ` - query ads { - ads(input: {writer_username: "${writerUsername}", limit: 2, type: "banner" }) { + query ads($input: AdsInput!) { + ads(input: $input) { id title body @@ -21,13 +21,21 @@ const adService = { } `; - const endpoint = getEndpoint(); + const variables = { + input: { + writer_username: writerUsername, + limit: 2, + type: 'banner', + }, + }; + const endpoint = getEndpoint(); const { data } = await Axios.post>( endpoint, { operationName: 'ads', query: GET_AD_LIST, + variables, }, { headers: { From 0f808ab7e843c25ed280280b48611683b60836c4 Mon Sep 17 00:00:00 2001 From: carrick Date: Tue, 20 Feb 2024 17:14:38 +0900 Subject: [PATCH 5/7] WIP: write&edit api migration --- src/services/postService.ts | 125 ++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/services/postService.ts b/src/services/postService.ts index a2d551d..da31c6d 100644 --- a/src/services/postService.ts +++ b/src/services/postService.ts @@ -1,3 +1,4 @@ +import { WritePostResponse } from './../../../velog-client/src/lib/graphql/post'; import { GetPostsByTagParams } from './../entity/PostsTags'; import { Post, PostTag, Tag, User } from '@prisma/client'; import db from '../lib/db'; @@ -321,6 +322,90 @@ const postService = { console.log('error', error); } }, + + async write(args: WritePostArgs, cookies: Cookies) { + const WRITE_POST_MUTATION = ` + mutation WritePost($input: WritePostInput!) { + writePost(input: $input) { + id + user { + id + username + } + url_slug + } + } + `; + + const endpoint = getEndpoint(); + const accessToken = cookies.get('access_token') ?? ''; + + try { + const res = await Axios.post>( + endpoint, + { + operationName: 'WritePost', + query: WRITE_POST_MUTATION, + variables: { + input: { + ...args, + }, + }, + }, + { + headers: { + 'Content-Type': 'application/json', + authorization: `Bearer ${accessToken}`, + }, + } + ); + + return res.data.data.writePost; + } catch (error) { + console.log('error', error); + } + }, + async editPost(args: EditPostArgs, cookies: Cookies) { + const EDIT_POST_MUTATION = ` + mutation EditPost($input: EditPostInput!) { + editPost(input: $input) { + id + user { + id + username + } + url_slug + } + } + `; + + const endpoint = getEndpoint(); + const accessToken = cookies.get('access_token') ?? ''; + try { + const res = await Axios.post>( + endpoint, + { + operationName: 'EditPost', + query: EDIT_POST_MUTATION, + variables: { + input: { + ...args, + }, + }, + }, + { + headers: { + 'Content-Type': 'application/json', + authorization: `Bearer ${accessToken}`, + }, + } + ); + + return res.data.data.editPost; + } catch (error) { + console.log('error', error); + } + }, }; export default postService; @@ -359,3 +444,43 @@ type UnlikePostResponse = { likes: number; }; }; + +type WritePostArgs = { + title: string; + body: string; + tags: string[]; + is_markdown: boolean; + is_temp: boolean; + url_slug: string; + thumbnail: string | null; + meta: any; + series_id?: string; + is_private: boolean; + token: string | null; +}; + +type EditPostArgs = WritePostArgs & { + id: string; +}; + +type WritePostResponse = { + writePost: { + id: string; + user: { + id: string; + username: string; + }; + url_slug: string; + }; +}; + +type EditPostResponse = { + editPost: { + id: string; + user: { + id: string; + username: string; + }; + url_slug: string; + }; +}; From 5f656f9bc86fa4299ae3a0f93c1a8a9612ffc93b Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 21 Feb 2024 16:13:50 +0900 Subject: [PATCH 6/7] feat: implement post write/edit api migration --- src/graphql/post.ts | 462 +----------------------------------- src/services/postService.ts | 7 +- 2 files changed, 5 insertions(+), 464 deletions(-) diff --git a/src/graphql/post.ts b/src/graphql/post.ts index 69ae982..bf01d38 100644 --- a/src/graphql/post.ts +++ b/src/graphql/post.ts @@ -660,468 +660,10 @@ export const resolvers: IResolvers = { }, Mutation: { writePost: async (parent: any, args, ctx) => { - const postRepo = getRepository(Post); - const seriesRepo = getRepository(Series); - - if (!ctx.user_id) { - throw new AuthenticationError('Not Logged In'); - } - - const post = new Post(); - const data = args as WritePostArgs; - - if (checkEmpty(data.title)) { - throw new ApolloError('Title is empty', 'BAD_REQUEST'); - } - - post.fk_user_id = ctx.user_id; - post.title = data.title; - post.body = data.body; - post.is_temp = data.is_temp; - post.is_markdown = data.is_markdown; - post.meta = data.meta; - post.thumbnail = data.thumbnail; - post.is_private = data.is_private; - - const allowList = ['KR', 'GB', '']; - const country = geoipCountry.lookup(ctx.ip)?.country ?? ''; - const isForeign = !allowList.includes(country); - const blockList = ['IN', 'PK', 'CN', 'VN', 'TH', 'PH']; - - const user = await getRepository(User).findOne(ctx.user_id, { - relations: ['profile'], - }); - - const extraText = data.tags - .join('') - .concat(user?.profile.short_bio ?? '', user?.profile.display_name ?? ''); - - if ( - blockList.includes(country) || - nextSpamFilter(data.body.concat(extraText), isForeign) || - nextSpamFilter(data.title, isForeign, true) - ) { - post.is_private = true; - await Axios.post(slackUrl, { - text: `스팸 의심!\n *userId*: ${ctx.user_id}\ntitle: ${post.title}, ip: ${ctx.ip}, country: ${country}`, - }); - } - - const recentPostCount = await postRepo.count({ - where: { - fk_user_id: ctx.user_id, - released_at: MoreThan(new Date(Date.now() - 1000 * 60 * 5)), - is_private: false, - }, - }); - - if (recentPostCount >= 10) { - post.is_private = true; - const user = await getRepository(User).findOne(ctx.user_id); - try { - await postRepo.update( - { - fk_user_id: ctx.user_id, - released_at: MoreThan(new Date(Date.now() - 1000 * 60 * 5)), - }, - { - is_private: true, - } - ); - await Axios.post(slackUrl, { - text: `스팸 의심!\n *User*: ${user?.username}\n*Count*: ${recentPostCount}\n*Title*:${post.title}\nip: ${ctx.ip}`, - }); - } catch (e) { - console.log(e); - } - } - - // check block list - const isBlockList = await checkBlockList(ctx.user_id, user?.username); - if (isBlockList) { - post.is_private = true; - } - - // check bot - if (!data.is_private && !data.is_temp) { - const isTrustedUser = await userService.checkTrust(ctx.user_id); - if (!isTrustedUser) { - if (!data.token) { - throw new ApolloError( - 'A Turnstile token is required for users who are not trusted', - 'NO_PERMISSION' - ); - } - - const isVerified = await verifyTurnstileToken(data.token!); - if (!isVerified) { - post.is_private = true; - } - } - } - - let processedUrlSlug = escapeForUrl(data.url_slug); - const urlSlugDuplicate = await postRepo.findOne({ - where: { - fk_user_id: ctx.user_id, - url_slug: processedUrlSlug, - }, - }); - if (urlSlugDuplicate) { - const randomString = generate('abcdefghijklmnopqrstuvwxyz1234567890', 8); - processedUrlSlug += `-${randomString}`; - } - if (processedUrlSlug === '') { - processedUrlSlug = generate('abcdefghijklmnopqrstuvwxyz1234567890', 8); - } - - post.url_slug = processedUrlSlug; - - // Check series - let series: Series | undefined; - if (data.series_id && !data.is_temp) { - series = await seriesRepo.findOne(data.series_id); - if (!series) { - throw new ApolloError('Series not found', 'NOT_FOUND'); - } - if (series.fk_user_id !== ctx.user_id) { - throw new ApolloError('This series is not yours', 'NO_PERMISSION'); - } - } - - const tagsData = await Promise.all(data.tags.map(Tag.findOrCreate)); - await postRepo.save(post); - - await PostsTags.syncPostTags(post.id, tagsData); - - // Link to series - if (data.series_id && !data.is_temp) { - await appendToSeries(data.series_id, post.id); - } - - post.tags = tagsData; - - if (!data.is_temp) { - await searchSync.update(post.id); - } - - if (!data.is_temp && !data.is_private) { - setImmediate(async () => { - if (!ctx.user_id) return; - const isIntegrated = await externalInterationService.checkIntegrated(ctx.user_id); - if (!isIntegrated) return; - const serializedPost = await postService.findPostById(post.id); - if (!serializedPost) return; - externalInterationService.notifyWebhook({ - type: 'created', - post: serializedPost, - }); - }); - - const queueData = { - fk_following_id: ctx.user_id, - fk_post_id: post.id, - }; - await cache.createFeed(queueData); - } - - purgeRecentPosts(); - purgeUser(ctx.user_id); - - setTimeout(async () => { - const images = await imageService.getImagesOf(post.id); - await imageService.trackImages(images, data.body); - }, 0); - - return post; - }, - createPostHistory: async (parent: any, args: CreatePostHistoryArgs, ctx) => { - if (!ctx.user_id) { - throw new AuthenticationError('Not Logged In'); - } - - // check ownPost - const { post_id, title, body, is_markdown } = args; - - const postRepo = getRepository(Post); - const post = await postRepo.findOne(post_id); - - if (!post) { - throw new ApolloError('Post not found', 'NOT_FOUND'); - } - if (post.fk_user_id !== ctx.user_id) { - throw new ApolloError('This post is not yours', 'NO_PERMISSION'); - } - - // create postHistory - const postHistoryRepo = getRepository(PostHistory); - const postHistory = new PostHistory(); - Object.assign(postHistory, { title, body, is_markdown, fk_post_id: post_id }); - - await postHistoryRepo.save(postHistory); - - const [data, count] = await postHistoryRepo.findAndCount({ - where: { - fk_post_id: post_id, - }, - order: { - created_at: 'DESC', - }, - }); - - if (count > 10) { - await postHistoryRepo - .createQueryBuilder('post_history') - .delete() - .where('fk_post_id = :postId', { - postId: post_id, - }) - .andWhere('created_at < :createdAt', { createdAt: data[9].created_at }) - .execute(); - - setTimeout(() => { - searchSync.update(post.id).catch(console.error); - }, 0); - } - - return postHistory; + return postService.write(args, ctx.cookies); }, editPost: async (parent: any, args, ctx) => { - if (!ctx.user_id) { - throw new AuthenticationError('Not Logged In'); - } - - const { - id, - title, - body, - is_temp, - is_markdown, - meta, - thumbnail, - series_id, - url_slug, - tags, - is_private, - token, - } = args as EditPostArgs; - const postRepo = getRepository(Post); - const seriesRepo = getRepository(Series); - const seriesPostsRepo = getRepository(SeriesPosts); - - const post = await postRepo.findOne(id, { - relations: ['user'], - }); - if (!post) { - throw new ApolloError('Post not found', 'NOT_FOUND'); - } - if (post.fk_user_id !== ctx.user_id) { - throw new ApolloError('This post is not yours', 'NO_PERMISSION'); - } - - const { username } = post.user; - const postCacheKey = `ssr:/@${username}/${post.url_slug}`; - const userVelogCacheKey = `ssr:/@${username}`; - const cacheKeys = [postCacheKey, userVelogCacheKey]; - - const prevSeriesPost = await seriesPostsRepo.findOne({ - fk_post_id: post.id, - }); - - if (!prevSeriesPost && series_id) { - await appendToSeries(series_id, post.id); - } - - if (prevSeriesPost && prevSeriesPost.fk_series_id !== series_id) { - if (series_id) { - // append series - const series = await seriesRepo.findOne({ - id: series_id, - }); - if (!series) { - throw new ApolloError('Series not found', 'NOT_FOUND'); - } - if (series.fk_user_id !== ctx.user_id) { - throw new ApolloError('This series is not yours', 'NO_PERMISSION'); - } - cacheKeys.push(`ssr:/@${username}/series/${series.url_slug}`); - await appendToSeries(series_id, post.id); - } - // remove series - await Promise.all([ - subtractIndexAfter(prevSeriesPost.fk_series_id, prevSeriesPost.index), - seriesPostsRepo.remove(prevSeriesPost), - ]); - } - - post.title = title; - post.body = body; - post.is_private = is_private; - - if (!post.is_temp) { - const recentPostCount = await postRepo.count({ - where: { - fk_user_id: ctx.user_id, - released_at: MoreThan(new Date(Date.now() - 1000 * 60 * 5)), - is_private: false, - }, - }); - - if (recentPostCount >= 10) { - post.is_private = true; - const user = await getRepository(User).findOne(ctx.user_id); - try { - await postRepo.update( - { - fk_user_id: ctx.user_id, - released_at: MoreThan(new Date(Date.now() - 1000 * 60 * 5)), - }, - { - is_private: true, - } - ); - await Axios.post(slackUrl, { - text: `스팸 의심!\n *User*: ${user?.username}\n*Count*: ${recentPostCount}\n*Title*:${post.title}`, - }); - } catch (e) { - console.log(e); - } - } - } - - if (post.is_temp && !is_temp) { - post.released_at = new Date(); - } - post.is_temp = is_temp; - post.is_markdown = is_markdown; - post.meta = meta; - post.thumbnail = thumbnail; - - const allowList = ['KR', 'GB', '']; - const country = geoipCountry.lookup(ctx.ip)?.country ?? ''; - const isForeign = !allowList.includes(country); - const blockList = ['IN', 'PK', 'CN', 'VN', 'TH', 'PH']; - - const user = await getRepository(User).findOne(ctx.user_id, { - relations: ['profile'], - }); - - const extraText = tags - .join('') - .concat(user?.profile.short_bio ?? '', user?.profile.display_name ?? ''); - - if ( - blockList.includes(country) || - nextSpamFilter(body.concat(extraText), isForeign) || - nextSpamFilter(title, isForeign, true) - ) { - post.is_private = true; - await Axios.post(slackUrl, { - text: `스팸 의심 (수정) !\n *userId*: ${ctx.user_id}\ntitle: ${post.title}, ip: ${ctx.ip}, country: ${country}`, - }); - } - - // TODO: if url_slug changes, create url_slug_alias - let processedUrlSlug = escapeForUrl(url_slug); - const urlSlugDuplicate = await postRepo.findOne({ - where: { - fk_user_id: ctx.user_id, - url_slug: processedUrlSlug, - }, - }); - if (urlSlugDuplicate && urlSlugDuplicate.id !== post.id) { - const randomString = generate('abcdefghijklmnopqrstuvwxyz1234567890', 8); - processedUrlSlug += `-${randomString}`; - } - if (processedUrlSlug === '') { - processedUrlSlug = generate('abcdefghijklmnopqrstuvwxyz1234567890', 8); - } - - if (post.url_slug !== processedUrlSlug) { - // url_slug - const urlSlugHistory = new UrlSlugHistory(); - urlSlugHistory.fk_post_id = post.id; - urlSlugHistory.fk_user_id = ctx.user_id; - urlSlugHistory.url_slug = post.url_slug; - const urlSlugHistoryRepo = getRepository(UrlSlugHistory); - await urlSlugHistoryRepo.save(urlSlugHistory); - } - post.url_slug = processedUrlSlug; - - const isBlockList = await checkBlockList(ctx.user_id, user?.username); - if (isBlockList) { - post.is_private = true; - } - - // check bot - if (!is_private && !is_temp) { - const isTrustedUser = await userService.checkTrust(ctx.user_id); - if (!isTrustedUser) { - if (!token) { - throw new ApolloError( - 'A Turnstile token is required for users who are not trusted', - 'NO_PERMISSION' - ); - } - - const isVerified = await verifyTurnstileToken(token!); - if (!isVerified) { - post.is_private = true; - } - } - } - - const tagsData = await Promise.all(tags.map(Tag.findOrCreate)); - await Promise.all([PostsTags.syncPostTags(post.id, tagsData), postRepo.save(post)]); - - try { - await Promise.all([ - is_temp ? null : searchSync.update(post.id), - cache.remove(...cacheKeys), - purgePost(post.id), - ]); - } catch (e) { - console.log(e); - } - - if (!is_temp && !is_private) { - setImmediate(async () => { - if (!ctx.user_id) return; - const isIntegrated = await externalInterationService.checkIntegrated(ctx.user_id); - if (!isIntegrated) return; - const serializedPost = await postService.findPostById(post.id); - if (!serializedPost) return; - externalInterationService.notifyWebhook({ - type: 'updated', - post: serializedPost, - }); - }); - - const queueData = { - fk_following_id: ctx.user_id, - fk_post_id: post.id, - }; - await cache.createFeed(queueData); - } - - if (!post.is_private && is_private) { - setImmediate(async () => { - if (!ctx.user_id) return; - const isIntegrated = await externalInterationService.checkIntegrated(ctx.user_id); - if (!isIntegrated) return; - externalInterationService.notifyWebhook({ - type: 'deleted', - post_id: post.id, - }); - }); - } - - setTimeout(async () => { - const images = await imageService.getImagesOf(post.id); - await imageService.trackImages(images, body); - }, 0); - - return post; + return postService.edit(args, ctx.cookies); }, removePost: async (parent: any, args, ctx) => { const { id } = args as { id: string }; diff --git a/src/services/postService.ts b/src/services/postService.ts index da31c6d..37c7ef4 100644 --- a/src/services/postService.ts +++ b/src/services/postService.ts @@ -1,4 +1,3 @@ -import { WritePostResponse } from './../../../velog-client/src/lib/graphql/post'; import { GetPostsByTagParams } from './../entity/PostsTags'; import { Post, PostTag, Tag, User } from '@prisma/client'; import db from '../lib/db'; @@ -362,10 +361,10 @@ const postService = { return res.data.data.writePost; } catch (error) { - console.log('error', error); + console.log('write post error', error); } }, - async editPost(args: EditPostArgs, cookies: Cookies) { + async edit(args: EditPostArgs, cookies: Cookies) { const EDIT_POST_MUTATION = ` mutation EditPost($input: EditPostInput!) { editPost(input: $input) { @@ -403,7 +402,7 @@ const postService = { return res.data.data.editPost; } catch (error) { - console.log('error', error); + console.log('edit post error', error); } }, }; From 7ae46f767686bbcccebf396416b1ae13eb2a86e7 Mon Sep 17 00:00:00 2001 From: carrick Date: Wed, 21 Feb 2024 21:45:27 +0900 Subject: [PATCH 7/7] add: await keyword for bug report --- src/graphql/post.ts | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/graphql/post.ts b/src/graphql/post.ts index bf01d38..57ba1c5 100644 --- a/src/graphql/post.ts +++ b/src/graphql/post.ts @@ -2,15 +2,12 @@ import { ApolloContext } from './../app'; import { gql, IResolvers, ApolloError, AuthenticationError } from 'apollo-server-koa'; import Post from '../entity/Post'; import { getRepository, getManager, LessThan, Not, MoreThan } from 'typeorm'; -import { normalize, escapeForUrl, checkEmpty } from '../lib/utils'; +import { normalize } from '../lib/utils'; import removeMd from 'remove-markdown'; -import PostsTags from '../entity/PostsTags'; -import Tag from '../entity/Tag'; import UrlSlugHistory from '../entity/UrlSlugHistory'; import Comment from '../entity/Comment'; import Series from '../entity/Series'; import SeriesPosts, { subtractIndexAfter, appendToSeries } from '../entity/SeriesPosts'; -import generate from 'nanoid/generate'; import PostLike from '../entity/PostLike'; import keywordSearch from '../search/keywordSearch'; import searchSync from '../search/searchSync'; @@ -20,26 +17,15 @@ import PostRead from '../entity/PostRead'; import hash from '../lib/hash'; import cache from '../cache'; import PostReadLog from '../entity/PostReadLog'; -import { nextSpamFilter } from '../etc/spamFilter'; -import Axios, { AxiosResponse } from 'axios'; -import LRU from 'lru-cache'; -import { createLikeLog, createReadLog } from '../lib/bigQuery'; +import Axios from 'axios'; +import { createReadLog } from '../lib/bigQuery'; import esClient from '../search/esClient'; import { buildFallbackRecommendedPosts, buildRecommendedPostsQuery } from '../search/buildQuery'; import { pickRandomItems } from '../etc/pickRandomItems'; -import geoipCountry from 'geoip-country'; import { purgeRecentPosts, purgeUser, purgePost } from '../lib/graphcdn'; import imageService from '../services/imageService'; import externalInterationService from '../services/externalIntegrationService'; import postService from '../services/postService'; -import { checkBlockList } from '../lib/checkBlockList'; -import userService from '../services/userService'; -import { verifyTurnstileToken } from '../lib/turnstile'; - -const lruCache = new LRU({ - max: 150, - maxAge: 1000 * 60 * 60, -}); type ReadingListQueryParams = { type: 'LIKED' | 'READ'; @@ -660,10 +646,10 @@ export const resolvers: IResolvers = { }, Mutation: { writePost: async (parent: any, args, ctx) => { - return postService.write(args, ctx.cookies); + return await postService.write(args, ctx.cookies); }, editPost: async (parent: any, args, ctx) => { - return postService.edit(args, ctx.cookies); + return await postService.edit(args, ctx.cookies); }, removePost: async (parent: any, args, ctx) => { const { id } = args as { id: string };