mirror of
https://github.com/wassname/talk.git
synced 2026-07-29 11:28:24 +08:00
fix: resolved issue with incorrect moderation url being generated (#2743)
This commit is contained in:
@@ -29,6 +29,24 @@ export function constructTenantURL(
|
||||
return url.href;
|
||||
}
|
||||
|
||||
export function reconstructTenantURL(
|
||||
config: Config,
|
||||
tenant: Pick<Tenant, "domain">,
|
||||
req?: Request,
|
||||
path = "/"
|
||||
) {
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (req) {
|
||||
return reconstructURL(req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(config, tenant, path);
|
||||
}
|
||||
|
||||
export function doesRequireSchemePrefixing(url: string) {
|
||||
return !url.startsWith("http");
|
||||
}
|
||||
|
||||
@@ -61,7 +61,11 @@ export default class TenantContext extends CommonContext {
|
||||
this.clientID = options.clientID;
|
||||
this.publisher = createPublisher({
|
||||
pubsub: this.pubsub,
|
||||
slackPublisher: createSlackPublisher(this.mongo, this.tenant),
|
||||
slackPublisher: createSlackPublisher(
|
||||
this.mongo,
|
||||
this.config,
|
||||
this.tenant
|
||||
),
|
||||
notifierQueue,
|
||||
tenantID: this.tenant.id,
|
||||
clientID: this.clientID,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { GraphQLResolveInfo } from "graphql";
|
||||
import graphqlFields from "graphql-fields";
|
||||
import { pull } from "lodash";
|
||||
|
||||
import { constructTenantURL, reconstructURL } from "coral-server/app/url";
|
||||
import { reconstructTenantURL } from "coral-server/app/url";
|
||||
|
||||
import TenantContext from "../context";
|
||||
|
||||
@@ -16,16 +16,6 @@ export function getRequestedFields<T>(info: GraphQLResolveInfo) {
|
||||
}
|
||||
|
||||
export function reconstructTenantURLResolver<T = any>(path: string) {
|
||||
return (parent: T, args: {}, ctx: TenantContext) => {
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (ctx.req) {
|
||||
return reconstructURL(ctx.req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(ctx.config, ctx.tenant, path);
|
||||
};
|
||||
return (parent: T, args: {}, ctx: TenantContext) =>
|
||||
reconstructTenantURL(ctx.config, ctx.tenant, ctx.req, path);
|
||||
}
|
||||
|
||||
@@ -2,44 +2,53 @@ import Logger from "bunyan";
|
||||
import DataLoader from "dataloader";
|
||||
import { Db } from "mongodb";
|
||||
|
||||
import { Config } from "coral-server/config";
|
||||
import { Comment, retrieveManyComments } from "coral-server/models/comment";
|
||||
import { retrieveManyStories, Story } from "coral-server/models/story";
|
||||
import { Tenant } from "coral-server/models/tenant";
|
||||
import { retrieveManyUsers, User } from "coral-server/models/user";
|
||||
import { Request } from "coral-server/types/express";
|
||||
|
||||
interface Options {
|
||||
mongo: Db;
|
||||
tenantID: string;
|
||||
tenant: Pick<Tenant, "id" | "domain">;
|
||||
config: Config;
|
||||
req?: Request;
|
||||
}
|
||||
|
||||
class SlackContext {
|
||||
public readonly mongo: Db;
|
||||
public readonly tenantID: string;
|
||||
public readonly tenant: Pick<Tenant, "id" | "domain">;
|
||||
public readonly logger: Logger;
|
||||
public readonly config: Config;
|
||||
public readonly req?: Request;
|
||||
|
||||
public readonly comments: DataLoader<
|
||||
string,
|
||||
Readonly<Comment> | null
|
||||
> = new DataLoader(commentIDs =>
|
||||
retrieveManyComments(this.mongo, this.tenantID, commentIDs)
|
||||
retrieveManyComments(this.mongo, this.tenant.id, commentIDs)
|
||||
);
|
||||
|
||||
public readonly stories: DataLoader<
|
||||
string,
|
||||
Readonly<Story> | null
|
||||
> = new DataLoader(storyIDs =>
|
||||
retrieveManyStories(this.mongo, this.tenantID, storyIDs)
|
||||
retrieveManyStories(this.mongo, this.tenant.id, storyIDs)
|
||||
);
|
||||
|
||||
public readonly users: DataLoader<
|
||||
string,
|
||||
Readonly<User> | null
|
||||
> = new DataLoader(userIDs =>
|
||||
retrieveManyUsers(this.mongo, this.tenantID, userIDs)
|
||||
retrieveManyUsers(this.mongo, this.tenant.id, userIDs)
|
||||
);
|
||||
|
||||
constructor({ mongo, tenantID }: Options) {
|
||||
constructor({ mongo, tenant, config, req }: Options) {
|
||||
this.mongo = mongo;
|
||||
this.tenantID = tenantID;
|
||||
this.tenant = tenant;
|
||||
this.config = config;
|
||||
this.req = req;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { Db } from "mongodb";
|
||||
|
||||
import { reconstructTenantURL } from "coral-server/app/url";
|
||||
import { Config } from "coral-server/config";
|
||||
import { CommentCreatedInput } from "coral-server/graph/tenant/resolvers/Subscription/commentCreated";
|
||||
import { CommentEnteredModerationQueueInput } from "coral-server/graph/tenant/resolvers/Subscription/commentEnteredModerationQueue";
|
||||
import { CommentFeaturedInput } from "coral-server/graph/tenant/resolvers/Subscription/commentFeatured";
|
||||
@@ -7,11 +11,15 @@ import { CommentReplyCreatedInput } from "coral-server/graph/tenant/resolvers/Su
|
||||
import { CommentStatusUpdatedInput } from "coral-server/graph/tenant/resolvers/Subscription/commentStatusUpdated";
|
||||
import { SUBSCRIPTION_CHANNELS } from "coral-server/graph/tenant/resolvers/Subscription/types";
|
||||
import logger from "coral-server/logger";
|
||||
import { getLatestRevision } from "coral-server/models/comment/helpers";
|
||||
import {
|
||||
getStoryTitle,
|
||||
getURLWithCommentID,
|
||||
} from "coral-server/models/story/helpers";
|
||||
import { Tenant } from "coral-server/models/tenant";
|
||||
|
||||
import { GQLMODERATION_QUEUE } from "coral-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { Tenant } from "coral-server/models/tenant";
|
||||
import { Db } from "mongodb";
|
||||
import SlackContext from "./context";
|
||||
|
||||
type Payload =
|
||||
@@ -55,21 +63,19 @@ function isPending(channel: SUBSCRIPTION_CHANNELS, payload: Payload) {
|
||||
);
|
||||
}
|
||||
|
||||
function createModerationLink(rootURL: string, commentID: string) {
|
||||
return `${rootURL}/admin/moderate/comment/${commentID}`;
|
||||
}
|
||||
|
||||
function createCommentLink(storyURL: string, commentID: string) {
|
||||
const urlBuilder = new URL(storyURL);
|
||||
urlBuilder.searchParams.set("commentID", commentID);
|
||||
return urlBuilder.href;
|
||||
function createModerationLink(ctx: SlackContext, commentID: string) {
|
||||
return reconstructTenantURL(
|
||||
ctx.config,
|
||||
ctx.tenant,
|
||||
ctx.req,
|
||||
`/admin/moderate/comment/${commentID}`
|
||||
);
|
||||
}
|
||||
|
||||
async function postCommentToSlack(
|
||||
ctx: SlackContext,
|
||||
orgURL: string,
|
||||
commentID: string,
|
||||
webhookURL: string
|
||||
hookURL: string
|
||||
) {
|
||||
const comment = await ctx.comments.load(commentID);
|
||||
if (comment === null) {
|
||||
@@ -84,14 +90,14 @@ async function postCommentToSlack(
|
||||
return;
|
||||
}
|
||||
|
||||
const storyTitle = story.metadata ? story.metadata.title : "";
|
||||
const commentBody =
|
||||
comment.revisions.length > 0
|
||||
? comment.revisions[comment.revisions.length - 1].body
|
||||
: "";
|
||||
// Get some properties about the event.
|
||||
const storyTitle = getStoryTitle(story);
|
||||
const commentBody = getLatestRevision(comment).body;
|
||||
const moderateLink = createModerationLink(ctx, commentID);
|
||||
const commentLink = getURLWithCommentID(story.url, comment.id);
|
||||
|
||||
// Replace HTML link breaks with newlines.
|
||||
const body = commentBody.replace(/<br\/?>/g, "\n");
|
||||
const moderateLink = createModerationLink(orgURL, comment.id);
|
||||
const commentLink = createCommentLink(story.url, comment.id);
|
||||
|
||||
const data = {
|
||||
text: `${author.username} commented on: ${storyTitle}`,
|
||||
@@ -110,9 +116,7 @@ async function postCommentToSlack(
|
||||
text: `<${moderateLink}|Go to Moderation> | <${commentLink}|See Comment>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{ type: "divider" },
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
@@ -124,17 +128,17 @@ async function postCommentToSlack(
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(webhookURL, {
|
||||
// Send the post to the Slack URL.
|
||||
const res = await fetch(hookURL, {
|
||||
method: "POST",
|
||||
cache: "no-cache",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
logger.error({ response }, "error sending Slack comment");
|
||||
if (!res.ok) {
|
||||
logger.error({ res }, "error sending Slack comment");
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({ err }, "error sending Slack comment");
|
||||
@@ -146,7 +150,11 @@ export type SlackPublisher = (
|
||||
payload: Payload
|
||||
) => Promise<void>;
|
||||
|
||||
function createSlackPublisher(mongo: Db, tenant: Tenant): SlackPublisher {
|
||||
function createSlackPublisher(
|
||||
mongo: Db,
|
||||
config: Config,
|
||||
tenant: Tenant
|
||||
): SlackPublisher {
|
||||
if (
|
||||
!tenant.slack ||
|
||||
!tenant.slack.channels ||
|
||||
@@ -160,7 +168,7 @@ function createSlackPublisher(mongo: Db, tenant: Tenant): SlackPublisher {
|
||||
const { channels } = tenant.slack;
|
||||
|
||||
return async (channel: SUBSCRIPTION_CHANNELS, payload: Payload) => {
|
||||
const ctx = new SlackContext({ mongo, tenantID: tenant.id });
|
||||
const ctx = new SlackContext({ mongo, config, tenant });
|
||||
|
||||
try {
|
||||
const inModeration = enteredModeration(channel, payload);
|
||||
@@ -186,19 +194,17 @@ function createSlackPublisher(mongo: Db, tenant: Tenant): SlackPublisher {
|
||||
return;
|
||||
}
|
||||
|
||||
const orgURL = tenant.organization.url;
|
||||
|
||||
if (
|
||||
triggers.allComments &&
|
||||
(reported || pending || featured || inModeration)
|
||||
) {
|
||||
await postCommentToSlack(ctx, orgURL, commentID, hookURL);
|
||||
await postCommentToSlack(ctx, commentID, hookURL);
|
||||
} else if (triggers.reportedComments && reported) {
|
||||
await postCommentToSlack(ctx, orgURL, commentID, hookURL);
|
||||
await postCommentToSlack(ctx, commentID, hookURL);
|
||||
} else if (triggers.pendingComments && pending) {
|
||||
await postCommentToSlack(ctx, orgURL, commentID, hookURL);
|
||||
await postCommentToSlack(ctx, commentID, hookURL);
|
||||
} else if (triggers.featuredComments && featured) {
|
||||
await postCommentToSlack(ctx, orgURL, commentID, hookURL);
|
||||
await postCommentToSlack(ctx, commentID, hookURL);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user