mirror of
https://github.com/wassname/talk.git
synced 2026-07-24 13:20:47 +08:00
[CORL 133] API Review (#2197)
* refactor: removed unused subscription code
* refactor: removed management api's
* refactor: cleanup of connections
* refactor: refactored comments edge
* refactor: simplified connection resolving
* feat: added story connection edge
* fix: added story index
* feat: added user pagination and user edge
* fix: added filter to comment query
* fix: removed unused resolvers
* fix: creating a comment reply should require auth
* refactor: cleanup of graph files
* feat: removed display name, made username non-unique
* fix: fixed tests
* fix: fixed tests
* fix: added more api docs
* fix: fixed bug with installer
* refactor: fixes and updates
* fix: added linting for graphql, fixed schema
* feat: added docker build tests
* fix: upped output timeout
* fix: fixed stacktraces in production builds
* fix: removed `git add`
- `git add` was causing issues with
partial staged changs on files
* feat: improved error messaging for auth
* refactor: cleaned up queue names
* fix: merge error
This commit is contained in:
@@ -90,7 +90,7 @@ export const spam: IntermediateModerationPhase = async ({
|
||||
user_agent: userAgent, // REQUIRED
|
||||
comment_content: comment.body,
|
||||
permalink: story.url,
|
||||
comment_author: author.displayName || author.username || "",
|
||||
comment_author: author.username || "",
|
||||
comment_type: "comment",
|
||||
is_test: false,
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Bearer } from "permit";
|
||||
import uuid from "uuid/v4";
|
||||
|
||||
import { Config } from "talk-server/config";
|
||||
import { AuthenticationError } from "talk-server/errors";
|
||||
import { User } from "talk-server/models/user";
|
||||
import { Request } from "talk-server/types/express";
|
||||
|
||||
@@ -78,8 +79,7 @@ export function createJWTSigningConfig(config: Config): JWTSigningConfig {
|
||||
return createAsymmetricSigningConfig(algorithm, secret);
|
||||
}
|
||||
|
||||
// TODO: (wyattjoh) return better error.
|
||||
throw new Error("invalid algorithm specified");
|
||||
throw new AuthenticationError(`invalid algorithm=${algorithm} specified`);
|
||||
}
|
||||
|
||||
export type SigningTokenOptions = Pick<
|
||||
@@ -137,7 +137,6 @@ export async function revokeJWT(redis: Redis, jti: string, validFor: number) {
|
||||
export async function checkJWTRevoked(redis: Redis, jti: string) {
|
||||
const expiredAtString = await redis.get(generateJTIRevokedKey(jti));
|
||||
if (expiredAtString) {
|
||||
// TODO: (wyattjoh) return a better error.
|
||||
throw new Error("JWT was revoked");
|
||||
throw new AuthenticationError("JWT was revoked");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,7 @@ export type AugmentedRedis = Omit<Redis, "pipeline"> &
|
||||
pipeline(commands?: string[][]): AugmentedPipeline;
|
||||
};
|
||||
|
||||
function configureRedisClient(redis: Redis) {
|
||||
// Attach to the error event.
|
||||
redis.on("error", (err: Error) => {
|
||||
logger.error({ err }, "an error occurred with redis");
|
||||
});
|
||||
|
||||
function augmentRedisClient(redis: Redis): AugmentedRedis {
|
||||
// mhincrby will increment many hash values.
|
||||
redis.defineCommand("mhincrby", {
|
||||
numberOfKeys: 1,
|
||||
@@ -31,28 +26,51 @@ function configureRedisClient(redis: Redis) {
|
||||
end
|
||||
`,
|
||||
});
|
||||
|
||||
return redis as AugmentedRedis;
|
||||
}
|
||||
|
||||
/**
|
||||
* create will connect to the Redis instance identified in the configuration.
|
||||
*
|
||||
* @param config application configuration.
|
||||
*/
|
||||
export async function createRedisClient(
|
||||
config: Config
|
||||
): Promise<AugmentedRedis> {
|
||||
function attachHandlers(redis: Redis) {
|
||||
// Attach to the error event.
|
||||
redis.on("error", (err: Error) => {
|
||||
logger.error({ err }, "an error occurred with redis");
|
||||
});
|
||||
}
|
||||
|
||||
export function createRedisClient(
|
||||
config: Config,
|
||||
lazyConnect: boolean = false
|
||||
): Redis {
|
||||
try {
|
||||
const redis = new RedisClient(config.get("redis"), {
|
||||
lazyConnect: true,
|
||||
lazyConnect,
|
||||
});
|
||||
|
||||
// Configure the redis client for use with the custom commands.
|
||||
configureRedisClient(redis);
|
||||
// Configure the redis client with the handlers for logging
|
||||
attachHandlers(redis);
|
||||
|
||||
return redis;
|
||||
} catch (err) {
|
||||
throw new InternalError(err, "could not connect to redis");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* createAugmentedRedisClient will connect to the Redis instance identified in
|
||||
* the configuration.
|
||||
*
|
||||
* @param config application configuration.
|
||||
*/
|
||||
export async function createAugmentedRedisClient(
|
||||
config: Config
|
||||
): Promise<AugmentedRedis> {
|
||||
try {
|
||||
const redis = augmentRedisClient(createRedisClient(config, true));
|
||||
|
||||
// Connect the redis client.
|
||||
await redis.connect();
|
||||
|
||||
return redis as AugmentedRedis;
|
||||
return redis;
|
||||
} catch (err) {
|
||||
throw new InternalError(err, "could not connect to redis");
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ import {
|
||||
UpdateStoryInput,
|
||||
} from "talk-server/models/story";
|
||||
import { Tenant } from "talk-server/models/tenant";
|
||||
import Task from "talk-server/queue/Task";
|
||||
import { ScraperData } from "talk-server/queue/tasks/scraper";
|
||||
import { ScraperQueue } from "talk-server/queue/tasks/scraper";
|
||||
import { scrape } from "talk-server/services/stories/scraper";
|
||||
|
||||
import { AugmentedRedis } from "../redis";
|
||||
@@ -47,10 +46,10 @@ import { AugmentedRedis } from "../redis";
|
||||
export type FindOrCreateStory = FindOrCreateStoryInput;
|
||||
|
||||
export async function findOrCreate(
|
||||
db: Db,
|
||||
mongo: Db,
|
||||
tenant: Tenant,
|
||||
input: FindOrCreateStory,
|
||||
scraper: Task<ScraperData>
|
||||
scraper: ScraperQueue
|
||||
) {
|
||||
// If the URL is provided, and the url is not on a allowed domain, then refuse
|
||||
// to create the Asset.
|
||||
@@ -63,7 +62,7 @@ export async function findOrCreate(
|
||||
|
||||
// TODO: check to see if the tenant has enabled lazy story creation, if they haven't, switch to find only.
|
||||
|
||||
const story = await findOrCreateStory(db, tenant.id, input);
|
||||
const story = await findOrCreateStory(mongo, tenant.id, input);
|
||||
if (!story) {
|
||||
return null;
|
||||
}
|
||||
@@ -186,16 +185,22 @@ export async function create(
|
||||
tenant: Tenant,
|
||||
storyID: string,
|
||||
storyURL: string,
|
||||
input: CreateStory
|
||||
{ metadata }: CreateStory
|
||||
) {
|
||||
// Ensure that the given URL is allowed.
|
||||
if (!isURLPermitted(tenant, storyURL)) {
|
||||
throw new StoryURLInvalidError({ storyURL, tenantDomains: tenant.domains });
|
||||
}
|
||||
|
||||
// Construct the input payload.
|
||||
const input: CreateStoryInput = { metadata };
|
||||
if (metadata) {
|
||||
input.scrapedAt = new Date();
|
||||
}
|
||||
|
||||
// Create the story in the database.
|
||||
let newStory = await createStory(mongo, tenant.id, storyID, storyURL, input);
|
||||
if (!input.metadata && !newStory.scrapedAt) {
|
||||
if (!metadata) {
|
||||
// If the scraper has not scraped this story and story metadata was not
|
||||
// provided, we need to scrape it now!
|
||||
newStory = await scrape(mongo, tenant.id, newStory.id);
|
||||
@@ -324,6 +329,7 @@ export async function merge(
|
||||
"updated destination story with new comment counts"
|
||||
);
|
||||
|
||||
// Remove the stories from MongoDB.
|
||||
const { deletedCount } = await removeStories(mongo, tenant.id, sourceIDs);
|
||||
|
||||
log.debug({ deletedStories: deletedCount }, "deleted source stories");
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
USERNAME_REGEX,
|
||||
} from "talk-common/helpers/validate";
|
||||
import {
|
||||
DisplayNameExceedsMaxLengthError,
|
||||
EmailAlreadySetError,
|
||||
EmailExceedsMaxLengthError,
|
||||
EmailInvalidFormatError,
|
||||
@@ -32,7 +31,6 @@ import {
|
||||
setUserLocalProfile,
|
||||
setUserUsername,
|
||||
updateUserAvatar,
|
||||
updateUserDisplayName,
|
||||
updateUserEmail,
|
||||
updateUserPassword,
|
||||
updateUserRole,
|
||||
@@ -71,25 +69,6 @@ function validateUsername(tenant: Tenant, username: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const DISPLAY_NAME_MAX_LENGTH = USERNAME_MAX_LENGTH;
|
||||
|
||||
/**
|
||||
* validateDisplayName will validate that the username is valid.
|
||||
*
|
||||
* @param tenant tenant where the User is associated with
|
||||
* @param displayName the display name to be tested
|
||||
*/
|
||||
function validateDisplayName(tenant: Tenant, displayName: string) {
|
||||
// TODO: replace these static regex/length with database options in the Tenant eventually
|
||||
|
||||
if (displayName.length > DISPLAY_NAME_MAX_LENGTH) {
|
||||
throw new DisplayNameExceedsMaxLengthError(
|
||||
displayName.length,
|
||||
DISPLAY_NAME_MAX_LENGTH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validatePassword will validate that the password is valid. Current
|
||||
* implementation uses a length statically, future versions will expose this as
|
||||
@@ -355,28 +334,6 @@ export async function updateUsername(
|
||||
return updateUserUsername(mongo, tenant.id, userID, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* updateDisplayName will update a given User's display name.
|
||||
*
|
||||
* @param mongo mongo database to interact with
|
||||
* @param tenant Tenant where the User will be interacted with
|
||||
* @param userID the User's ID that we are updating
|
||||
* @param displayName the display name that we are setting on the User
|
||||
*/
|
||||
export async function updateDisplayName(
|
||||
mongo: Db,
|
||||
tenant: Tenant,
|
||||
userID: string,
|
||||
displayName?: string
|
||||
) {
|
||||
if (displayName) {
|
||||
// Validate the display name.
|
||||
validateDisplayName(tenant, displayName);
|
||||
}
|
||||
|
||||
return updateUserDisplayName(mongo, tenant.id, userID, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* updateRole will update the given User to the specified role.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user