mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
remove knex
This commit is contained in:
parent
928ed2e6a9
commit
425864ce01
90 changed files with 1431 additions and 1557 deletions
|
|
@ -10,9 +10,8 @@ import { ContextFunction } from 'apollo-server-core'
|
|||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { ExpressContext } from 'apollo-server-express/dist/ApolloServer'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { Knex } from 'knex'
|
||||
import { EntityManager } from 'typeorm'
|
||||
import { promisify } from 'util'
|
||||
import { kx } from './datalayer/knex_config'
|
||||
import { createPubSubClient } from './datalayer/pubsub'
|
||||
import { sanitizeDirectiveTransformer } from './directives'
|
||||
import { env } from './env'
|
||||
|
|
@ -20,7 +19,7 @@ import { functionResolvers } from './resolvers/function_resolvers'
|
|||
import { ClaimsToSet, ResolverContext } from './resolvers/types'
|
||||
import ScalarResolvers from './scalars'
|
||||
import typeDefs from './schema'
|
||||
import { initModels } from './server'
|
||||
import { AppDataSource } from './server'
|
||||
import { tracer } from './tracing'
|
||||
import { getClaimsByToken, setAuthInCookie } from './utils/auth'
|
||||
import { SetClaimsRole } from './utils/dictionary'
|
||||
|
|
@ -47,7 +46,7 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
|||
const claims = await getClaimsByToken(token)
|
||||
|
||||
async function setClaims(
|
||||
tx: Knex.Transaction,
|
||||
em: EntityManager,
|
||||
uuid?: string,
|
||||
userRole?: string
|
||||
): Promise<void> {
|
||||
|
|
@ -55,18 +54,14 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
|||
(claims && claims.uid) || uuid || '00000000-0000-0000-0000-000000000000'
|
||||
const dbRole =
|
||||
userRole === SetClaimsRole.ADMIN ? 'omnivore_admin' : 'omnivore_user'
|
||||
return tx.raw('SELECT * from omnivore.set_claims(?, ?)', [uid, dbRole])
|
||||
return em.query('SELECT * from omnivore.set_claims($1, $2)', [uid, dbRole])
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
log: logger,
|
||||
claims,
|
||||
kx,
|
||||
pubsub,
|
||||
// no caching for subscriptions
|
||||
// TODO: create per request caching for connections
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
models: initModels(kx, true),
|
||||
clearAuth: () => {
|
||||
res.clearCookie('auth')
|
||||
res.clearCookie('pendingUserAuth')
|
||||
|
|
@ -78,12 +73,12 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
|||
) => await setAuthInCookie(claims, res, secret),
|
||||
setClaims,
|
||||
authTrx: <TResult>(
|
||||
cb: (tx: Knex.Transaction) => TResult,
|
||||
cb: (em: EntityManager) => TResult,
|
||||
userRole?: string
|
||||
): Promise<TResult> =>
|
||||
kx.transaction(async (tx) => {
|
||||
await setClaims(tx, undefined, userRole)
|
||||
return cb(tx)
|
||||
AppDataSource.transaction(async (em) => {
|
||||
await setClaims(em, undefined, userRole)
|
||||
return cb(em)
|
||||
}),
|
||||
tracingSpan: tracer.startSpan('apollo.request'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { EntityManager, EntityTarget, Repository } from 'typeorm'
|
||||
import { AppDataSource } from '../server'
|
||||
import { Reminder } from './reminder'
|
||||
import { UploadFile } from './upload_file'
|
||||
import { User } from './user'
|
||||
|
||||
export const setClaims = async (
|
||||
t: EntityManager,
|
||||
|
|
@ -14,3 +17,7 @@ export const setClaims = async (
|
|||
export const getRepository = <T>(entity: EntityTarget<T>): Repository<T> => {
|
||||
return AppDataSource.getRepository(entity)
|
||||
}
|
||||
|
||||
export const userRepository = getRepository(User)
|
||||
export const uploadFileRepository = getRepository(UploadFile)
|
||||
export const reminderRepository = getRepository(Reminder)
|
||||
|
|
@ -3,12 +3,9 @@ import {
|
|||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { LibraryItem } from './library_item'
|
||||
import { User } from './user'
|
||||
|
||||
@Entity({ name: 'labels' })
|
||||
|
|
@ -37,8 +34,4 @@ export class Label {
|
|||
|
||||
@Column('boolean', { default: false })
|
||||
internal!: boolean
|
||||
|
||||
@ManyToMany(() => LibraryItem, (libraryItem) => libraryItem.labels)
|
||||
@JoinTable()
|
||||
libraryItems?: LibraryItem[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export class LibraryItem {
|
|||
originalUrl!: string
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
downloadUrl?: string
|
||||
downloadUrl?: string | null
|
||||
|
||||
@Column('text')
|
||||
slug!: string
|
||||
|
|
@ -73,10 +73,10 @@ export class LibraryItem {
|
|||
title!: string
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
author?: string
|
||||
author?: string | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
description?: string
|
||||
description?: string | null
|
||||
|
||||
@Column('timestamptz')
|
||||
savedAt?: Date
|
||||
|
|
@ -85,49 +85,49 @@ export class LibraryItem {
|
|||
createdAt?: Date
|
||||
|
||||
@Column('timestamptz', { nullable: true })
|
||||
publishedAt?: Date
|
||||
publishedAt?: Date | null
|
||||
|
||||
@Column('timestamptz')
|
||||
archivedAt?: Date
|
||||
archivedAt?: Date | null
|
||||
|
||||
@Column('timestamptz')
|
||||
deletedAt?: Date
|
||||
deletedAt?: Date | null
|
||||
|
||||
@Column('timestamptz')
|
||||
readAt?: Date
|
||||
readAt?: Date | null
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt?: Date
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
itemLanguage?: string
|
||||
itemLanguage?: string | null
|
||||
|
||||
@Column('integer', { nullable: true })
|
||||
wordCount?: number
|
||||
wordCount?: number | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
siteName?: string
|
||||
siteName?: string | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
siteIcon?: string
|
||||
siteIcon?: string | null
|
||||
|
||||
@Column('json', { nullable: true })
|
||||
metadata?: Record<string, unknown>
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Column('integer', { nullable: true })
|
||||
readingProgressLastReadAnchor?: number
|
||||
readingProgressLastReadAnchor?: number | null
|
||||
|
||||
@Column('integer', { nullable: true })
|
||||
readingProgressHighestReadAnchor?: number
|
||||
readingProgressHighestReadAnchor?: number | null
|
||||
|
||||
@Column('real', { nullable: true })
|
||||
readingProgressTopPercent?: number
|
||||
readingProgressTopPercent?: number | null
|
||||
|
||||
@Column('real', { nullable: true })
|
||||
readingProgressBottomPercent?: number
|
||||
readingProgressBottomPercent?: number | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
thumbnail?: string
|
||||
thumbnail?: string | null
|
||||
|
||||
@Column('enum', { enum: LibraryItemType, default: LibraryItemType.Unknown })
|
||||
itemType?: LibraryItemType
|
||||
|
|
@ -140,47 +140,29 @@ export class LibraryItem {
|
|||
contentReader?: ContentReaderType
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
originalContent?: string
|
||||
originalContent?: string | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
readableContent?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
contentTsv?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
siteTsv?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
titleTsv?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
authorTsv?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
descriptionTsv?: string
|
||||
|
||||
@Column('tsvector', { nullable: true })
|
||||
searchTsv?: string
|
||||
readableContent?: string | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
modelName?: string
|
||||
modelName?: string | null
|
||||
|
||||
// NOT SUPPORTED IN TYPEORM
|
||||
// @Column('vector', { nullable: true })
|
||||
// embedding?: number[]
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
textContentHash?: string
|
||||
textContentHash?: string | null
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
gcsArchiveId?: string
|
||||
gcsArchiveId?: string | null
|
||||
|
||||
@OneToOne(() => Subscription, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'subscription_id' })
|
||||
subscription?: Subscription
|
||||
|
||||
@ManyToMany(() => Label, (label) => label.libraryItems)
|
||||
@ManyToMany(() => Label)
|
||||
@JoinTable()
|
||||
labels?: Label[]
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ export class Profile {
|
|||
@Column('text')
|
||||
username!: string
|
||||
|
||||
@Column('text')
|
||||
bio!: string
|
||||
@Column('text', { nullable: true })
|
||||
bio?: string | null
|
||||
|
||||
@Column('text')
|
||||
pictureUrl!: string
|
||||
@Column('text', { nullable: true })
|
||||
pictureUrl?: string | null
|
||||
|
||||
@OneToOne(() => User, (user) => user.profile)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
|
|
|
|||
|
|
@ -7,13 +7,23 @@ import {
|
|||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { RegistrationType, StatusType } from '../datalayer/user/model'
|
||||
import { Label } from './label'
|
||||
import { NewsletterEmail } from './newsletter_email'
|
||||
import { Profile } from './profile'
|
||||
import { Label } from './label'
|
||||
import { Subscription } from './subscription'
|
||||
import { UserPersonalization } from './user_personalization'
|
||||
|
||||
export enum RegistrationType {
|
||||
Google = 'GOOGLE',
|
||||
Apple = 'APPLE',
|
||||
Email = 'EMAIL',
|
||||
}
|
||||
|
||||
export enum StatusType {
|
||||
Active = 'ACTIVE',
|
||||
Pending = 'PENDING',
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
|
@ -40,7 +50,10 @@ export class User {
|
|||
@OneToMany(() => NewsletterEmail, (newsletterEmail) => newsletterEmail.user)
|
||||
newsletterEmails?: NewsletterEmail[]
|
||||
|
||||
@OneToOne(() => Profile, (profile) => profile.user, { eager: true })
|
||||
@OneToOne(() => Profile, (profile) => profile.user, {
|
||||
eager: true,
|
||||
cascade: true,
|
||||
})
|
||||
profile!: Profile
|
||||
|
||||
@Column('varchar', { length: 255, nullable: true })
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export class CreateIntercomAccount
|
|||
email: profile.user.email,
|
||||
externalId: profile.user.id,
|
||||
name: profile.user.name,
|
||||
avatar: profile.pictureUrl,
|
||||
avatar: profile.pictureUrl || undefined,
|
||||
customAttributes: customAttributes,
|
||||
signedUpAt: Math.floor(Date.now() / 1000),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getRepository } from '../../entity'
|
||||
import { ApiKey } from '../../entity/api_key'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
ApiKeysError,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ import {
|
|||
PageType,
|
||||
SearchItem as SearchItemData,
|
||||
} from '../../elastic/types'
|
||||
import { getRepository } from '../../entity'
|
||||
import { UploadFile } from '../../entity/upload_file'
|
||||
import { User } from '../../entity/user'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
Article,
|
||||
|
|
@ -42,7 +45,6 @@ import {
|
|||
MutationSaveArticleReadingProgressArgs,
|
||||
MutationSetBookmarkArticleArgs,
|
||||
MutationSetFavoriteArticleArgs,
|
||||
MutationSetShareArticleArgs,
|
||||
PageInfo,
|
||||
QueryArticleArgs,
|
||||
QueryArticlesArgs,
|
||||
|
|
@ -63,8 +65,6 @@ import {
|
|||
SetFavoriteArticleError,
|
||||
SetFavoriteArticleErrorCode,
|
||||
SetFavoriteArticleSuccess,
|
||||
SetShareArticleError,
|
||||
SetShareArticleErrorCode,
|
||||
SetShareArticleSuccess,
|
||||
SortParams,
|
||||
TypeaheadSearchError,
|
||||
|
|
@ -82,6 +82,7 @@ import {
|
|||
createLabels,
|
||||
getLabelsByIds,
|
||||
} from '../../services/labels'
|
||||
import { setFileUploadComplete } from '../../services/save_file'
|
||||
import { parsedContentToPage } from '../../services/save_page'
|
||||
import { traceAs } from '../../tracing'
|
||||
import { Merge } from '../../util'
|
||||
|
|
@ -167,12 +168,7 @@ export const createArticleResolver = authorized<
|
|||
},
|
||||
ctx
|
||||
) => {
|
||||
const {
|
||||
models,
|
||||
authTrx,
|
||||
claims: { uid },
|
||||
log,
|
||||
} = ctx
|
||||
const { authTrx, log, uid } = ctx
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
|
|
@ -184,7 +180,18 @@ export const createArticleResolver = authorized<
|
|||
},
|
||||
})
|
||||
|
||||
const user = userDataToUser(await models.user.get(uid))
|
||||
const userData = await getRepository(User).findOneBy({ id: uid })
|
||||
if (!userData) {
|
||||
return pageError(
|
||||
{
|
||||
errorCodes: [CreateArticleErrorCode.Unauthorized],
|
||||
},
|
||||
ctx,
|
||||
pageId
|
||||
)
|
||||
}
|
||||
const user = userDataToUser(userData)
|
||||
|
||||
try {
|
||||
if (isSiteBlockedForParse(url)) {
|
||||
return pageError(
|
||||
|
|
@ -235,14 +242,17 @@ export const createArticleResolver = authorized<
|
|||
},
|
||||
}
|
||||
// save state
|
||||
let archivedAt =
|
||||
const archivedAt =
|
||||
state === ArticleSavingRequestStatus.Archived ? new Date() : null
|
||||
if (pageId) {
|
||||
const reminder = await models.reminder.getByRequestId(uid, pageId)
|
||||
if (reminder && reminder.archiveUntil) {
|
||||
archivedAt = new Date()
|
||||
}
|
||||
}
|
||||
// if (pageId) {
|
||||
// const reminder = await getRepository(Reminder).findOneBy({
|
||||
// articleSavingRequest: pageId,
|
||||
// user: { id: uid },
|
||||
// })
|
||||
// if (reminder && reminder.archiveUntil) {
|
||||
// archivedAt = new Date()
|
||||
// }
|
||||
// }
|
||||
// add labels to page
|
||||
const labels = inputLabels
|
||||
? await createLabels(ctx, inputLabels)
|
||||
|
|
@ -252,9 +262,9 @@ export const createArticleResolver = authorized<
|
|||
/* We do not trust the values from client, lookup upload file by querying
|
||||
* with filtering on user ID and URL to verify client's uploadFileId is valid.
|
||||
*/
|
||||
const uploadFile = await models.uploadFile.getWhere({
|
||||
const uploadFile = await getRepository(UploadFile).findOneBy({
|
||||
id: uploadFileId,
|
||||
userId: uid,
|
||||
user: { id: uid },
|
||||
})
|
||||
if (!uploadFile) {
|
||||
return pageError(
|
||||
|
|
@ -330,7 +340,7 @@ export const createArticleResolver = authorized<
|
|||
|
||||
if (uploadFileId) {
|
||||
const uploadFileData = await authTrx(async (tx) => {
|
||||
return models.uploadFile.setFileUploadComplete(uploadFileId, tx)
|
||||
return setFileUploadComplete(uploadFileId, tx)
|
||||
})
|
||||
if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) {
|
||||
return pageError(
|
||||
|
|
@ -579,64 +589,65 @@ export type SetShareArticleSuccessPartial = Merge<
|
|||
}
|
||||
>
|
||||
|
||||
export const setShareArticleResolver = authorized<
|
||||
SetShareArticleSuccessPartial,
|
||||
SetShareArticleError,
|
||||
MutationSetShareArticleArgs
|
||||
>(
|
||||
async (
|
||||
_,
|
||||
{ input: { articleID, share, sharedComment, sharedWithHighlights } },
|
||||
{ models, authTrx, claims: { uid }, log }
|
||||
) => {
|
||||
const article = await models.article.get(articleID)
|
||||
if (!article) {
|
||||
return { errorCodes: [SetShareArticleErrorCode.NotFound] }
|
||||
}
|
||||
// TODO: not implemented yet
|
||||
// export const setShareArticleResolver = authorized<
|
||||
// SetShareArticleSuccessPartial,
|
||||
// SetShareArticleError,
|
||||
// MutationSetShareArticleArgs
|
||||
// >(
|
||||
// async (
|
||||
// _,
|
||||
// { input: { articleID, share, sharedComment, sharedWithHighlights } },
|
||||
// { models, authTrx, claims: { uid }, log }
|
||||
// ) => {
|
||||
// const article = await models.article.get(articleID)
|
||||
// if (!article) {
|
||||
// return { errorCodes: [SetShareArticleErrorCode.NotFound] }
|
||||
// }
|
||||
|
||||
const sharedAt = share ? new Date() : null
|
||||
// const sharedAt = share ? new Date() : null
|
||||
|
||||
log.info(`${share ? 'S' : 'Uns'}haring an article`, {
|
||||
article: Object.assign({}, article, {
|
||||
content: undefined,
|
||||
originalHtml: undefined,
|
||||
sharedAt,
|
||||
}),
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'setShareArticleResolver',
|
||||
articleId: article.id,
|
||||
userId: uid,
|
||||
},
|
||||
})
|
||||
// log.info(`${share ? 'S' : 'Uns'}haring an article`, {
|
||||
// article: Object.assign({}, article, {
|
||||
// content: undefined,
|
||||
// originalHtml: undefined,
|
||||
// sharedAt,
|
||||
// }),
|
||||
// labels: {
|
||||
// source: 'resolver',
|
||||
// resolver: 'setShareArticleResolver',
|
||||
// articleId: article.id,
|
||||
// userId: uid,
|
||||
// },
|
||||
// })
|
||||
|
||||
const result = await authTrx((tx) =>
|
||||
models.userArticle.updateByArticleId(
|
||||
uid,
|
||||
articleID,
|
||||
{ sharedAt, sharedComment, sharedWithHighlights },
|
||||
tx
|
||||
)
|
||||
)
|
||||
// const result = await authTrx((tx) =>
|
||||
// models.userArticle.updateByArticleId(
|
||||
// uid,
|
||||
// articleID,
|
||||
// { sharedAt, sharedComment, sharedWithHighlights },
|
||||
// tx
|
||||
// )
|
||||
// )
|
||||
|
||||
if (!result) {
|
||||
return { errorCodes: [SetShareArticleErrorCode.NotFound] }
|
||||
}
|
||||
// if (!result) {
|
||||
// return { errorCodes: [SetShareArticleErrorCode.NotFound] }
|
||||
// }
|
||||
|
||||
// Make sure article.id instead of userArticle.id has passed. We use it for cache updates
|
||||
const updatedArticle = {
|
||||
...result,
|
||||
...article,
|
||||
postedByViewer: !!sharedAt,
|
||||
}
|
||||
const updatedFeedArticle = sharedAt ? { ...result, sharedAt } : undefined
|
||||
return {
|
||||
updatedFeedArticleId: result.id,
|
||||
updatedFeedArticle,
|
||||
updatedArticle,
|
||||
}
|
||||
}
|
||||
)
|
||||
// // Make sure article.id instead of userArticle.id has passed. We use it for cache updates
|
||||
// const updatedArticle = {
|
||||
// ...result,
|
||||
// ...article,
|
||||
// postedByViewer: !!sharedAt,
|
||||
// }
|
||||
// const updatedFeedArticle = sharedAt ? { ...result, sharedAt } : undefined
|
||||
// return {
|
||||
// updatedFeedArticleId: result.id,
|
||||
// updatedFeedArticle,
|
||||
// updatedArticle,
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
export type SetBookmarkArticleSuccessPartial = Merge<
|
||||
SetBookmarkArticleSuccess,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable prefer-const */
|
||||
import { getPageByParam } from '../../elastic/pages'
|
||||
import { getRepository } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
ArticleSavingRequestError,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { authorized } from '../../utils/helpers'
|
||||
import { Between } from 'typeorm'
|
||||
import { getRepository, setClaims } from '../../entity'
|
||||
import { Filter } from '../../entity/filter'
|
||||
import { User } from '../../entity/user'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
DeleteFilterError,
|
||||
DeleteFilterErrorCode,
|
||||
|
|
@ -20,14 +24,11 @@ import {
|
|||
UpdateFilterSuccess,
|
||||
UpdateFilterErrorCode,
|
||||
} from '../../generated/graphql'
|
||||
import { Filter } from '../../entity/filter'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { User } from '../../entity/user'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { Between } from 'typeorm'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
import { isNil, mergeWith } from 'lodash'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const saveFilterResolver = authorized<
|
||||
SaveFilterSuccess,
|
||||
|
|
|
|||
|
|
@ -3,23 +3,21 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { getShareInfoForArticle } from '../datalayer/links/share_info'
|
||||
import { getPageByParam } from '../elastic/pages'
|
||||
import { getRepository } from '../entity'
|
||||
import { Subscription } from '../entity/subscription'
|
||||
import { UploadFile } from '../entity/upload_file'
|
||||
import { User } from '../entity/user'
|
||||
import {
|
||||
Article,
|
||||
ArticleHighlightsInput,
|
||||
Highlight,
|
||||
HighlightType,
|
||||
LinkShareInfo,
|
||||
PageType,
|
||||
Reaction,
|
||||
SearchItem,
|
||||
User,
|
||||
} from '../generated/graphql'
|
||||
import { userDataToUser, validatedDate, wordsCount } from '../utils/helpers'
|
||||
import { createImageProxyUrl } from '../utils/imageproxy'
|
||||
import { logger } from '../utils/logger'
|
||||
import {
|
||||
contentReaderForPage,
|
||||
generateDownloadSignedUrl,
|
||||
|
|
@ -38,14 +36,14 @@ import {
|
|||
createHighlightResolver,
|
||||
createLabelResolver,
|
||||
createNewsletterEmailResolver,
|
||||
createReminderResolver,
|
||||
// createReminderResolver,
|
||||
deleteAccountResolver,
|
||||
deleteFilterResolver,
|
||||
deleteHighlightResolver,
|
||||
deleteIntegrationResolver,
|
||||
deleteLabelResolver,
|
||||
deleteNewsletterEmailResolver,
|
||||
deleteReminderResolver,
|
||||
// deleteReminderResolver,
|
||||
deleteRuleResolver,
|
||||
deleteWebhookResolver,
|
||||
deviceTokensResolver,
|
||||
|
|
@ -54,11 +52,11 @@ import {
|
|||
getAllUsersResolver,
|
||||
getArticleResolver,
|
||||
getArticlesResolver,
|
||||
getFollowersResolver,
|
||||
getFollowingResolver,
|
||||
// getFollowersResolver,
|
||||
// getFollowingResolver,
|
||||
getMeUserResolver,
|
||||
getSharedArticleResolver,
|
||||
getUserFeedArticlesResolver,
|
||||
// getSharedArticleResolver,
|
||||
// getUserFeedArticlesResolver,
|
||||
getUserPersonalizationResolver,
|
||||
getUserResolver,
|
||||
googleLoginResolver,
|
||||
|
|
@ -76,7 +74,7 @@ import {
|
|||
newsletterEmailsResolver,
|
||||
recommendHighlightsResolver,
|
||||
recommendResolver,
|
||||
reminderResolver,
|
||||
// reminderResolver,
|
||||
reportItemResolver,
|
||||
revokeApiKeyResolver,
|
||||
rulesResolver,
|
||||
|
|
@ -90,13 +88,13 @@ import {
|
|||
setBookmarkArticleResolver,
|
||||
setDeviceTokenResolver,
|
||||
setFavoriteArticleResolver,
|
||||
setFollowResolver,
|
||||
// setFollowResolver,
|
||||
setIntegrationResolver,
|
||||
setLabelsForHighlightResolver,
|
||||
setLabelsResolver,
|
||||
setLinkArchivedResolver,
|
||||
setRuleResolver,
|
||||
setShareArticleResolver,
|
||||
// setShareArticleResolver,
|
||||
setShareHighlightResolver,
|
||||
setUserPersonalizationResolver,
|
||||
setWebhookResolver,
|
||||
|
|
@ -107,10 +105,10 @@ import {
|
|||
updateFilterResolver,
|
||||
updateHighlightResolver,
|
||||
updateLabelResolver,
|
||||
updateLinkShareInfoResolver,
|
||||
// updateLinkShareInfoResolver,
|
||||
updatePageResolver,
|
||||
updateReminderResolver,
|
||||
updateSharedCommentResolver,
|
||||
// updateReminderResolver,
|
||||
// updateSharedCommentResolver,
|
||||
updatesSinceResolver,
|
||||
updateSubscriptionResolver,
|
||||
updateUserProfileResolver,
|
||||
|
|
@ -120,7 +118,6 @@ import {
|
|||
webhookResolver,
|
||||
webhooksResolver,
|
||||
} from './index'
|
||||
import { createReactionResolver, deleteReactionResolver } from './reaction'
|
||||
import { markEmailAsItemResolver, recentEmailsResolver } from './recent_emails'
|
||||
import { recentSearchesResolver } from './recent_searches'
|
||||
import { Claims, WithDataSourcesContext } from './types'
|
||||
|
|
@ -154,30 +151,30 @@ export const functionResolvers = {
|
|||
updateUserProfile: updateUserProfileResolver,
|
||||
createArticle: createArticleResolver,
|
||||
createHighlight: createHighlightResolver,
|
||||
createReaction: createReactionResolver,
|
||||
deleteReaction: deleteReactionResolver,
|
||||
// createReaction: createReactionResolver,
|
||||
// deleteReaction: deleteReactionResolver,
|
||||
mergeHighlight: mergeHighlightResolver,
|
||||
updateHighlight: updateHighlightResolver,
|
||||
deleteHighlight: deleteHighlightResolver,
|
||||
uploadFileRequest: uploadFileRequestResolver,
|
||||
setShareArticle: setShareArticleResolver,
|
||||
updateSharedComment: updateSharedCommentResolver,
|
||||
setFollow: setFollowResolver,
|
||||
// setShareArticle: setShareArticleResolver,
|
||||
// updateSharedComment: updateSharedCommentResolver,
|
||||
// setFollow: setFollowResolver,
|
||||
setBookmarkArticle: setBookmarkArticleResolver,
|
||||
setUserPersonalization: setUserPersonalizationResolver,
|
||||
createArticleSavingRequest: createArticleSavingRequestResolver,
|
||||
setShareHighlight: setShareHighlightResolver,
|
||||
reportItem: reportItemResolver,
|
||||
updateLinkShareInfo: updateLinkShareInfoResolver,
|
||||
// updateLinkShareInfo: updateLinkShareInfoResolver,
|
||||
setLinkArchived: setLinkArchivedResolver,
|
||||
createNewsletterEmail: createNewsletterEmailResolver,
|
||||
deleteNewsletterEmail: deleteNewsletterEmailResolver,
|
||||
saveUrl: saveUrlResolver,
|
||||
savePage: savePageResolver,
|
||||
saveFile: saveFileResolver,
|
||||
createReminder: createReminderResolver,
|
||||
updateReminder: updateReminderResolver,
|
||||
deleteReminder: deleteReminderResolver,
|
||||
// createReminder: createReminderResolver,
|
||||
// updateReminder: updateReminderResolver,
|
||||
// deleteReminder: deleteReminderResolver,
|
||||
setDeviceToken: setDeviceTokenResolver,
|
||||
createLabel: createLabelResolver,
|
||||
updateLabel: updateLabelResolver,
|
||||
|
|
@ -221,15 +218,15 @@ export const functionResolvers = {
|
|||
users: getAllUsersResolver,
|
||||
validateUsername: validateUsernameResolver,
|
||||
article: getArticleResolver,
|
||||
sharedArticle: getSharedArticleResolver,
|
||||
// sharedArticle: getSharedArticleResolver,
|
||||
articles: getArticlesResolver,
|
||||
feedArticles: getUserFeedArticlesResolver,
|
||||
getFollowers: getFollowersResolver,
|
||||
getFollowing: getFollowingResolver,
|
||||
// feedArticles: getUserFeedArticlesResolver,
|
||||
// getFollowers: getFollowersResolver,
|
||||
// getFollowing: getFollowingResolver,
|
||||
getUserPersonalization: getUserPersonalizationResolver,
|
||||
articleSavingRequest: articleSavingRequestResolver,
|
||||
newsletterEmails: newsletterEmailsResolver,
|
||||
reminder: reminderResolver,
|
||||
// reminder: reminderResolver,
|
||||
labels: labelsResolver,
|
||||
search: searchResolver,
|
||||
subscriptions: subscriptionsResolver,
|
||||
|
|
@ -247,141 +244,141 @@ export const functionResolvers = {
|
|||
groups: groupsResolver,
|
||||
recentEmails: recentEmailsResolver,
|
||||
},
|
||||
User: {
|
||||
async sharedArticles(
|
||||
user: User,
|
||||
__: Record<string, unknown>,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
return ctx.models.userArticle.getUserSharedArticles(user.id, ctx.kx)
|
||||
},
|
||||
async sharedArticlesCount(
|
||||
user: { id: string; sharedArticlesCount?: number },
|
||||
__: Record<string, unknown>,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (user.sharedArticlesCount) return user.sharedArticlesCount
|
||||
return ctx.models.userArticle.getSharedArticlesCount(user.id, ctx.kx)
|
||||
},
|
||||
async sharedHighlightsCount(
|
||||
user: { id: string; sharedHighlightsCount?: number },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
// #TODO: restructure highlightStats and sharedArticlesCount in order to get it within a single query
|
||||
if (user.sharedHighlightsCount) return user.sharedHighlightsCount
|
||||
const { sharedHighlightsCount } =
|
||||
await ctx.models.user.getSharedHighlightsStats(user.id)
|
||||
return sharedHighlightsCount
|
||||
},
|
||||
async sharedNotesCount(
|
||||
user: User,
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (user.sharedNotesCount) return user.sharedNotesCount
|
||||
const { sharedNotesCount } =
|
||||
await ctx.models.user.getSharedHighlightsStats(user.id)
|
||||
return sharedNotesCount
|
||||
},
|
||||
},
|
||||
FeedArticle: {
|
||||
async article(
|
||||
feedArticle: { articleId: string; userId: string; article?: Article },
|
||||
__: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (feedArticle.article) return feedArticle.article
|
||||
// User: {
|
||||
// async sharedArticles(
|
||||
// user: User,
|
||||
// __: Record<string, unknown>,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// return ctx.models.userArticle.getUserSharedArticles(user.id, ctx.kx)
|
||||
// },
|
||||
// async sharedArticlesCount(
|
||||
// user: { id: string; sharedArticlesCount?: number },
|
||||
// __: Record<string, unknown>,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (user.sharedArticlesCount) return user.sharedArticlesCount
|
||||
// return ctx.models.userArticle.getSharedArticlesCount(user.id, ctx.kx)
|
||||
// },
|
||||
// async sharedHighlightsCount(
|
||||
// user: { id: string; sharedHighlightsCount?: number },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// // #TODO: restructure highlightStats and sharedArticlesCount in order to get it within a single query
|
||||
// if (user.sharedHighlightsCount) return user.sharedHighlightsCount
|
||||
// const { sharedHighlightsCount } =
|
||||
// await ctx.models.user.getSharedHighlightsStats(user.id)
|
||||
// return sharedHighlightsCount
|
||||
// },
|
||||
// async sharedNotesCount(
|
||||
// user: User,
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (user.sharedNotesCount) return user.sharedNotesCount
|
||||
// const { sharedNotesCount } =
|
||||
// await ctx.models.user.getSharedHighlightsStats(user.id)
|
||||
// return sharedNotesCount
|
||||
// },
|
||||
// },
|
||||
// FeedArticle: {
|
||||
// async article(
|
||||
// feedArticle: { articleId: string; userId: string; article?: Article },
|
||||
// __: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (feedArticle.article) return feedArticle.article
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let a: any
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// let a: any
|
||||
|
||||
const savedArticle =
|
||||
ctx.claims?.uid &&
|
||||
(await ctx.models.userArticle.getForUser(
|
||||
ctx.claims?.uid,
|
||||
feedArticle.articleId,
|
||||
ctx.kx
|
||||
))
|
||||
// const savedArticle =
|
||||
// ctx.claims?.uid &&
|
||||
// (await ctx.models.userArticle.getForUser(
|
||||
// ctx.claims?.uid,
|
||||
// feedArticle.articleId,
|
||||
// ctx.kx
|
||||
// ))
|
||||
|
||||
if (savedArticle) {
|
||||
// If user has saved the article, use his version (slug) then
|
||||
a = {
|
||||
...savedArticle,
|
||||
savedByViewer: true,
|
||||
postedByViewer: !!savedArticle.sharedAt,
|
||||
}
|
||||
} else {
|
||||
a = await ctx.models.userArticle.getForUser(
|
||||
feedArticle.userId,
|
||||
feedArticle.articleId,
|
||||
ctx.kx
|
||||
)
|
||||
}
|
||||
// if (savedArticle) {
|
||||
// // If user has saved the article, use his version (slug) then
|
||||
// a = {
|
||||
// ...savedArticle,
|
||||
// savedByViewer: true,
|
||||
// postedByViewer: !!savedArticle.sharedAt,
|
||||
// }
|
||||
// } else {
|
||||
// a = await ctx.models.userArticle.getForUser(
|
||||
// feedArticle.userId,
|
||||
// feedArticle.articleId,
|
||||
// ctx.kx
|
||||
// )
|
||||
// }
|
||||
|
||||
if (a && a.image) {
|
||||
a.image = createImageProxyUrl(a.image, 0, 180)
|
||||
} else {
|
||||
logger.info(
|
||||
'error getting article for feedItem',
|
||||
feedArticle.userId,
|
||||
feedArticle.articleId
|
||||
)
|
||||
}
|
||||
// if (a && a.image) {
|
||||
// a.image = createImageProxyUrl(a.image, 0, 180)
|
||||
// } else {
|
||||
// logger.info(
|
||||
// 'error getting article for feedItem',
|
||||
// feedArticle.userId,
|
||||
// feedArticle.articleId
|
||||
// )
|
||||
// }
|
||||
|
||||
return a
|
||||
},
|
||||
async sharedBy(
|
||||
feedArticle: { userId: string; sharedBy?: User },
|
||||
__: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (feedArticle.sharedBy) return feedArticle.sharedBy
|
||||
return userDataToUser(await ctx.models.user.get(feedArticle.userId))
|
||||
},
|
||||
async highlight(
|
||||
feedArticle: { highlightId?: string; highlight?: Highlight },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (feedArticle.highlight) return feedArticle.highlight
|
||||
return feedArticle.highlightId
|
||||
? await ctx.models.highlight.get(feedArticle.highlightId)
|
||||
: null
|
||||
},
|
||||
async reactions(
|
||||
feedArticle: { id: string; reactions?: Reaction[] },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
const { reactions, id } = feedArticle
|
||||
if (reactions) return reactions
|
||||
// return a
|
||||
// },
|
||||
// async sharedBy(
|
||||
// feedArticle: { userId: string; sharedBy?: User },
|
||||
// __: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (feedArticle.sharedBy) return feedArticle.sharedBy
|
||||
// return userDataToUser(await ctx.models.user.get(feedArticle.userId))
|
||||
// },
|
||||
// async highlight(
|
||||
// feedArticle: { highlightId?: string; highlight?: Highlight },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (feedArticle.highlight) return feedArticle.highlight
|
||||
// return feedArticle.highlightId
|
||||
// ? await ctx.models.highlight.get(feedArticle.highlightId)
|
||||
// : null
|
||||
// },
|
||||
// async reactions(
|
||||
// feedArticle: { id: string; reactions?: Reaction[] },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// const { reactions, id } = feedArticle
|
||||
// if (reactions) return reactions
|
||||
|
||||
return await ctx.models.reaction.batchGetFromArticle(id)
|
||||
},
|
||||
async highlightsCount(
|
||||
feedArticle: { id: string; highlightsCount?: number },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (feedArticle.highlightsCount) return feedArticle.highlightsCount
|
||||
const { highlightsCount } = await ctx.models.userArticle.getStats(
|
||||
feedArticle.id
|
||||
)
|
||||
return highlightsCount
|
||||
},
|
||||
async annotationsCount(
|
||||
feedArticle: { id: string; annotationsCount?: number },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
if (feedArticle.annotationsCount) return feedArticle.annotationsCount
|
||||
const { annotationsCount } = await ctx.models.userArticle.getStats(
|
||||
feedArticle.id
|
||||
)
|
||||
return annotationsCount
|
||||
},
|
||||
},
|
||||
// return await ctx.models.reaction.batchGetFromArticle(id)
|
||||
// },
|
||||
// async highlightsCount(
|
||||
// feedArticle: { id: string; highlightsCount?: number },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (feedArticle.highlightsCount) return feedArticle.highlightsCount
|
||||
// const { highlightsCount } = await ctx.models.userArticle.getStats(
|
||||
// feedArticle.id
|
||||
// )
|
||||
// return highlightsCount
|
||||
// },
|
||||
// async annotationsCount(
|
||||
// feedArticle: { id: string; annotationsCount?: number },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// if (feedArticle.annotationsCount) return feedArticle.annotationsCount
|
||||
// const { annotationsCount } = await ctx.models.userArticle.getStats(
|
||||
// feedArticle.id
|
||||
// )
|
||||
// return annotationsCount
|
||||
// },
|
||||
// },
|
||||
Article: {
|
||||
async url(article: Article, _: unknown, ctx: WithDataSourcesContext) {
|
||||
if (
|
||||
|
|
@ -390,7 +387,9 @@ export const functionResolvers = {
|
|||
ctx.claims &&
|
||||
article.uploadFileId
|
||||
) {
|
||||
const upload = await ctx.models.uploadFile.get(article.uploadFileId)
|
||||
const upload = await getRepository(UploadFile).findOneBy({
|
||||
id: article.uploadFileId,
|
||||
})
|
||||
if (!upload || !upload.fileName) {
|
||||
return undefined
|
||||
}
|
||||
|
|
@ -490,20 +489,20 @@ export const functionResolvers = {
|
|||
) {
|
||||
return article.highlights || []
|
||||
},
|
||||
async shareInfo(
|
||||
article: { id: string; sharedBy?: User; shareInfo?: LinkShareInfo },
|
||||
__: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
): Promise<LinkShareInfo | undefined> {
|
||||
if (article.shareInfo) return article.shareInfo
|
||||
if (!ctx.claims?.uid) return undefined
|
||||
return getShareInfoForArticle(
|
||||
ctx.kx,
|
||||
ctx.claims?.uid,
|
||||
article.id,
|
||||
ctx.models
|
||||
)
|
||||
},
|
||||
// async shareInfo(
|
||||
// article: { id: string; sharedBy?: User; shareInfo?: LinkShareInfo },
|
||||
// __: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ): Promise<LinkShareInfo | undefined> {
|
||||
// if (article.shareInfo) return article.shareInfo
|
||||
// if (!ctx.claims?.uid) return undefined
|
||||
// return getShareInfoForArticle(
|
||||
// ctx.kx,
|
||||
// ctx.claims?.uid,
|
||||
// article.id,
|
||||
// ctx.models
|
||||
// )
|
||||
// },
|
||||
image(article: { image?: string }): string | undefined {
|
||||
return article.image && createImageProxyUrl(article.image, 320, 320)
|
||||
},
|
||||
|
|
@ -528,18 +527,23 @@ export const functionResolvers = {
|
|||
__: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
return userDataToUser(await ctx.models.user.get(highlight.userId))
|
||||
},
|
||||
async reactions(
|
||||
highlight: { id: string; reactions?: Reaction[] },
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
const { reactions, id } = highlight
|
||||
if (reactions) return reactions
|
||||
const userData = await getRepository(User).findOneBy({
|
||||
id: highlight.userId,
|
||||
})
|
||||
if (!userData) return null
|
||||
|
||||
return await ctx.models.reaction.batchGetFromHighlight(id)
|
||||
return userDataToUser(userData)
|
||||
},
|
||||
// async reactions(
|
||||
// highlight: { id: string; reactions?: Reaction[] },
|
||||
// _: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// const { reactions, id } = highlight
|
||||
// if (reactions) return reactions
|
||||
|
||||
// return await ctx.models.reaction.batchGetFromHighlight(id)
|
||||
// },
|
||||
async createdByMe(
|
||||
highlight: { userId: string; createdByMe?: boolean },
|
||||
__: unknown,
|
||||
|
|
@ -551,15 +555,15 @@ export const functionResolvers = {
|
|||
return highlight.type || HighlightType.Highlight
|
||||
},
|
||||
},
|
||||
Reaction: {
|
||||
async user(
|
||||
reaction: { userId: string },
|
||||
__: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
return userDataToUser(await ctx.models.user.get(reaction.userId))
|
||||
},
|
||||
},
|
||||
// Reaction: {
|
||||
// async user(
|
||||
// reaction: { userId: string },
|
||||
// __: unknown,
|
||||
// ctx: WithDataSourcesContext
|
||||
// ) {
|
||||
// return userDataToUser(await ctx.models.user.get(reaction.userId))
|
||||
// },
|
||||
// },
|
||||
SearchItem: {
|
||||
async url(item: SearchItem, _: unknown, ctx: WithDataSourcesContext) {
|
||||
if (
|
||||
|
|
@ -567,7 +571,9 @@ export const functionResolvers = {
|
|||
ctx.claims &&
|
||||
item.uploadFileId
|
||||
) {
|
||||
const upload = await ctx.models.uploadFile.get(item.uploadFileId)
|
||||
const upload = await getRepository(UploadFile).findOneBy({
|
||||
id: item.uploadFileId,
|
||||
})
|
||||
if (!upload || !upload.fileName) {
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { DateTime } from 'luxon'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getRepository } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
MutationUploadImportFileArgs,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getRepository } from '../../entity'
|
||||
import { Integration, IntegrationType } from '../../entity/integration'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
DeleteIntegrationError,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import {
|
|||
updateLabelsInPage,
|
||||
} from '../../elastic/labels'
|
||||
import { getPageById } from '../../elastic/pages'
|
||||
import { getRepository, setClaims } from '../../entity'
|
||||
import { Label } from '../../entity/label'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
CreateLabelError,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { createOrUpdateLinkShareInfo } from '../../datalayer/links/share_info'
|
||||
import { updatePage } from '../../elastic/pages'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
|
|
@ -6,52 +5,48 @@ import {
|
|||
ArchiveLinkErrorCode,
|
||||
ArchiveLinkSuccess,
|
||||
MutationSetLinkArchivedArgs,
|
||||
MutationUpdateLinkShareInfoArgs,
|
||||
UpdateLinkShareInfoError,
|
||||
UpdateLinkShareInfoErrorCode,
|
||||
UpdateLinkShareInfoSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const updateLinkShareInfoResolver = authorized<
|
||||
UpdateLinkShareInfoSuccess,
|
||||
UpdateLinkShareInfoError,
|
||||
MutationUpdateLinkShareInfoArgs
|
||||
>(async (_obj, args, { models, claims, authTrx, log }) => {
|
||||
const { title, description } = args.input
|
||||
// export const updateLinkShareInfoResolver = authorized<
|
||||
// UpdateLinkShareInfoSuccess,
|
||||
// UpdateLinkShareInfoError,
|
||||
// MutationUpdateLinkShareInfoArgs
|
||||
// >(async (_obj, args, { models, claims, authTrx, log }) => {
|
||||
// const { title, description } = args.input
|
||||
|
||||
log.info('updateLinkShareInfoResolver', args.input.linkId, title, description)
|
||||
// log.info('updateLinkShareInfoResolver', args.input.linkId, title, description)
|
||||
|
||||
// TEMP: because the old API uses articles instead of Links, we are actually
|
||||
// getting an article ID here and need to map it to a link ID. When the API
|
||||
// is updated to use Links instead of Articles this will be removed.
|
||||
const link = await authTrx((tx) =>
|
||||
models.userArticle.getByArticleId(claims.uid, args.input.linkId, tx)
|
||||
)
|
||||
// // TEMP: because the old API uses articles instead of Links, we are actually
|
||||
// // getting an article ID here and need to map it to a link ID. When the API
|
||||
// // is updated to use Links instead of Articles this will be removed.
|
||||
// const link = await authTrx((tx) =>
|
||||
// models.userArticle.getByArticleId(claims.uid, args.input.linkId, tx)
|
||||
// )
|
||||
|
||||
if (!link?.id) {
|
||||
return {
|
||||
__typename: 'UpdateLinkShareInfoError',
|
||||
errorCodes: [UpdateLinkShareInfoErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
// if (!link?.id) {
|
||||
// return {
|
||||
// __typename: 'UpdateLinkShareInfoError',
|
||||
// errorCodes: [UpdateLinkShareInfoErrorCode.Unauthorized],
|
||||
// }
|
||||
// }
|
||||
|
||||
const result = await authTrx((tx) =>
|
||||
createOrUpdateLinkShareInfo(tx, link.id, title, description)
|
||||
)
|
||||
if (!result) {
|
||||
return {
|
||||
__typename: 'UpdateLinkShareInfoError',
|
||||
errorCodes: [UpdateLinkShareInfoErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
// const result = await authTrx((tx) =>
|
||||
// createOrUpdateLinkShareInfo(tx, link.id, title, description)
|
||||
// )
|
||||
// if (!result) {
|
||||
// return {
|
||||
// __typename: 'UpdateLinkShareInfoError',
|
||||
// errorCodes: [UpdateLinkShareInfoErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
__typename: 'UpdateLinkShareInfoSuccess',
|
||||
message: 'Updated Share Information',
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// __typename: 'UpdateLinkShareInfoSuccess',
|
||||
// message: 'Updated Share Information',
|
||||
// }
|
||||
// })
|
||||
|
||||
export const setLinkArchivedResolver = authorized<
|
||||
ArchiveLinkSuccess,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getRepository } from '../../entity'
|
||||
import { NewsletterEmail } from '../../entity/newsletter_email'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
CreateNewsletterEmailError,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
import { Merge } from '../../util'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
import {
|
||||
CreateReactionError,
|
||||
CreateReactionErrorCode,
|
||||
CreateReactionSuccess,
|
||||
DeleteReactionError,
|
||||
DeleteReactionErrorCode,
|
||||
MutationCreateReactionArgs,
|
||||
MutationDeleteReactionArgs,
|
||||
Reaction,
|
||||
} from './../../generated/graphql'
|
||||
import { CreateReactionSuccess, Reaction } from './../../generated/graphql'
|
||||
|
||||
export type PartialReaction = Omit<Reaction, 'user'>
|
||||
|
||||
|
|
@ -18,125 +8,125 @@ export type PartialCreateReactionSuccess = Merge<
|
|||
{ reaction: PartialReaction }
|
||||
>
|
||||
|
||||
export const createReactionResolver = authorized<
|
||||
PartialCreateReactionSuccess,
|
||||
CreateReactionError,
|
||||
MutationCreateReactionArgs
|
||||
>(async (_, { input }, { models, claims, log, authTrx }) => {
|
||||
const { userArticleId, highlightId } = input
|
||||
// export const createReactionResolver = authorized<
|
||||
// PartialCreateReactionSuccess,
|
||||
// CreateReactionError,
|
||||
// MutationCreateReactionArgs
|
||||
// >(async (_, { input }, { models, claims, log, authTrx }) => {
|
||||
// const { userArticleId, highlightId } = input
|
||||
|
||||
if ((!userArticleId && !highlightId) || (userArticleId && highlightId)) {
|
||||
// One reaction target is required
|
||||
// Higlight replies hasn't supported yet
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
}
|
||||
}
|
||||
// if ((!userArticleId && !highlightId) || (userArticleId && highlightId)) {
|
||||
// // One reaction target is required
|
||||
// // Higlight replies hasn't supported yet
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (input.code && input.code.length > 50) {
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.BadCode],
|
||||
}
|
||||
}
|
||||
// if (input.code && input.code.length > 50) {
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.BadCode],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (userArticleId) {
|
||||
if (!(await models.userArticle.get(userArticleId))) {
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
}
|
||||
}
|
||||
} else if (highlightId) {
|
||||
if (!(await models.highlight.get(highlightId))) {
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (userArticleId) {
|
||||
// if (!(await models.userArticle.get(userArticleId))) {
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
// }
|
||||
// }
|
||||
// } else if (highlightId) {
|
||||
// if (!(await models.highlight.get(highlightId))) {
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.BadTarget],
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
try {
|
||||
const previousReaction = userArticleId
|
||||
? await models.reaction.getByUserAndParam(claims.uid, { userArticleId })
|
||||
: await models.reaction.getByUserAndParam(claims.uid, { highlightId })
|
||||
// try {
|
||||
// const previousReaction = userArticleId
|
||||
// ? await models.reaction.getByUserAndParam(claims.uid, { userArticleId })
|
||||
// : await models.reaction.getByUserAndParam(claims.uid, { highlightId })
|
||||
|
||||
let reaction
|
||||
if (!previousReaction) {
|
||||
reaction = await authTrx((tx) =>
|
||||
models.reaction.create({ ...input, userId: claims.uid }, tx)
|
||||
)
|
||||
} else {
|
||||
reaction = await authTrx((tx) =>
|
||||
models.reaction.update(
|
||||
previousReaction.id,
|
||||
{
|
||||
code: input.code,
|
||||
},
|
||||
tx
|
||||
)
|
||||
)
|
||||
}
|
||||
// let reaction
|
||||
// if (!previousReaction) {
|
||||
// reaction = await authTrx((tx) =>
|
||||
// models.reaction.create({ ...input, userId: claims.uid }, tx)
|
||||
// )
|
||||
// } else {
|
||||
// reaction = await authTrx((tx) =>
|
||||
// models.reaction.update(
|
||||
// previousReaction.id,
|
||||
// {
|
||||
// code: input.code,
|
||||
// },
|
||||
// tx
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
if (!reaction) {
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
log.info(`${previousReaction ? 'Updating' : 'Creating'} a new reaction`, {
|
||||
reaction,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'createReactionResolver',
|
||||
uid: claims.uid,
|
||||
},
|
||||
})
|
||||
// if (!reaction) {
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
// log.info(`${previousReaction ? 'Updating' : 'Creating'} a new reaction`, {
|
||||
// reaction,
|
||||
// labels: {
|
||||
// source: 'resolver',
|
||||
// resolver: 'createReactionResolver',
|
||||
// uid: claims.uid,
|
||||
// },
|
||||
// })
|
||||
|
||||
return {
|
||||
reaction: reaction as PartialReaction,
|
||||
}
|
||||
} catch (err) {
|
||||
log.info(err)
|
||||
return {
|
||||
errorCodes: [CreateReactionErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// reaction: reaction as PartialReaction,
|
||||
// }
|
||||
// } catch (err) {
|
||||
// log.info(err)
|
||||
// return {
|
||||
// errorCodes: [CreateReactionErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
export const deleteReactionResolver = authorized<
|
||||
PartialCreateReactionSuccess,
|
||||
DeleteReactionError,
|
||||
MutationDeleteReactionArgs
|
||||
>(async (_, { id }, { authTrx, models, claims, log }) => {
|
||||
const reaction = await models.reaction.get(id)
|
||||
// export const deleteReactionResolver = authorized<
|
||||
// PartialCreateReactionSuccess,
|
||||
// DeleteReactionError,
|
||||
// MutationDeleteReactionArgs
|
||||
// >(async (_, { id }, { authTrx, models, claims, log }) => {
|
||||
// const reaction = await models.reaction.get(id)
|
||||
|
||||
if (!reaction?.id) {
|
||||
return {
|
||||
errorCodes: [DeleteReactionErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// if (!reaction?.id) {
|
||||
// return {
|
||||
// errorCodes: [DeleteReactionErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (reaction.userId !== claims.uid) {
|
||||
return {
|
||||
errorCodes: [DeleteReactionErrorCode.Forbidden],
|
||||
}
|
||||
}
|
||||
// if (reaction.userId !== claims.uid) {
|
||||
// return {
|
||||
// errorCodes: [DeleteReactionErrorCode.Forbidden],
|
||||
// }
|
||||
// }
|
||||
|
||||
const deleted = await authTrx((tx) => models.reaction.delete(id, tx))
|
||||
// const deleted = await authTrx((tx) => models.reaction.delete(id, tx))
|
||||
|
||||
if ('error' in deleted) {
|
||||
return {
|
||||
errorCodes: [DeleteReactionErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// if ('error' in deleted) {
|
||||
// return {
|
||||
// errorCodes: [DeleteReactionErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
log.info('Deleting a highlight', {
|
||||
deleted,
|
||||
labels: {
|
||||
source: 'resolver',
|
||||
resolver: 'deleteHighlightResolver',
|
||||
uid: claims.uid,
|
||||
},
|
||||
})
|
||||
// log.info('Deleting a highlight', {
|
||||
// deleted,
|
||||
// labels: {
|
||||
// source: 'resolver',
|
||||
// resolver: 'deleteHighlightResolver',
|
||||
// uid: claims.uid,
|
||||
// },
|
||||
// })
|
||||
|
||||
return {
|
||||
reaction: reaction as PartialReaction,
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// reaction: reaction as PartialReaction,
|
||||
// }
|
||||
// })
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ILike } from 'typeorm'
|
||||
import { getRepository } from '../../entity'
|
||||
import { NewsletterEmail } from '../../entity/newsletter_email'
|
||||
import { ReceivedEmail } from '../../entity/received_email'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
MarkEmailAsItemError,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { authorized } from '../../utils/helpers'
|
||||
import { getRepository } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
RecentSearchesError,
|
||||
RecentSearchesErrorCode,
|
||||
RecentSearchesSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRecentSearches } from '../../services/search_history'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const recentSearchesResolver = authorized<
|
||||
RecentSearchesSuccess,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { In } from 'typeorm'
|
||||
import { getPageByParam } from '../../elastic/pages'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Group } from '../../entity/groups/group'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
CreateGroupError,
|
||||
|
|
|
|||
|
|
@ -1,31 +1,4 @@
|
|||
import { DateTime } from 'luxon'
|
||||
import { getPageById } from '../../elastic/pages'
|
||||
import { Page } from '../../elastic/types'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
CreateReminderError,
|
||||
CreateReminderErrorCode,
|
||||
CreateReminderSuccess,
|
||||
DeleteReminderError,
|
||||
DeleteReminderErrorCode,
|
||||
DeleteReminderSuccess,
|
||||
MutationCreateReminderArgs,
|
||||
MutationDeleteReminderArgs,
|
||||
MutationUpdateReminderArgs,
|
||||
QueryReminderArgs,
|
||||
ReminderError,
|
||||
ReminderErrorCode,
|
||||
ReminderSuccess,
|
||||
UpdateReminderError,
|
||||
UpdateReminderErrorCode,
|
||||
UpdateReminderSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { setLinkArchived } from '../../services/archive_link'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { deleteTask, enqueueReminder } from '../../utils/createTask'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
import { logger } from '../../utils/logger'
|
||||
import { DataModels } from '../types'
|
||||
|
||||
const validScheduleTime = (str: string): Date | undefined => {
|
||||
const scheduleTime = DateTime.fromISO(str, { setZone: true }).set({
|
||||
|
|
@ -39,320 +12,320 @@ const validScheduleTime = (str: string): Date | undefined => {
|
|||
return scheduleTime.toJSDate()
|
||||
}
|
||||
|
||||
export const createReminderResolver = authorized<
|
||||
CreateReminderSuccess,
|
||||
CreateReminderError,
|
||||
MutationCreateReminderArgs
|
||||
>(async (_, { input }, { models, claims: { uid }, log }) => {
|
||||
log.info('createReminderResolver')
|
||||
// export const createReminderResolver = authorized<
|
||||
// CreateReminderSuccess,
|
||||
// CreateReminderError,
|
||||
// MutationCreateReminderArgs
|
||||
// >(async (_, { input }, { models, claims: { uid }, log }) => {
|
||||
// log.info('createReminderResolver')
|
||||
|
||||
const { clientRequestId, linkId, archiveUntil, sendNotification } = input
|
||||
// const { clientRequestId, linkId, archiveUntil, sendNotification } = input
|
||||
|
||||
const scheduledTime = validScheduleTime(input.remindAt)
|
||||
if (!scheduledTime) {
|
||||
log.error('Invalid scheduled time', input.remindAt)
|
||||
return {
|
||||
errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
// const scheduledTime = validScheduleTime(input.remindAt)
|
||||
// if (!scheduledTime) {
|
||||
// log.error('Invalid scheduled time', input.remindAt)
|
||||
// return {
|
||||
// errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
|
||||
const pageId = linkId || clientRequestId
|
||||
if (!pageId) {
|
||||
log.error('client request id or link id is required')
|
||||
return {
|
||||
errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
// const pageId = linkId || clientRequestId
|
||||
// if (!pageId) {
|
||||
// log.error('client request id or link id is required')
|
||||
// return {
|
||||
// errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'reminder_created',
|
||||
properties: {
|
||||
clientRequestId,
|
||||
remindAt: scheduledTime,
|
||||
archiveUntil,
|
||||
sendNotification,
|
||||
linkId,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
// analytics.track({
|
||||
// userId: uid,
|
||||
// event: 'reminder_created',
|
||||
// properties: {
|
||||
// clientRequestId,
|
||||
// remindAt: scheduledTime,
|
||||
// archiveUntil,
|
||||
// sendNotification,
|
||||
// linkId,
|
||||
// env: env.server.apiEnv,
|
||||
// },
|
||||
// })
|
||||
|
||||
try {
|
||||
// saving from web
|
||||
const page = await getPageById(pageId)
|
||||
if (!page) {
|
||||
log.error('page not found', pageId)
|
||||
// try {
|
||||
// // saving from web
|
||||
// const page = await getPageById(pageId)
|
||||
// if (!page) {
|
||||
// log.error('page not found', pageId)
|
||||
|
||||
return {
|
||||
errorCodes: [CreateReminderErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// errorCodes: [CreateReminderErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (page.userId !== uid) {
|
||||
log.error('user not authorized', uid)
|
||||
// if (page.userId !== uid) {
|
||||
// log.error('user not authorized', uid)
|
||||
|
||||
return {
|
||||
errorCodes: [CreateReminderErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
if (archiveUntil) {
|
||||
await archivePage(uid, page)
|
||||
}
|
||||
// return {
|
||||
// errorCodes: [CreateReminderErrorCode.Unauthorized],
|
||||
// }
|
||||
// }
|
||||
// if (archiveUntil) {
|
||||
// await archivePage(uid, page)
|
||||
// }
|
||||
|
||||
const taskName = await groupReminders(scheduledTime, uid, models)
|
||||
log.info('scheduled task name', taskName)
|
||||
// const taskName = await groupReminders(scheduledTime, uid, models)
|
||||
// log.info('scheduled task name', taskName)
|
||||
|
||||
// insert reminder to db
|
||||
const reminder = await models.reminder.create({
|
||||
userId: uid,
|
||||
taskName: taskName,
|
||||
archiveUntil: archiveUntil,
|
||||
sendNotification: sendNotification,
|
||||
createdAt: new Date(),
|
||||
remindAt: scheduledTime,
|
||||
elasticPageId: pageId,
|
||||
})
|
||||
log.info('created reminder', reminder)
|
||||
// // insert reminder to db
|
||||
// const reminder = await models.reminder.create({
|
||||
// userId: uid,
|
||||
// taskName: taskName,
|
||||
// archiveUntil: archiveUntil,
|
||||
// sendNotification: sendNotification,
|
||||
// createdAt: new Date(),
|
||||
// remindAt: scheduledTime,
|
||||
// elasticPageId: pageId,
|
||||
// })
|
||||
// log.info('created reminder', reminder)
|
||||
|
||||
return {
|
||||
reminder: {
|
||||
id: reminder.id,
|
||||
archiveUntil,
|
||||
sendNotification,
|
||||
remindAt: scheduledTime,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
log.info('error creating reminder', e)
|
||||
// return {
|
||||
// reminder: {
|
||||
// id: reminder.id,
|
||||
// archiveUntil,
|
||||
// sendNotification,
|
||||
// remindAt: scheduledTime,
|
||||
// },
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log.info('error creating reminder', e)
|
||||
|
||||
return {
|
||||
errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// errorCodes: [CreateReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
// Attempts to find a link and archive it if it exists.
|
||||
// It is possible that the link has not been created
|
||||
// yet if it is still in the saving process. In that
|
||||
// case it will be archived when the link is created.
|
||||
const archivePage = async (uid: string, page: Page) => {
|
||||
try {
|
||||
await setLinkArchived(uid, page.id, true)
|
||||
} catch (e) {
|
||||
logger.info('error archiving link', e)
|
||||
}
|
||||
}
|
||||
// // Attempts to find a link and archive it if it exists.
|
||||
// // It is possible that the link has not been created
|
||||
// // yet if it is still in the saving process. In that
|
||||
// // case it will be archived when the link is created.
|
||||
// const archivePage = async (uid: string, page: Page) => {
|
||||
// try {
|
||||
// await setLinkArchived(uid, page.id, true)
|
||||
// } catch (e) {
|
||||
// logger.info('error archiving link', e)
|
||||
// }
|
||||
// }
|
||||
|
||||
export const reminderResolver = authorized<
|
||||
ReminderSuccess,
|
||||
ReminderError,
|
||||
QueryReminderArgs
|
||||
>(async (_, { linkId: pageId }, { models, claims: { uid }, log }) => {
|
||||
log.info('reminderResolver')
|
||||
// export const reminderResolver = authorized<
|
||||
// ReminderSuccess,
|
||||
// ReminderError,
|
||||
// QueryReminderArgs
|
||||
// >(async (_, { linkId: pageId }, { models, claims: { uid }, log }) => {
|
||||
// log.info('reminderResolver')
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'reminder',
|
||||
properties: {
|
||||
linkId: pageId,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
// analytics.track({
|
||||
// userId: uid,
|
||||
// event: 'reminder',
|
||||
// properties: {
|
||||
// linkId: pageId,
|
||||
// env: env.server.apiEnv,
|
||||
// },
|
||||
// })
|
||||
|
||||
try {
|
||||
// get page from articleId
|
||||
const page = await getPageById(pageId)
|
||||
if (!page) {
|
||||
return {
|
||||
errorCodes: [ReminderErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
if (page.userId !== uid) {
|
||||
return {
|
||||
errorCodes: [ReminderErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
const reminder = await models.reminder.getCreatedByParameters(uid, {
|
||||
elasticPageId: page.id,
|
||||
})
|
||||
// try {
|
||||
// // get page from articleId
|
||||
// const page = await getPageById(pageId)
|
||||
// if (!page) {
|
||||
// return {
|
||||
// errorCodes: [ReminderErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
// if (page.userId !== uid) {
|
||||
// return {
|
||||
// errorCodes: [ReminderErrorCode.Unauthorized],
|
||||
// }
|
||||
// }
|
||||
// const reminder = await models.reminder.getCreatedByParameters(uid, {
|
||||
// elasticPageId: page.id,
|
||||
// })
|
||||
|
||||
if (!reminder) {
|
||||
log.error('reminder not found: pageId: ', pageId)
|
||||
// if (!reminder) {
|
||||
// log.error('reminder not found: pageId: ', pageId)
|
||||
|
||||
return {
|
||||
errorCodes: [ReminderErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// errorCodes: [ReminderErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
reminder: {
|
||||
id: reminder.id,
|
||||
archiveUntil: reminder.archiveUntil || false,
|
||||
sendNotification: reminder.sendNotification || true,
|
||||
remindAt: reminder.remindAt,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
// return {
|
||||
// reminder: {
|
||||
// id: reminder.id,
|
||||
// archiveUntil: reminder.archiveUntil || false,
|
||||
// sendNotification: reminder.sendNotification || true,
|
||||
// remindAt: reminder.remindAt,
|
||||
// },
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log.error(e)
|
||||
|
||||
return {
|
||||
errorCodes: [ReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// errorCodes: [ReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
export const updateReminderResolver = authorized<
|
||||
UpdateReminderSuccess,
|
||||
UpdateReminderError,
|
||||
MutationUpdateReminderArgs
|
||||
>(async (_, { input }, { models, claims: { uid }, log, authTrx }) => {
|
||||
log.info('updateReminderResolver')
|
||||
// export const updateReminderResolver = authorized<
|
||||
// UpdateReminderSuccess,
|
||||
// UpdateReminderError,
|
||||
// MutationUpdateReminderArgs
|
||||
// >(async (_, { input }, { models, claims: { uid }, log, authTrx }) => {
|
||||
// log.info('updateReminderResolver')
|
||||
|
||||
const { id, archiveUntil, sendNotification } = input
|
||||
// const { id, archiveUntil, sendNotification } = input
|
||||
|
||||
const scheduledTime = validScheduleTime(input.remindAt)
|
||||
if (!scheduledTime) {
|
||||
log.error('Invalid scheduled time', input.remindAt)
|
||||
return {
|
||||
errorCodes: [UpdateReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
// const scheduledTime = validScheduleTime(input.remindAt)
|
||||
// if (!scheduledTime) {
|
||||
// log.error('Invalid scheduled time', input.remindAt)
|
||||
// return {
|
||||
// errorCodes: [UpdateReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'reminder_updated',
|
||||
properties: {
|
||||
id,
|
||||
remindAt: scheduledTime,
|
||||
archiveUntil,
|
||||
sendNotification,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
// analytics.track({
|
||||
// userId: uid,
|
||||
// event: 'reminder_updated',
|
||||
// properties: {
|
||||
// id,
|
||||
// remindAt: scheduledTime,
|
||||
// archiveUntil,
|
||||
// sendNotification,
|
||||
// env: env.server.apiEnv,
|
||||
// },
|
||||
// })
|
||||
|
||||
try {
|
||||
const reminder = await models.reminder.getCreated(id)
|
||||
// try {
|
||||
// const reminder = await models.reminder.getCreated(id)
|
||||
|
||||
if (!reminder) {
|
||||
log.error('reminder not found:', id)
|
||||
// if (!reminder) {
|
||||
// log.error('reminder not found:', id)
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateReminderErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// errorCodes: [UpdateReminderErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (reminder.userId !== uid) {
|
||||
return {
|
||||
errorCodes: [UpdateReminderErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
// if (reminder.userId !== uid) {
|
||||
// return {
|
||||
// errorCodes: [UpdateReminderErrorCode.Unauthorized],
|
||||
// }
|
||||
// }
|
||||
|
||||
// delete old google cloud task
|
||||
if (reminder.taskName) {
|
||||
await deleteTask(reminder.taskName)
|
||||
}
|
||||
// // delete old google cloud task
|
||||
// if (reminder.taskName) {
|
||||
// await deleteTask(reminder.taskName)
|
||||
// }
|
||||
|
||||
const taskName = await groupReminders(scheduledTime, uid, models)
|
||||
// const taskName = await groupReminders(scheduledTime, uid, models)
|
||||
|
||||
// update db
|
||||
await authTrx((tx) =>
|
||||
models.reminder.update(
|
||||
id,
|
||||
{
|
||||
taskName: taskName,
|
||||
archiveUntil,
|
||||
sendNotification,
|
||||
remindAt: scheduledTime,
|
||||
},
|
||||
tx
|
||||
)
|
||||
)
|
||||
// // update db
|
||||
// await authTrx((tx) =>
|
||||
// models.reminder.update(
|
||||
// id,
|
||||
// {
|
||||
// taskName: taskName,
|
||||
// archiveUntil,
|
||||
// sendNotification,
|
||||
// remindAt: scheduledTime,
|
||||
// },
|
||||
// tx
|
||||
// )
|
||||
// )
|
||||
|
||||
return {
|
||||
reminder: {
|
||||
id: reminder.id,
|
||||
archiveUntil,
|
||||
sendNotification,
|
||||
remindAt: scheduledTime,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
// return {
|
||||
// reminder: {
|
||||
// id: reminder.id,
|
||||
// archiveUntil,
|
||||
// sendNotification,
|
||||
// remindAt: scheduledTime,
|
||||
// },
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log.error(e)
|
||||
|
||||
return {
|
||||
errorCodes: [UpdateReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// errorCodes: [UpdateReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
export const deleteReminderResolver = authorized<
|
||||
DeleteReminderSuccess,
|
||||
DeleteReminderError,
|
||||
MutationDeleteReminderArgs
|
||||
>(async (_, { id }, { models, claims: { uid }, log, authTrx }) => {
|
||||
log.info('deleteReminderResolver')
|
||||
// export const deleteReminderResolver = authorized<
|
||||
// DeleteReminderSuccess,
|
||||
// DeleteReminderError,
|
||||
// MutationDeleteReminderArgs
|
||||
// >(async (_, { id }, { models, claims: { uid }, log, authTrx }) => {
|
||||
// log.info('deleteReminderResolver')
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'reminder_deleted',
|
||||
properties: {
|
||||
id: id,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
// analytics.track({
|
||||
// userId: uid,
|
||||
// event: 'reminder_deleted',
|
||||
// properties: {
|
||||
// id: id,
|
||||
// env: env.server.apiEnv,
|
||||
// },
|
||||
// })
|
||||
|
||||
try {
|
||||
const reminder = await models.reminder.getCreated(id)
|
||||
// try {
|
||||
// const reminder = await models.reminder.getCreated(id)
|
||||
|
||||
if (!reminder) {
|
||||
log.error('reminder not found:', id)
|
||||
// if (!reminder) {
|
||||
// log.error('reminder not found:', id)
|
||||
|
||||
return {
|
||||
errorCodes: [DeleteReminderErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// errorCodes: [DeleteReminderErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (reminder.userId !== uid) {
|
||||
return {
|
||||
errorCodes: [DeleteReminderErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
// if (reminder.userId !== uid) {
|
||||
// return {
|
||||
// errorCodes: [DeleteReminderErrorCode.Unauthorized],
|
||||
// }
|
||||
// }
|
||||
|
||||
// update db
|
||||
await authTrx((tx) => models.reminder.delete(id, tx))
|
||||
// // update db
|
||||
// await authTrx((tx) => models.reminder.delete(id, tx))
|
||||
|
||||
return {
|
||||
reminder: {
|
||||
id: reminder.id,
|
||||
archiveUntil: reminder.archiveUntil || false,
|
||||
sendNotification: reminder.sendNotification || true,
|
||||
remindAt: reminder.remindAt,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
// return {
|
||||
// reminder: {
|
||||
// id: reminder.id,
|
||||
// archiveUntil: reminder.archiveUntil || false,
|
||||
// sendNotification: reminder.sendNotification || true,
|
||||
// remindAt: reminder.remindAt,
|
||||
// },
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log.error(e)
|
||||
|
||||
return {
|
||||
errorCodes: [DeleteReminderErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
// return {
|
||||
// errorCodes: [DeleteReminderErrorCode.BadRequest],
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
// check if there exists reminders for the same user at the same time
|
||||
// create a Google cloud task if no existing task and return task name
|
||||
const groupReminders = async (
|
||||
scheduledTime: Date,
|
||||
userId: string,
|
||||
models: DataModels
|
||||
): Promise<string | undefined> => {
|
||||
const exists = await models.reminder.existByUserAndRemindAt(
|
||||
userId,
|
||||
scheduledTime
|
||||
)
|
||||
// // check if there exists reminders for the same user at the same time
|
||||
// // create a Google cloud task if no existing task and return task name
|
||||
// const groupReminders = async (
|
||||
// scheduledTime: Date,
|
||||
// userId: string,
|
||||
// models: DataModels
|
||||
// ): Promise<string | undefined> => {
|
||||
// const exists = await models.reminder.existByUserAndRemindAt(
|
||||
// userId,
|
||||
// scheduledTime
|
||||
// )
|
||||
|
||||
if (!exists) {
|
||||
return enqueueReminder(userId, scheduledTime.getTime())
|
||||
}
|
||||
// if (!exists) {
|
||||
// return enqueueReminder(userId, scheduledTime.getTime())
|
||||
// }
|
||||
|
||||
return undefined
|
||||
}
|
||||
// return undefined
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { authorized } from '../../utils/helpers'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Rule } from '../../entity/rule'
|
||||
import { User } from '../../entity/user'
|
||||
import {
|
||||
DeleteRuleError,
|
||||
DeleteRuleErrorCode,
|
||||
|
|
@ -13,9 +15,7 @@ import {
|
|||
SetRuleErrorCode,
|
||||
SetRuleSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { User } from '../../entity/user'
|
||||
import { Rule } from '../../entity/rule'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const setRuleResolver = authorized<
|
||||
SetRuleSuccess,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getRepository } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
MutationSaveFileArgs,
|
||||
|
|
@ -13,19 +13,15 @@ import { saveFile } from '../../services/save_file'
|
|||
import { savePage } from '../../services/save_page'
|
||||
import { saveUrl } from '../../services/save_url'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { authorized, userDataToUser } from '../../utils/helpers'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const savePageResolver = authorized<
|
||||
SaveSuccess,
|
||||
SaveError,
|
||||
MutationSavePageArgs
|
||||
>(async (_, { input }, ctx) => {
|
||||
const {
|
||||
models,
|
||||
claims: { uid },
|
||||
} = ctx
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
userId: ctx.uid,
|
||||
event: 'link_saved',
|
||||
properties: {
|
||||
url: input.url,
|
||||
|
|
@ -35,16 +31,14 @@ export const savePageResolver = authorized<
|
|||
},
|
||||
})
|
||||
|
||||
const user = userDataToUser(await models.user.get(uid))
|
||||
const user = await getRepository(User).findOneBy({
|
||||
id: ctx.uid,
|
||||
})
|
||||
if (!user) {
|
||||
return { errorCodes: [SaveErrorCode.Unauthorized] }
|
||||
}
|
||||
|
||||
return savePage(
|
||||
{ ...ctx, uid, refresh: true },
|
||||
{ userId: user.id, username: user.profile.username },
|
||||
input
|
||||
)
|
||||
return savePage(ctx, user, input)
|
||||
})
|
||||
|
||||
export const saveUrlResolver = authorized<
|
||||
|
|
@ -74,7 +68,7 @@ export const saveUrlResolver = authorized<
|
|||
return { errorCodes: [SaveErrorCode.Unauthorized] }
|
||||
}
|
||||
|
||||
return (await saveUrl({ ...ctx, uid }, user, input)) as SaveSuccess
|
||||
return saveUrl(ctx, user, input)
|
||||
})
|
||||
|
||||
export const saveFileResolver = authorized<
|
||||
|
|
@ -82,13 +76,8 @@ export const saveFileResolver = authorized<
|
|||
SaveError,
|
||||
MutationSaveFileArgs
|
||||
>(async (_, { input }, ctx) => {
|
||||
const {
|
||||
models,
|
||||
claims: { uid },
|
||||
} = ctx
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
userId: ctx.uid,
|
||||
event: 'link_saved',
|
||||
properties: {
|
||||
url: input.url,
|
||||
|
|
@ -98,10 +87,12 @@ export const saveFileResolver = authorized<
|
|||
},
|
||||
})
|
||||
|
||||
const user = userDataToUser(await models.user.get(uid))
|
||||
const user = await getRepository(User).findOneBy({
|
||||
id: ctx.uid,
|
||||
})
|
||||
if (!user) {
|
||||
return { errorCodes: [SaveErrorCode.Unauthorized] }
|
||||
}
|
||||
|
||||
return (await saveFile({ ...ctx, uid }, user, input)) as SaveSuccess
|
||||
return saveFile(ctx, user, input)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import Parser from 'rss-parser'
|
||||
import { Brackets } from 'typeorm'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Subscription } from '../../entity/subscription'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
MutationSubscribeArgs,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import { Context as ApolloContext } from 'apollo-server-core'
|
||||
import winston from 'winston'
|
||||
import { Knex } from 'knex'
|
||||
import UserModel from '../datalayer/user'
|
||||
import ArticleModel from '../datalayer/article'
|
||||
import UserArticleModel from '../datalayer/links'
|
||||
import UserFriendModel from '../datalayer/user_friends'
|
||||
import UserPersonalizationModel from '../datalayer/user_personalization'
|
||||
import ArticleSavingRequestModel from '../datalayer/article_saving_request'
|
||||
import UploadFileDataModel from '../datalayer/upload_files'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { Span } from '@opentelemetry/api'
|
||||
import HighlightModel from '../datalayer/highlight'
|
||||
import ReactionModel from '../datalayer/reaction'
|
||||
import { Context as ApolloContext } from 'apollo-server-core'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { EntityManager } from 'typeorm'
|
||||
import winston from 'winston'
|
||||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import ReminderModel from '../datalayer/reminders'
|
||||
|
||||
export interface Claims {
|
||||
uid: string
|
||||
|
|
@ -30,35 +20,19 @@ export type ClaimsToSet = {
|
|||
userRole?: string | null
|
||||
}
|
||||
|
||||
export type DataModels = {
|
||||
user: UserModel
|
||||
article: ArticleModel
|
||||
userArticle: UserArticleModel
|
||||
userFriends: UserFriendModel
|
||||
userPersonalization: UserPersonalizationModel
|
||||
articleSavingRequest: ArticleSavingRequestModel
|
||||
uploadFile: UploadFileDataModel
|
||||
highlight: HighlightModel
|
||||
reaction: ReactionModel
|
||||
reminder: ReminderModel
|
||||
}
|
||||
|
||||
export interface RequestContext {
|
||||
log: winston.Logger
|
||||
claims: Claims | undefined
|
||||
kx: Knex
|
||||
pubsub: PubsubClient
|
||||
models: DataModels
|
||||
setAuth: (claims: ClaimsToSet, secret?: string) => Promise<void>
|
||||
clearAuth: () => void
|
||||
setClaims: (tx: Knex.Transaction, uuid?: string | undefined) => Promise<void>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
setClaims: (em: EntityManager, uuid?: string | undefined) => Promise<void>
|
||||
signToken: (
|
||||
arg1: string | object | Buffer,
|
||||
arg2: jwt.Secret
|
||||
) => Promise<unknown>
|
||||
authTrx: <TResult>(
|
||||
cb: (tx: Knex.Transaction) => TResult,
|
||||
cb: (em: EntityManager) => TResult,
|
||||
userRole?: string
|
||||
) => Promise<TResult>
|
||||
tracingSpan: Span
|
||||
|
|
|
|||
|
|
@ -3,24 +3,25 @@ import normalizeUrl from 'normalize-url'
|
|||
import path from 'path'
|
||||
import { createPage, getPageByParam, updatePage } from '../../elastic/pages'
|
||||
import { PageType } from '../../elastic/types'
|
||||
import { uploadFileRepository } from '../../entity'
|
||||
import { UploadFile } from '../../entity/upload_file'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
MutationUploadFileRequestArgs,
|
||||
ResolverFn,
|
||||
UploadFileRequestError,
|
||||
UploadFileRequestErrorCode,
|
||||
UploadFileRequestResult,
|
||||
UploadFileRequestSuccess,
|
||||
UploadFileStatus,
|
||||
} from '../../generated/graphql'
|
||||
import { validateUrl } from '../../services/create_page_save_request'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { generateSlug } from '../../utils/helpers'
|
||||
import { authorized, generateSlug } from '../../utils/helpers'
|
||||
import {
|
||||
generateUploadFilePathName,
|
||||
generateUploadSignedUrl,
|
||||
getFilePublicUrl,
|
||||
} from '../../utils/uploads'
|
||||
import { WithDataSourcesContext } from '../types'
|
||||
|
||||
const isFileUrl = (url: string): boolean => {
|
||||
const parsedUrl = new URL(url)
|
||||
|
|
@ -34,23 +35,18 @@ export const pageTypeForContentType = (contentType: string): PageType => {
|
|||
return PageType.File
|
||||
}
|
||||
|
||||
export const uploadFileRequestResolver: ResolverFn<
|
||||
UploadFileRequestResult,
|
||||
unknown,
|
||||
WithDataSourcesContext,
|
||||
export const uploadFileRequestResolver = authorized<
|
||||
UploadFileRequestSuccess,
|
||||
UploadFileRequestError,
|
||||
MutationUploadFileRequestArgs
|
||||
> = async (_obj, { input }, ctx) => {
|
||||
const { models, authTrx, claims, log } = ctx
|
||||
>(async (_, { input }, ctx) => {
|
||||
const { authTrx, uid, log } = ctx
|
||||
let uploadFileData: { id: string | null } = {
|
||||
id: null,
|
||||
}
|
||||
|
||||
if (!claims?.uid) {
|
||||
return { errorCodes: [UploadFileRequestErrorCode.Unauthorized] }
|
||||
}
|
||||
|
||||
analytics.track({
|
||||
userId: claims.uid,
|
||||
userId: uid,
|
||||
event: 'file_upload_request',
|
||||
properties: {
|
||||
url: input.url,
|
||||
|
|
@ -89,10 +85,10 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
return { errorCodes: [UploadFileRequestErrorCode.BadInput] }
|
||||
}
|
||||
|
||||
uploadFileData = await models.uploadFile.create({
|
||||
uploadFileData = await uploadFileRepository.save({
|
||||
url: input.url,
|
||||
userId: claims.uid,
|
||||
fileName: fileName,
|
||||
userId: uid,
|
||||
fileName,
|
||||
status: UploadFileStatus.Initialized,
|
||||
contentType: input.contentType,
|
||||
})
|
||||
|
|
@ -113,14 +109,10 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
// If this is a file URL, we swap in the GCS public URL
|
||||
if (isFileUrl(input.url)) {
|
||||
await authTrx(async (tx) => {
|
||||
await models.uploadFile.update(
|
||||
uploadFileId,
|
||||
{
|
||||
url: publicUrl,
|
||||
status: UploadFileStatus.Initialized,
|
||||
},
|
||||
tx
|
||||
)
|
||||
await tx.getRepository(UploadFile).update(uploadFileId, {
|
||||
url: publicUrl,
|
||||
status: UploadFileStatus.Initialized,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +123,7 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
// new item.
|
||||
const page = isFileUrl(input.url)
|
||||
? await getPageByParam({
|
||||
userId: claims.uid,
|
||||
userId: uid,
|
||||
url: input.url,
|
||||
})
|
||||
: undefined
|
||||
|
|
@ -155,7 +147,7 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
{
|
||||
url: isFileUrl(input.url) ? publicUrl : input.url,
|
||||
id: input.clientRequestId || '',
|
||||
userId: claims.uid,
|
||||
userId: uid,
|
||||
title: title,
|
||||
hash: uploadFilePathName,
|
||||
content: '',
|
||||
|
|
@ -185,4 +177,4 @@ export const uploadFileRequestResolver: ResolverFn<
|
|||
} else {
|
||||
return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { RegistrationType } from '../../datalayer/user/model'
|
||||
import { deletePagesByParam } from '../../elastic/pages'
|
||||
import { setClaims, userRepository } from '../../entity'
|
||||
import { User as UserEntity } from '../../entity/user'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
DeleteAccountError,
|
||||
|
|
@ -39,7 +39,7 @@ import {
|
|||
UsersSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { createUser } from '../../services/create_user'
|
||||
import { createUser, getTopUsers } from '../../services/create_user'
|
||||
import { sendVerificationEmail } from '../../services/send_emails'
|
||||
import { authorized, userDataToUser } from '../../utils/helpers'
|
||||
import { validateUsername } from '../../utils/usernamePolicy'
|
||||
|
|
@ -49,8 +49,10 @@ export const updateUserResolver = authorized<
|
|||
UpdateUserSuccess,
|
||||
UpdateUserError,
|
||||
MutationUpdateUserArgs
|
||||
>(async (_, { input: { name, bio } }, { models, authTrx, claims }) => {
|
||||
const user = await models.user.get(claims.uid)
|
||||
>(async (_, { input: { name, bio } }, { uid }) => {
|
||||
const user = await userRepository.findOneBy({
|
||||
id: uid,
|
||||
})
|
||||
if (!user) {
|
||||
return { errorCodes: [UpdateUserErrorCode.UserNotFound] }
|
||||
}
|
||||
|
|
@ -69,16 +71,14 @@ export const updateUserResolver = authorized<
|
|||
return { errorCodes }
|
||||
}
|
||||
|
||||
const updatedUser = await authTrx(async (tx) => {
|
||||
const [updatedRecord, profile] = await Promise.all([
|
||||
models.user.update(
|
||||
claims.uid,
|
||||
{ name, source: user.source, sourceUserId: user.sourceUserId },
|
||||
tx
|
||||
),
|
||||
models.user.updateProfile(claims.uid, { bio }, tx),
|
||||
])
|
||||
return { ...updatedRecord, profile }
|
||||
const updatedUser = await userRepository.save({
|
||||
id: uid,
|
||||
name,
|
||||
source: user.source,
|
||||
sourceUserId: user.sourceUserId,
|
||||
profile: {
|
||||
bio,
|
||||
},
|
||||
})
|
||||
|
||||
return { user: userDataToUser(updatedUser) }
|
||||
|
|
@ -88,69 +88,63 @@ export const updateUserProfileResolver = authorized<
|
|||
UpdateUserProfileSuccess,
|
||||
UpdateUserProfileError,
|
||||
MutationUpdateUserProfileArgs
|
||||
>(
|
||||
async (
|
||||
_,
|
||||
{ input: { userId, username, pictureUrl } },
|
||||
{ models, authTrx, claims }
|
||||
) => {
|
||||
const user = await models.user.get(userId)
|
||||
if (user.id !== claims.uid) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.Forbidden],
|
||||
}
|
||||
}
|
||||
|
||||
if (!(username || pictureUrl)) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.BadData],
|
||||
}
|
||||
}
|
||||
|
||||
const lowerCasedUsername = username?.toLowerCase()
|
||||
if (lowerCasedUsername) {
|
||||
const existingUser = await models.user.getWhere({
|
||||
username: lowerCasedUsername,
|
||||
})
|
||||
if (existingUser?.id) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.UsernameExists],
|
||||
}
|
||||
}
|
||||
|
||||
if (!validateUsername(lowerCasedUsername)) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.BadUsername],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedProfile = await authTrx((tx) =>
|
||||
models.user.updateProfile(
|
||||
userId,
|
||||
{
|
||||
username: lowerCasedUsername,
|
||||
picture_url: pictureUrl,
|
||||
},
|
||||
tx
|
||||
)
|
||||
)
|
||||
|
||||
user.profile = {
|
||||
...updatedProfile,
|
||||
picture_url: updatedProfile.pictureUrl,
|
||||
}
|
||||
|
||||
return { user: userDataToUser(user) }
|
||||
>(async (_, { input: { userId, username, pictureUrl } }, { uid }) => {
|
||||
const user = await userRepository.findOneBy({
|
||||
id: userId,
|
||||
})
|
||||
if (!user) {
|
||||
return { errorCodes: [UpdateUserProfileErrorCode.Unauthorized] }
|
||||
}
|
||||
)
|
||||
|
||||
if (user.id !== uid) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.Forbidden],
|
||||
}
|
||||
}
|
||||
|
||||
if (!(username || pictureUrl)) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.BadData],
|
||||
}
|
||||
}
|
||||
|
||||
const lowerCasedUsername = username?.toLowerCase()
|
||||
if (lowerCasedUsername) {
|
||||
const existingUser = await userRepository.findOneBy({
|
||||
profile: {
|
||||
username: lowerCasedUsername,
|
||||
},
|
||||
})
|
||||
if (existingUser?.id) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.UsernameExists],
|
||||
}
|
||||
}
|
||||
|
||||
if (!validateUsername(lowerCasedUsername)) {
|
||||
return {
|
||||
errorCodes: [UpdateUserProfileErrorCode.BadUsername],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedUser = await userRepository.save({
|
||||
id: uid,
|
||||
profile: {
|
||||
username: lowerCasedUsername,
|
||||
pictureUrl,
|
||||
},
|
||||
})
|
||||
|
||||
return { user: userDataToUser(updatedUser) }
|
||||
})
|
||||
|
||||
export const googleLoginResolver: ResolverFn<
|
||||
LoginResult,
|
||||
unknown,
|
||||
WithDataSourcesContext,
|
||||
MutationGoogleLoginArgs
|
||||
> = async (_obj, { input }, { models, setAuth }) => {
|
||||
> = async (_obj, { input }, { setAuth }) => {
|
||||
const { email, secret } = input
|
||||
|
||||
try {
|
||||
|
|
@ -159,7 +153,7 @@ export const googleLoginResolver: ResolverFn<
|
|||
return { errorCodes: [LoginErrorCode.AuthFailed] }
|
||||
}
|
||||
|
||||
const user = await models.user.getWhere({
|
||||
const user = await userRepository.findOneBy({
|
||||
email,
|
||||
})
|
||||
if (!user?.id) {
|
||||
|
|
@ -176,13 +170,18 @@ export const validateUsernameResolver: ResolverFn<
|
|||
Record<string, unknown>,
|
||||
WithDataSourcesContext,
|
||||
QueryValidateUsernameArgs
|
||||
> = async (_obj, { username }, { models }) => {
|
||||
> = async (_obj, { username }) => {
|
||||
const lowerCasedUsername = username.toLowerCase()
|
||||
if (!validateUsername(lowerCasedUsername)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !(await models.user.exists({ username: lowerCasedUsername }))
|
||||
const user = await userRepository.findOneBy({
|
||||
profile: {
|
||||
username: lowerCasedUsername,
|
||||
},
|
||||
})
|
||||
return !user
|
||||
}
|
||||
|
||||
export const googleSignupResolver: ResolverFn<
|
||||
|
|
@ -245,11 +244,20 @@ export const getMeUserResolver: ResolverFn<
|
|||
unknown,
|
||||
WithDataSourcesContext,
|
||||
unknown
|
||||
> = async (_obj, __, { models, claims }) => {
|
||||
> = async (_obj, __, { claims }) => {
|
||||
try {
|
||||
return claims?.uid
|
||||
? userDataToUser(await models.user.get(claims.uid))
|
||||
: undefined
|
||||
if (!claims?.uid) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const user = await userRepository.findOneBy({
|
||||
id: claims.uid,
|
||||
})
|
||||
if (!user) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return userDataToUser(user)
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
|
|
@ -260,18 +268,20 @@ export const getUserResolver: ResolverFn<
|
|||
unknown,
|
||||
WithDataSourcesContext,
|
||||
QueryUserArgs
|
||||
> = async (_obj, { userId: id, username }, { models, claims }) => {
|
||||
> = async (_obj, { userId: id, username }, { uid }) => {
|
||||
if (!(id || username)) {
|
||||
return { errorCodes: [UserErrorCode.BadRequest] }
|
||||
}
|
||||
|
||||
const userId =
|
||||
id || (username && (await models.user.getWhere({ username }))?.id)
|
||||
id ||
|
||||
(username &&
|
||||
(await userRepository.findOneBy({ profile: { username } }))?.id)
|
||||
if (!userId) {
|
||||
return { errorCodes: [UserErrorCode.UserNotFound] }
|
||||
}
|
||||
|
||||
const userRecord = await models.user.getUserDetails(claims?.uid, userId)
|
||||
const userRecord = await userRepository.findOneBy({ id: userId })
|
||||
if (!userRecord) {
|
||||
return { errorCodes: [UserErrorCode.UserNotFound] }
|
||||
}
|
||||
|
|
@ -280,9 +290,8 @@ export const getUserResolver: ResolverFn<
|
|||
}
|
||||
|
||||
export const getAllUsersResolver = authorized<UsersSuccess, UsersError>(
|
||||
async (_obj, _params, { models, claims, authTrx }) => {
|
||||
const users =
|
||||
(await authTrx((tx) => models.user.getTopUsers(claims.uid, tx))) || []
|
||||
async (_obj, _params) => {
|
||||
const users = await getTopUsers()
|
||||
const result = { users: users.map((userData) => userDataToUser(userData)) }
|
||||
return result
|
||||
}
|
||||
|
|
@ -303,8 +312,10 @@ export const deleteAccountResolver = authorized<
|
|||
DeleteAccountSuccess,
|
||||
DeleteAccountError,
|
||||
MutationDeleteAccountArgs
|
||||
>(async (_, { userID }, { models, claims, log, pubsub }) => {
|
||||
const user = await models.user.get(userID)
|
||||
>(async (_, { userID }, { claims, log, pubsub }) => {
|
||||
const user = await userRepository.findOneBy({
|
||||
id: userID,
|
||||
})
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [DeleteAccountErrorCode.UserNotFound],
|
||||
|
|
|
|||
|
|
@ -1,24 +1,12 @@
|
|||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { PartialArticle } from '..'
|
||||
import { getShareInfoForArticle } from '../../datalayer/links/share_info'
|
||||
import {
|
||||
FeedArticle,
|
||||
MutationUpdateSharedCommentArgs,
|
||||
PageInfo,
|
||||
QueryFeedArticlesArgs,
|
||||
QuerySharedArticleArgs,
|
||||
ResolverFn,
|
||||
SharedArticleError,
|
||||
SharedArticleErrorCode,
|
||||
SharedArticleSuccess,
|
||||
UpdateSharedCommentError,
|
||||
UpdateSharedCommentErrorCode,
|
||||
UpdateSharedCommentSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { Merge } from '../../util'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
import { WithDataSourcesContext } from '../types'
|
||||
|
||||
export type PartialFeedArticle = Omit<
|
||||
FeedArticle,
|
||||
|
|
@ -35,139 +23,139 @@ export type SharedArticleSuccessPartial = Merge<
|
|||
{ article: PartialArticle }
|
||||
>
|
||||
|
||||
export const getSharedArticleResolver: ResolverFn<
|
||||
SharedArticleSuccessPartial | SharedArticleError,
|
||||
Record<string, unknown>,
|
||||
WithDataSourcesContext,
|
||||
QuerySharedArticleArgs
|
||||
> = async (_obj, { username, slug, selectedHighlightId }, { kx, models }) => {
|
||||
try {
|
||||
const user = await models.user.getWhere({ username })
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// export const getSharedArticleResolver: ResolverFn<
|
||||
// SharedArticleSuccessPartial | SharedArticleError,
|
||||
// Record<string, unknown>,
|
||||
// WithDataSourcesContext,
|
||||
// QuerySharedArticleArgs
|
||||
// > = async (_obj, { username, slug, selectedHighlightId }, { kx, models }) => {
|
||||
// try {
|
||||
// const user = await models.user.getWhere({ username })
|
||||
// if (!user) {
|
||||
// return {
|
||||
// errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
const article = await models.userArticle.getBySlug(username, slug)
|
||||
if (!article || !article.sharedAt) {
|
||||
return {
|
||||
errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
// const article = await models.userArticle.getBySlug(username, slug)
|
||||
// if (!article || !article.sharedAt) {
|
||||
// return {
|
||||
// errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
|
||||
if (selectedHighlightId) {
|
||||
const highlightResult = await models.highlight.getWhereIn('shortId', [
|
||||
selectedHighlightId,
|
||||
])
|
||||
if (!highlightResult || !highlightResult[0].sharedAt) {
|
||||
return {
|
||||
errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (selectedHighlightId) {
|
||||
// const highlightResult = await models.highlight.getWhereIn('shortId', [
|
||||
// selectedHighlightId,
|
||||
// ])
|
||||
// if (!highlightResult || !highlightResult[0].sharedAt) {
|
||||
// return {
|
||||
// errorCodes: [SharedArticleErrorCode.NotFound],
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const shareInfo = await getShareInfoForArticle(
|
||||
kx,
|
||||
user.id,
|
||||
article.id,
|
||||
models
|
||||
)
|
||||
// const shareInfo = await getShareInfoForArticle(
|
||||
// kx,
|
||||
// user.id,
|
||||
// article.id,
|
||||
// models
|
||||
// )
|
||||
|
||||
return { article: { ...article, userId: user.id, shareInfo: shareInfo } }
|
||||
} catch (error) {
|
||||
return { errorCodes: [SharedArticleErrorCode.NotFound] }
|
||||
}
|
||||
}
|
||||
// return { article: { ...article, userId: user.id, shareInfo: shareInfo } }
|
||||
// } catch (error) {
|
||||
// return { errorCodes: [SharedArticleErrorCode.NotFound] }
|
||||
// }
|
||||
// }
|
||||
|
||||
export const getUserFeedArticlesResolver: ResolverFn<
|
||||
PaginatedFeedArticlesSuccessPartial,
|
||||
unknown,
|
||||
WithDataSourcesContext,
|
||||
QueryFeedArticlesArgs
|
||||
> = async (
|
||||
_obj,
|
||||
{ after: _startCursor, first: _first, sharedByUser },
|
||||
{ models, claims, authTrx }
|
||||
) => {
|
||||
if (!(sharedByUser || claims?.uid)) {
|
||||
return {
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
startCursor: '',
|
||||
endCursor: '',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
// export const getUserFeedArticlesResolver: ResolverFn<
|
||||
// PaginatedFeedArticlesSuccessPartial,
|
||||
// unknown,
|
||||
// WithDataSourcesContext,
|
||||
// QueryFeedArticlesArgs
|
||||
// > = async (
|
||||
// _obj,
|
||||
// { after: _startCursor, first: _first, sharedByUser },
|
||||
// { models, claims, authTrx }
|
||||
// ) => {
|
||||
// if (!(sharedByUser || claims?.uid)) {
|
||||
// return {
|
||||
// edges: [],
|
||||
// pageInfo: {
|
||||
// startCursor: '',
|
||||
// endCursor: '',
|
||||
// hasNextPage: false,
|
||||
// hasPreviousPage: false,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
const first = _first || 0
|
||||
const startCursor = _startCursor || ''
|
||||
// const first = _first || 0
|
||||
// const startCursor = _startCursor || ''
|
||||
|
||||
const feedArticles =
|
||||
(await authTrx((tx) =>
|
||||
models.userArticle.getUserFeedArticlesPaginatedWithHighlights(
|
||||
{ cursor: startCursor, first: first + 1, sharedByUser }, // fetch one more item to get next cursor
|
||||
claims?.uid || '',
|
||||
tx
|
||||
)
|
||||
)) || []
|
||||
// const feedArticles =
|
||||
// (await authTrx((tx) =>
|
||||
// models.userArticle.getUserFeedArticlesPaginatedWithHighlights(
|
||||
// { cursor: startCursor, first: first + 1, sharedByUser }, // fetch one more item to get next cursor
|
||||
// claims?.uid || '',
|
||||
// tx
|
||||
// )
|
||||
// )) || []
|
||||
|
||||
const endCursor = feedArticles[feedArticles.length - 1]?.sharedAt
|
||||
.getTime()
|
||||
?.toString()
|
||||
const hasNextPage = feedArticles.length > first
|
||||
// const endCursor = feedArticles[feedArticles.length - 1]?.sharedAt
|
||||
// .getTime()
|
||||
// ?.toString()
|
||||
// const hasNextPage = feedArticles.length > first
|
||||
|
||||
if (hasNextPage) {
|
||||
// remove an extra if exists
|
||||
feedArticles.pop()
|
||||
}
|
||||
// if (hasNextPage) {
|
||||
// // remove an extra if exists
|
||||
// feedArticles.pop()
|
||||
// }
|
||||
|
||||
const edges = feedArticles.map((fa) => {
|
||||
return {
|
||||
node: fa,
|
||||
cursor: fa.sharedAt.getTime()?.toString(),
|
||||
}
|
||||
})
|
||||
// const edges = feedArticles.map((fa) => {
|
||||
// return {
|
||||
// node: fa,
|
||||
// cursor: fa.sharedAt.getTime()?.toString(),
|
||||
// }
|
||||
// })
|
||||
|
||||
return {
|
||||
edges,
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
startCursor: '',
|
||||
hasNextPage,
|
||||
endCursor,
|
||||
},
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// edges,
|
||||
// pageInfo: {
|
||||
// hasPreviousPage: false,
|
||||
// startCursor: '',
|
||||
// hasNextPage,
|
||||
// endCursor,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
export const updateSharedCommentResolver = authorized<
|
||||
UpdateSharedCommentSuccess,
|
||||
UpdateSharedCommentError,
|
||||
MutationUpdateSharedCommentArgs
|
||||
>(
|
||||
async (
|
||||
_,
|
||||
{ input: { articleID, sharedComment } },
|
||||
{ models, authTrx, claims: { uid } }
|
||||
) => {
|
||||
const ua = await authTrx((tx) =>
|
||||
models.userArticle.getByParameters(uid, { articleId: articleID }, tx)
|
||||
)
|
||||
if (!ua) {
|
||||
return { errorCodes: [UpdateSharedCommentErrorCode.NotFound] }
|
||||
}
|
||||
// export const updateSharedCommentResolver = authorized<
|
||||
// UpdateSharedCommentSuccess,
|
||||
// UpdateSharedCommentError,
|
||||
// MutationUpdateSharedCommentArgs
|
||||
// >(
|
||||
// async (
|
||||
// _,
|
||||
// { input: { articleID, sharedComment } },
|
||||
// { models, authTrx, claims: { uid } }
|
||||
// ) => {
|
||||
// const ua = await authTrx((tx) =>
|
||||
// models.userArticle.getByParameters(uid, { articleId: articleID }, tx)
|
||||
// )
|
||||
// if (!ua) {
|
||||
// return { errorCodes: [UpdateSharedCommentErrorCode.NotFound] }
|
||||
// }
|
||||
|
||||
await authTrx((tx) =>
|
||||
models.userArticle.updateByArticleId(
|
||||
uid,
|
||||
articleID,
|
||||
{ sharedComment },
|
||||
tx
|
||||
)
|
||||
)
|
||||
// await authTrx((tx) =>
|
||||
// models.userArticle.updateByArticleId(
|
||||
// uid,
|
||||
// articleID,
|
||||
// { sharedComment },
|
||||
// tx
|
||||
// )
|
||||
// )
|
||||
|
||||
return { articleID, sharedComment }
|
||||
}
|
||||
)
|
||||
// return { articleID, sharedComment }
|
||||
// }
|
||||
// )
|
||||
|
|
|
|||
|
|
@ -1,118 +1,101 @@
|
|||
import { Knex } from 'knex'
|
||||
import { UserData } from '../../datalayer/user/model'
|
||||
import {
|
||||
GetFollowersResult,
|
||||
GetFollowingResult,
|
||||
MutationSetFollowArgs,
|
||||
QueryGetFollowersArgs,
|
||||
QueryGetFollowingArgs,
|
||||
ResolverFn,
|
||||
SetFollowError,
|
||||
SetFollowErrorCode,
|
||||
SetFollowSuccess,
|
||||
User,
|
||||
} from '../../generated/graphql'
|
||||
import { authorized, userDataToUser } from '../../utils/helpers'
|
||||
import { DataModels, WithDataSourcesContext } from '../types'
|
||||
// export const setFollowResolver = authorized<
|
||||
// SetFollowSuccess,
|
||||
// SetFollowError,
|
||||
// MutationSetFollowArgs
|
||||
// >(
|
||||
// async (
|
||||
// _,
|
||||
// { input: { userId: friendUserId, follow } },
|
||||
// { models, authTrx, claims: { uid } }
|
||||
// ) => {
|
||||
// const user = await models.user.getUserDetails(uid, friendUserId)
|
||||
// if (!user) return { errorCodes: [SetFollowErrorCode.NotFound] }
|
||||
|
||||
export const setFollowResolver = authorized<
|
||||
SetFollowSuccess,
|
||||
SetFollowError,
|
||||
MutationSetFollowArgs
|
||||
>(
|
||||
async (
|
||||
_,
|
||||
{ input: { userId: friendUserId, follow } },
|
||||
{ models, authTrx, claims: { uid } }
|
||||
) => {
|
||||
const user = await models.user.getUserDetails(uid, friendUserId)
|
||||
if (!user) return { errorCodes: [SetFollowErrorCode.NotFound] }
|
||||
// const userFriendRecord = await authTrx((tx) =>
|
||||
// models.userFriends.getByUserFriendId(uid, friendUserId, tx)
|
||||
// )
|
||||
|
||||
const userFriendRecord = await authTrx((tx) =>
|
||||
models.userFriends.getByUserFriendId(uid, friendUserId, tx)
|
||||
)
|
||||
// if (follow) {
|
||||
// if (!userFriendRecord) {
|
||||
// await authTrx((tx) =>
|
||||
// models.userFriends.create({ friendUserId, userId: uid }, tx)
|
||||
// )
|
||||
// }
|
||||
// } else if (userFriendRecord) {
|
||||
// await authTrx((tx) => models.userFriends.delete(userFriendRecord.id, tx))
|
||||
// }
|
||||
|
||||
if (follow) {
|
||||
if (!userFriendRecord) {
|
||||
await authTrx((tx) =>
|
||||
models.userFriends.create({ friendUserId, userId: uid }, tx)
|
||||
)
|
||||
}
|
||||
} else if (userFriendRecord) {
|
||||
await authTrx((tx) => models.userFriends.delete(userFriendRecord.id, tx))
|
||||
}
|
||||
// const updatedUser = await models.user.getUserDetails(uid, friendUserId)
|
||||
// if (!updatedUser) return { errorCodes: [SetFollowErrorCode.NotFound] }
|
||||
|
||||
const updatedUser = await models.user.getUserDetails(uid, friendUserId)
|
||||
if (!updatedUser) return { errorCodes: [SetFollowErrorCode.NotFound] }
|
||||
// return {
|
||||
// updatedUser: {
|
||||
// ...userDataToUser(updatedUser),
|
||||
// isFriend: updatedUser.viewerIsFollowing,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
return {
|
||||
updatedUser: {
|
||||
...userDataToUser(updatedUser),
|
||||
isFriend: updatedUser.viewerIsFollowing,
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
// const getUserList = async (
|
||||
// uid: string,
|
||||
// users: UserData[],
|
||||
// models: DataModels,
|
||||
// authTrx: <TResult>(
|
||||
// cb: (tx: Knex.Transaction) => TResult,
|
||||
// userRole?: string
|
||||
// ) => Promise<TResult>
|
||||
// ): Promise<User[]> => {
|
||||
// const usersIds = users.map(({ id }) => id)
|
||||
// const friends = await authTrx((tx) =>
|
||||
// models.userFriends.getByFriendIds(uid, usersIds, tx)
|
||||
// )
|
||||
|
||||
const getUserList = async (
|
||||
uid: string,
|
||||
users: UserData[],
|
||||
models: DataModels,
|
||||
authTrx: <TResult>(
|
||||
cb: (tx: Knex.Transaction) => TResult,
|
||||
userRole?: string
|
||||
) => Promise<TResult>
|
||||
): Promise<User[]> => {
|
||||
const usersIds = users.map(({ id }) => id)
|
||||
const friends = await authTrx((tx) =>
|
||||
models.userFriends.getByFriendIds(uid, usersIds, tx)
|
||||
)
|
||||
// const friendsIds = friends.map(({ friendUserId }) => friendUserId)
|
||||
// users = users.map((f) => ({
|
||||
// ...f,
|
||||
// isFriend: friendsIds.includes(f.id),
|
||||
// viewerIsFollowing: friendsIds.includes(f.id),
|
||||
// }))
|
||||
|
||||
const friendsIds = friends.map(({ friendUserId }) => friendUserId)
|
||||
users = users.map((f) => ({
|
||||
...f,
|
||||
isFriend: friendsIds.includes(f.id),
|
||||
viewerIsFollowing: friendsIds.includes(f.id),
|
||||
}))
|
||||
// return users.map((u) => userDataToUser(u))
|
||||
// }
|
||||
|
||||
return users.map((u) => userDataToUser(u))
|
||||
}
|
||||
// export const getFollowersResolver: ResolverFn<
|
||||
// GetFollowersResult,
|
||||
// unknown,
|
||||
// WithDataSourcesContext,
|
||||
// QueryGetFollowersArgs
|
||||
// > = async (_parent, { userId }, { models, claims, authTrx }) => {
|
||||
// const followers = userId
|
||||
// ? await authTrx((tx) => models.user.getUserFollowersList(userId, tx))
|
||||
// : []
|
||||
// if (!claims?.uid) return { followers: usersWithNoFriends(followers) }
|
||||
// return {
|
||||
// followers: await getUserList(claims?.uid, followers, models, authTrx),
|
||||
// }
|
||||
// }
|
||||
|
||||
export const getFollowersResolver: ResolverFn<
|
||||
GetFollowersResult,
|
||||
unknown,
|
||||
WithDataSourcesContext,
|
||||
QueryGetFollowersArgs
|
||||
> = async (_parent, { userId }, { models, claims, authTrx }) => {
|
||||
const followers = userId
|
||||
? await authTrx((tx) => models.user.getUserFollowersList(userId, tx))
|
||||
: []
|
||||
if (!claims?.uid) return { followers: usersWithNoFriends(followers) }
|
||||
return {
|
||||
followers: await getUserList(claims?.uid, followers, models, authTrx),
|
||||
}
|
||||
}
|
||||
// export const getFollowingResolver: ResolverFn<
|
||||
// GetFollowingResult,
|
||||
// unknown,
|
||||
// WithDataSourcesContext,
|
||||
// QueryGetFollowingArgs
|
||||
// > = async (_parent, { userId }, { models, claims, authTrx }) => {
|
||||
// const following = userId
|
||||
// ? await authTrx((tx) => models.user.getUserFollowingList(userId, tx))
|
||||
// : []
|
||||
// if (!claims?.uid) return { following: usersWithNoFriends(following) }
|
||||
// return {
|
||||
// following: await getUserList(claims?.uid, following, models, authTrx),
|
||||
// }
|
||||
// }
|
||||
|
||||
export const getFollowingResolver: ResolverFn<
|
||||
GetFollowingResult,
|
||||
unknown,
|
||||
WithDataSourcesContext,
|
||||
QueryGetFollowingArgs
|
||||
> = async (_parent, { userId }, { models, claims, authTrx }) => {
|
||||
const following = userId
|
||||
? await authTrx((tx) => models.user.getUserFollowingList(userId, tx))
|
||||
: []
|
||||
if (!claims?.uid) return { following: usersWithNoFriends(following) }
|
||||
return {
|
||||
following: await getUserList(claims?.uid, following, models, authTrx),
|
||||
}
|
||||
}
|
||||
|
||||
const usersWithNoFriends = (users: UserData[]): User[] => {
|
||||
return users.map((f) =>
|
||||
userDataToUser({
|
||||
...f,
|
||||
isFriend: false,
|
||||
} as UserData)
|
||||
)
|
||||
}
|
||||
// const usersWithNoFriends = (users: UserData[]): User[] => {
|
||||
// return users.map((f) =>
|
||||
// userDataToUser({
|
||||
// ...f,
|
||||
// isFriend: false,
|
||||
// } as UserData)
|
||||
// )
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { getRepository, setClaims } from '../../entity'
|
||||
import { UserPersonalization } from '../../entity/user_personalization'
|
||||
import {
|
||||
GetUserPersonalizationError,
|
||||
GetUserPersonalizationResult,
|
||||
|
|
@ -7,10 +9,8 @@ import {
|
|||
SetUserPersonalizationSuccess,
|
||||
SortOrder,
|
||||
} from '../../generated/graphql'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
import { UserPersonalization } from '../../entity/user_personalization'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const setUserPersonalizationResolver = authorized<
|
||||
SetUserPersonalizationSuccess,
|
||||
|
|
@ -58,8 +58,12 @@ export const setUserPersonalizationResolver = authorized<
|
|||
export const getUserPersonalizationResolver = authorized<
|
||||
GetUserPersonalizationResult,
|
||||
GetUserPersonalizationError
|
||||
>(async (_parent, _args, { models, claims: { uid } }) => {
|
||||
const userPersonalization = await models.userPersonalization.getByUserId(uid)
|
||||
>(async (_parent, _args, { uid }) => {
|
||||
const userPersonalization = await getRepository(
|
||||
UserPersonalization
|
||||
).findOneBy({
|
||||
user: { id: uid },
|
||||
})
|
||||
|
||||
// Cast SortOrder from string to enum
|
||||
const librarySortOrder = userPersonalization?.librarySortOrder as
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { authorized } from '../../utils/helpers'
|
||||
import { getRepository } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { Webhook } from '../../entity/webhook'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
DeleteWebhookError,
|
||||
DeleteWebhookErrorCode,
|
||||
|
|
@ -18,11 +21,8 @@ import {
|
|||
WebhooksSuccess,
|
||||
WebhookSuccess,
|
||||
} from '../../generated/graphql'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { User } from '../../entity/user'
|
||||
import { Webhook } from '../../entity/webhook'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const webhooksResolver = authorized<WebhooksSuccess, WebhooksError>(
|
||||
async (_obj, _params, { claims: { uid }, log }) => {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import express from 'express'
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { createPubSubClient } from '../datalayer/pubsub'
|
||||
import { getPageById, updatePage } from '../elastic/pages'
|
||||
import { getRepository } from '../entity'
|
||||
import { Speech, SpeechState } from '../entity/speech'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { CreateArticleErrorCode } from '../generated/graphql'
|
||||
import { Claims } from '../resolvers/types'
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import {
|
|||
StatusType,
|
||||
UserData,
|
||||
} from '../../datalayer/user/model'
|
||||
import { getRepository, setClaims } from '../../entity'
|
||||
import { User } from '../../entity/user'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import { LoginErrorCode, SignupErrorCode } from '../../generated/graphql'
|
||||
import { isErrorWithCode } from '../../resolvers'
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { kx } from '../datalayer/knex_config'
|
||||
import { createPubSubClient } from '../datalayer/pubsub'
|
||||
import { createPage, getPageByParam, updatePage } from '../elastic/pages'
|
||||
import { addRecommendation } from '../elastic/recommendation'
|
||||
import { Recommendation } from '../elastic/types'
|
||||
import { uploadFileRepository } from '../entity'
|
||||
import { env } from '../env'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
|
|
@ -17,7 +17,6 @@ import {
|
|||
UploadFileStatus,
|
||||
} from '../generated/graphql'
|
||||
import { Claims } from '../resolvers/types'
|
||||
import { initModels } from '../server'
|
||||
import { getTokenByRequest } from '../utils/auth'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import {
|
||||
|
|
@ -76,7 +75,6 @@ export function pageRouter() {
|
|||
return res.status(400).send({ errorCode: 'BAD_DATA' })
|
||||
}
|
||||
|
||||
const models = initModels(kx, false)
|
||||
const ctx = {
|
||||
uid: claims.uid,
|
||||
pubsub: createPubSubClient(),
|
||||
|
|
@ -84,10 +82,10 @@ export function pageRouter() {
|
|||
|
||||
const title = titleForFilePath(url)
|
||||
const fileName = fileNameForFilePath(url)
|
||||
const uploadFileData = await models.uploadFile.create({
|
||||
url: url,
|
||||
const uploadFileData = await uploadFileRepository.save({
|
||||
url,
|
||||
userId: claims.uid,
|
||||
fileName: fileName,
|
||||
fileName,
|
||||
status: UploadFileStatus.Initialized,
|
||||
contentType: 'application/pdf',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import express from 'express'
|
||||
import { setClaims } from '../../datalayer/helpers'
|
||||
import { kx } from '../../datalayer/knex_config'
|
||||
import {
|
||||
createPubSubClient,
|
||||
readPushSubscription,
|
||||
} from '../../datalayer/pubsub'
|
||||
import { getPageByParam, updatePage } from '../../elastic/pages'
|
||||
import { Page } from '../../elastic/types'
|
||||
import { setClaims } from '../../entity'
|
||||
import { ArticleSavingRequestStatus } from '../../generated/graphql'
|
||||
import { initModels } from '../../server'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { setFileUploadComplete } from '../../services/save_file'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
interface UpdateContentMessage {
|
||||
|
|
@ -76,10 +76,9 @@ export function contentServiceRouter() {
|
|||
pageToUpdate.state = ArticleSavingRequestStatus.Succeeded
|
||||
|
||||
try {
|
||||
const models = initModels(kx, false)
|
||||
const uploadFileData = await kx.transaction(async (tx) => {
|
||||
const uploadFileData = await AppDataSource.transaction(async (tx) => {
|
||||
await setClaims(tx, page.userId)
|
||||
return models.uploadFile.setFileUploadComplete(fileId, tx)
|
||||
return setFileUploadComplete(fileId, tx)
|
||||
})
|
||||
logger.info('updated uploadFileData', uploadFileData)
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import express from 'express'
|
||||
import { setClaims } from '../../datalayer/helpers'
|
||||
import { kx } from '../../datalayer/knex_config'
|
||||
import { createPubSubClient } from '../../datalayer/pubsub'
|
||||
import { createPage } from '../../elastic/pages'
|
||||
import { ArticleSavingRequestStatus, Page } from '../../elastic/types'
|
||||
import { setClaims, uploadFileRepository } from '../../entity'
|
||||
import { env } from '../../env'
|
||||
import { PageType, UploadFileStatus } from '../../generated/graphql'
|
||||
import { initModels } from '../../server'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { getNewsletterEmail } from '../../services/newsletters'
|
||||
import { updateReceivedEmail } from '../../services/received_emails'
|
||||
import { setFileUploadComplete } from '../../services/save_file'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { getClaimsByToken } from '../../utils/auth'
|
||||
import { generateSlug } from '../../utils/helpers'
|
||||
|
|
@ -54,8 +54,7 @@ export function emailAttachmentRouter() {
|
|||
})
|
||||
|
||||
try {
|
||||
const models = initModels(kx, false)
|
||||
const uploadFileData = await models.uploadFile.create({
|
||||
const uploadFileData = await uploadFileRepository.save({
|
||||
url: '',
|
||||
userId: user.id,
|
||||
fileName: fileName,
|
||||
|
|
@ -117,10 +116,9 @@ export function emailAttachmentRouter() {
|
|||
})
|
||||
|
||||
try {
|
||||
const models = initModels(kx, false)
|
||||
const uploadFile = await models.uploadFile.getWhere({
|
||||
const uploadFile = await uploadFileRepository.findOneBy({
|
||||
id: uploadFileId,
|
||||
userId: user.id,
|
||||
user: { id: user.id },
|
||||
})
|
||||
if (!uploadFile) {
|
||||
return res.status(400).send('BAD REQUEST')
|
||||
|
|
@ -131,9 +129,9 @@ export function emailAttachmentRouter() {
|
|||
uploadFile.fileName
|
||||
)
|
||||
|
||||
const uploadFileData = await kx.transaction(async (tx) => {
|
||||
const uploadFileData = await AppDataSource.transaction(async (tx) => {
|
||||
await setClaims(tx, user.id)
|
||||
return models.uploadFile.setFileUploadComplete(uploadFileId, tx)
|
||||
return setFileUploadComplete(uploadFileId, tx)
|
||||
})
|
||||
if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) {
|
||||
return res.status(400).send('BAD REQUEST')
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { v4 as uuidv4 } from 'uuid'
|
|||
import { EntityType, readPushSubscription } from '../../datalayer/pubsub'
|
||||
import { getPageById, searchPages } from '../../elastic/pages'
|
||||
import { Page } from '../../elastic/types'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Integration, IntegrationType } from '../../entity/integration'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { Claims } from '../../resolvers/types'
|
||||
import { getIntegrationService } from '../../services/integrations'
|
||||
import { getClaimsByToken } from '../../utils/auth'
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
import express from 'express'
|
||||
import { MulticastMessage } from 'firebase-admin/messaging'
|
||||
import { setClaims } from '../../datalayer/helpers'
|
||||
import { kx } from '../../datalayer/knex_config'
|
||||
import { createPubSubClient } from '../../datalayer/pubsub'
|
||||
import { updatePage } from '../../elastic/pages'
|
||||
import { setClaims } from '../../entity'
|
||||
import { UserDeviceToken } from '../../entity/user_device_tokens'
|
||||
import { env, homePageURL } from '../../env'
|
||||
import { homePageURL } from '../../env'
|
||||
import { ContentReader } from '../../generated/graphql'
|
||||
import { DataModels } from '../../resolvers/types'
|
||||
import { initModels } from '../../server'
|
||||
import { getPagesWithReminder, PageReminder } from '../../services/reminders'
|
||||
import { getDeviceTokensByUserId } from '../../services/user_device_tokens'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { logger } from '../../utils/logger'
|
||||
import { sendEmail } from '../../utils/sendEmail'
|
||||
import { sendMulticastPushNotifications } from '../../utils/sendNotification'
|
||||
import { AppDataSource } from '../../server'
|
||||
import { PageReminder, setRemindersComplete } from '../../services/reminders'
|
||||
|
||||
interface PageToNotify {
|
||||
title: string
|
||||
|
|
@ -24,116 +16,116 @@ interface PageToNotify {
|
|||
image: string | undefined | null
|
||||
}
|
||||
|
||||
export function remindersServiceRouter() {
|
||||
const router = express.Router()
|
||||
// export function remindersServiceRouter() {
|
||||
// const router = express.Router()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
router.post('/trigger', async (req, res) => {
|
||||
logger.info('reminders/trigger')
|
||||
// // eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
// router.post('/trigger', async (req, res) => {
|
||||
// logger.info('reminders/trigger')
|
||||
|
||||
const { userId, scheduleTime } = req.body as {
|
||||
userId?: string
|
||||
scheduleTime?: number
|
||||
}
|
||||
// const { userId, scheduleTime } = req.body as {
|
||||
// userId?: string
|
||||
// scheduleTime?: number
|
||||
// }
|
||||
|
||||
analytics.track({
|
||||
userId: userId,
|
||||
event: 'reminder_triggered',
|
||||
properties: {
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
// analytics.track({
|
||||
// userId: userId,
|
||||
// event: 'reminder_triggered',
|
||||
// properties: {
|
||||
// env: env.server.apiEnv,
|
||||
// },
|
||||
// })
|
||||
|
||||
if (!userId || !scheduleTime) {
|
||||
res.status(400).send('Bad Request')
|
||||
return
|
||||
}
|
||||
// if (!userId || !scheduleTime) {
|
||||
// res.status(400).send('Bad Request')
|
||||
// return
|
||||
// }
|
||||
|
||||
try {
|
||||
const remindAt = new Date(scheduleTime)
|
||||
// try {
|
||||
// const remindAt = new Date(scheduleTime)
|
||||
|
||||
// get all reminders by userid and scheduled time
|
||||
const models = initModels(kx, false)
|
||||
// // get all reminders by userid and scheduled time
|
||||
// const models = initModels(kx, false)
|
||||
|
||||
const user = await models.user.get(userId)
|
||||
if (!user || !user.email) {
|
||||
logger.info('user not found', userId)
|
||||
res.status(400).send('User Not Found')
|
||||
return
|
||||
}
|
||||
// const user = await models.user.get(userId)
|
||||
// if (!user || !user.email) {
|
||||
// logger.info('user not found', userId)
|
||||
// res.status(400).send('User Not Found')
|
||||
// return
|
||||
// }
|
||||
|
||||
const pageReminders = await getPagesWithReminder(userId, remindAt)
|
||||
// const pageReminders = await getPagesWithReminder(userId, remindAt)
|
||||
|
||||
if (!pageReminders) {
|
||||
logger.info('pages with reminders not found', userId, scheduleTime)
|
||||
res.status(200).send('Reminders Not Found')
|
||||
return
|
||||
}
|
||||
// if (!pageReminders) {
|
||||
// logger.info('pages with reminders not found', userId, scheduleTime)
|
||||
// res.status(200).send('Reminders Not Found')
|
||||
// return
|
||||
// }
|
||||
|
||||
logger.info('page with reminders:', pageReminders)
|
||||
// logger.info('page with reminders:', pageReminders)
|
||||
|
||||
const [pagesToNotify, pagesToUnarchive] = getPagesToNotifyAndUnarchive(
|
||||
pageReminders,
|
||||
user.profile.username
|
||||
)
|
||||
// const [pagesToNotify, pagesToUnarchive] = getPagesToNotifyAndUnarchive(
|
||||
// pageReminders,
|
||||
// user.profile.username
|
||||
// )
|
||||
|
||||
// If none of the fetch reminders have sendNotification
|
||||
// set to true, then we should not send an email or notification
|
||||
if (pagesToNotify.length > 0) {
|
||||
// we have configured Sendgrid to send a template
|
||||
if (!process.env.SENDGRID_REMINDER_TEMPLATE_ID) {
|
||||
logger.info('Sendgrid reminder email template_id not set')
|
||||
// // If none of the fetch reminders have sendNotification
|
||||
// // set to true, then we should not send an email or notification
|
||||
// if (pagesToNotify.length > 0) {
|
||||
// // we have configured Sendgrid to send a template
|
||||
// if (!process.env.SENDGRID_REMINDER_TEMPLATE_ID) {
|
||||
// logger.info('Sendgrid reminder email template_id not set')
|
||||
|
||||
await updateRemindersStatus(
|
||||
models,
|
||||
userId,
|
||||
pagesToUnarchive,
|
||||
remindAt
|
||||
)
|
||||
res.status(200).send('Template Id Not Found')
|
||||
return
|
||||
}
|
||||
// await updateRemindersStatus(
|
||||
// models,
|
||||
// userId,
|
||||
// pagesToUnarchive,
|
||||
// remindAt
|
||||
// )
|
||||
// res.status(200).send('Template Id Not Found')
|
||||
// return
|
||||
// }
|
||||
|
||||
const dynamicTemplateData = {
|
||||
subject: `Omnivore Reminder Service`,
|
||||
title: `Hey ${user.name}, you have ${pagesToNotify.length} article(s) to read on Omnivore`,
|
||||
articles: pagesToNotify,
|
||||
}
|
||||
// const dynamicTemplateData = {
|
||||
// subject: `Omnivore Reminder Service`,
|
||||
// title: `Hey ${user.name}, you have ${pagesToNotify.length} article(s) to read on Omnivore`,
|
||||
// articles: pagesToNotify,
|
||||
// }
|
||||
|
||||
logger.info('dynamic template data:', dynamicTemplateData)
|
||||
// logger.info('dynamic template data:', dynamicTemplateData)
|
||||
|
||||
await sendEmail({
|
||||
from: env.sender.message,
|
||||
dynamicTemplateData: dynamicTemplateData,
|
||||
templateId: env.sendgrid.reminderTemplateId,
|
||||
to: user.email,
|
||||
})
|
||||
// await sendEmail({
|
||||
// from: env.sender.message,
|
||||
// dynamicTemplateData: dynamicTemplateData,
|
||||
// templateId: env.sendgrid.reminderTemplateId,
|
||||
// to: user.email,
|
||||
// })
|
||||
|
||||
// send push notifications
|
||||
const deviceTokens = await getDeviceTokensByUserId(userId)
|
||||
if (deviceTokens && deviceTokens.length > 0) {
|
||||
const message = messageForPages(pageReminders, deviceTokens)
|
||||
await sendMulticastPushNotifications(userId, message, 'reminder')
|
||||
}
|
||||
// // send push notifications
|
||||
// const deviceTokens = await getDeviceTokensByUserId(userId)
|
||||
// if (deviceTokens && deviceTokens.length > 0) {
|
||||
// const message = messageForPages(pageReminders, deviceTokens)
|
||||
// await sendMulticastPushNotifications(userId, message, 'reminder')
|
||||
// }
|
||||
|
||||
if (!deviceTokens) {
|
||||
logger.info('Device tokens not set:', userId)
|
||||
// if (!deviceTokens) {
|
||||
// logger.info('Device tokens not set:', userId)
|
||||
|
||||
res.status(400).send('Device token Not Found')
|
||||
return
|
||||
}
|
||||
}
|
||||
// res.status(400).send('Device token Not Found')
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt)
|
||||
res.status(200).send('Reminders triggered')
|
||||
} catch (e) {
|
||||
logger.info(e)
|
||||
res.status(500).send(e)
|
||||
}
|
||||
})
|
||||
// await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt)
|
||||
// res.status(200).send('Reminders triggered')
|
||||
// } catch (e) {
|
||||
// logger.info(e)
|
||||
// res.status(500).send(e)
|
||||
// }
|
||||
// })
|
||||
|
||||
return router
|
||||
}
|
||||
// return router
|
||||
// }
|
||||
|
||||
const getPagesToNotifyAndUnarchive = (
|
||||
pageReminders: PageReminder[],
|
||||
|
|
@ -221,7 +213,6 @@ const messageForPages = (
|
|||
}
|
||||
|
||||
const updateRemindersStatus = async (
|
||||
models: DataModels,
|
||||
userId: string,
|
||||
pagesToUnarchive: string[],
|
||||
remindAt: Date
|
||||
|
|
@ -243,8 +234,8 @@ const updateRemindersStatus = async (
|
|||
}
|
||||
|
||||
// db update
|
||||
await kx.transaction(async (tx) => {
|
||||
await AppDataSource.transaction(async (tx) => {
|
||||
await setClaims(tx, userId)
|
||||
await models.reminder.setRemindersComplete(userId, remindAt, tx)
|
||||
await setRemindersComplete(tx, userId, remindAt)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import express from 'express'
|
||||
import { readPushSubscription } from '../../datalayer/pubsub'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Subscription } from '../../entity/subscription'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { SubscriptionStatus, SubscriptionType } from '../../generated/graphql'
|
||||
import { enqueueRssFeedFetch } from '../../utils/createTask'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import axios, { Method } from 'axios'
|
||||
import express from 'express'
|
||||
import { readPushSubscription } from '../../datalayer/pubsub'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Webhook } from '../../entity/webhook'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import express from 'express'
|
|||
import { readPushSubscription } from '../datalayer/pubsub'
|
||||
import { getPageById } from '../elastic/pages'
|
||||
import { ArticleSavingRequestStatus } from '../elastic/types'
|
||||
import { getRepository, setClaims } from '../entity'
|
||||
import { Speech, SpeechState } from '../entity/speech'
|
||||
import { UserPersonalization } from '../entity/user_personalization'
|
||||
import { getRepository, setClaims } from '../entity/utils'
|
||||
import { AppDataSource } from '../server'
|
||||
import { FeatureName, getFeature } from '../services/features'
|
||||
import { shouldSynthesize } from '../services/speech'
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import { getRepository } from '../entity'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { getClaimsByToken, getTokenByRequest } from '../utils/auth'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
|
|
|
|||
|
|
@ -2604,24 +2604,24 @@ const schema = gql`
|
|||
mergeHighlight(input: MergeHighlightInput!): MergeHighlightResult!
|
||||
updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult!
|
||||
deleteHighlight(highlightId: ID!): DeleteHighlightResult!
|
||||
createHighlightReply(
|
||||
input: CreateHighlightReplyInput!
|
||||
): CreateHighlightReplyResult!
|
||||
updateHighlightReply(
|
||||
input: UpdateHighlightReplyInput!
|
||||
): UpdateHighlightReplyResult!
|
||||
deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult!
|
||||
createReaction(input: CreateReactionInput!): CreateReactionResult!
|
||||
deleteReaction(id: ID!): DeleteReactionResult!
|
||||
# createHighlightReply(
|
||||
# input: CreateHighlightReplyInput!
|
||||
# ): CreateHighlightReplyResult!
|
||||
# updateHighlightReply(
|
||||
# input: UpdateHighlightReplyInput!
|
||||
# ): UpdateHighlightReplyResult!
|
||||
# deleteHighlightReply(highlightReplyId: ID!): DeleteHighlightReplyResult!
|
||||
# createReaction(input: CreateReactionInput!): CreateReactionResult!
|
||||
# deleteReaction(id: ID!): DeleteReactionResult!
|
||||
uploadFileRequest(input: UploadFileRequestInput!): UploadFileRequestResult!
|
||||
saveArticleReadingProgress(
|
||||
input: SaveArticleReadingProgressInput!
|
||||
): SaveArticleReadingProgressResult!
|
||||
setShareArticle(input: SetShareArticleInput!): SetShareArticleResult!
|
||||
updateSharedComment(
|
||||
input: UpdateSharedCommentInput!
|
||||
): UpdateSharedCommentResult!
|
||||
setFollow(input: SetFollowInput!): SetFollowResult!
|
||||
# setShareArticle(input: SetShareArticleInput!): SetShareArticleResult!
|
||||
# updateSharedComment(
|
||||
# input: UpdateSharedCommentInput!
|
||||
# ): UpdateSharedCommentResult!
|
||||
# setFollow(input: SetFollowInput!): SetFollowResult!
|
||||
setBookmarkArticle(
|
||||
input: SetBookmarkArticleInput!
|
||||
): SetBookmarkArticleResult!
|
||||
|
|
@ -2631,11 +2631,11 @@ const schema = gql`
|
|||
createArticleSavingRequest(
|
||||
input: CreateArticleSavingRequestInput!
|
||||
): CreateArticleSavingRequestResult!
|
||||
setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult!
|
||||
# setShareHighlight(input: SetShareHighlightInput!): SetShareHighlightResult!
|
||||
reportItem(input: ReportItemInput!): ReportItemResult!
|
||||
updateLinkShareInfo(
|
||||
input: UpdateLinkShareInfoInput!
|
||||
): UpdateLinkShareInfoResult!
|
||||
# updateLinkShareInfo(
|
||||
# input: UpdateLinkShareInfoInput!
|
||||
# ): UpdateLinkShareInfoResult!
|
||||
setLinkArchived(input: ArchiveLinkInput!): ArchiveLinkResult!
|
||||
createNewsletterEmail: CreateNewsletterEmailResult!
|
||||
deleteNewsletterEmail(newsletterEmailId: ID!): DeleteNewsletterEmailResult!
|
||||
|
|
@ -2643,9 +2643,9 @@ const schema = gql`
|
|||
savePage(input: SavePageInput!): SaveResult!
|
||||
updatePage(input: UpdatePageInput!): UpdatePageResult!
|
||||
saveFile(input: SaveFileInput!): SaveResult!
|
||||
createReminder(input: CreateReminderInput!): CreateReminderResult!
|
||||
updateReminder(input: UpdateReminderInput!): UpdateReminderResult!
|
||||
deleteReminder(id: ID!): DeleteReminderResult!
|
||||
# createReminder(input: CreateReminderInput!): CreateReminderResult!
|
||||
# updateReminder(input: UpdateReminderInput!): UpdateReminderResult!
|
||||
# deleteReminder(id: ID!): DeleteReminderResult!
|
||||
setDeviceToken(input: SetDeviceTokenInput!): SetDeviceTokenResult!
|
||||
createLabel(input: CreateLabelInput!): CreateLabelResult!
|
||||
updateLabel(input: UpdateLabelInput!): UpdateLabelResult!
|
||||
|
|
@ -2710,25 +2710,25 @@ const schema = gql`
|
|||
includePending: Boolean
|
||||
): ArticlesResult!
|
||||
article(username: String!, slug: String!, format: String): ArticleResult!
|
||||
sharedArticle(
|
||||
username: String!
|
||||
slug: String!
|
||||
selectedHighlightId: String
|
||||
): SharedArticleResult!
|
||||
feedArticles(
|
||||
after: String
|
||||
first: Int
|
||||
sort: SortParams
|
||||
sharedByUser: ID
|
||||
): FeedArticlesResult!
|
||||
# sharedArticle(
|
||||
# username: String!
|
||||
# slug: String!
|
||||
# selectedHighlightId: String
|
||||
# ): SharedArticleResult!
|
||||
# feedArticles(
|
||||
# after: String
|
||||
# first: Int
|
||||
# sort: SortParams
|
||||
# sharedByUser: ID
|
||||
# ): FeedArticlesResult!
|
||||
users: UsersResult!
|
||||
validateUsername(username: String!): Boolean!
|
||||
getFollowers(userId: ID): GetFollowersResult!
|
||||
getFollowing(userId: ID): GetFollowingResult!
|
||||
# getFollowers(userId: ID): GetFollowersResult!
|
||||
# getFollowing(userId: ID): GetFollowingResult!
|
||||
getUserPersonalization: GetUserPersonalizationResult!
|
||||
articleSavingRequest(id: ID, url: String): ArticleSavingRequestResult!
|
||||
newsletterEmails: NewsletterEmailsResult!
|
||||
reminder(linkId: ID!): ReminderResult!
|
||||
# reminder(linkId: ID!): ReminderResult!
|
||||
labels: LabelsResult!
|
||||
search(
|
||||
after: String
|
||||
|
|
|
|||
|
|
@ -11,24 +11,12 @@ import express, { Express } from 'express'
|
|||
import * as httpContext from 'express-http-context2'
|
||||
import rateLimit from 'express-rate-limit'
|
||||
import { createServer, Server } from 'http'
|
||||
import { Knex } from 'knex'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
|
||||
import { config, loggers } from 'winston'
|
||||
import { makeApolloServer } from './apollo'
|
||||
import ArticleModel from './datalayer/article'
|
||||
import ArticleSavingRequestModel from './datalayer/article_saving_request'
|
||||
import HighlightModel from './datalayer/highlight'
|
||||
import UserArticleModel from './datalayer/links'
|
||||
import ReactionModel from './datalayer/reaction'
|
||||
import ReminderModel from './datalayer/reminders'
|
||||
import UploadFileDataModel from './datalayer/upload_files'
|
||||
import UserModel from './datalayer/user'
|
||||
import UserFriendModel from './datalayer/user_friends'
|
||||
import UserPersonalizationModel from './datalayer/user_personalization'
|
||||
import { initElasticsearch } from './elastic'
|
||||
import { env } from './env'
|
||||
import { DataModels } from './resolvers/types'
|
||||
import { articleRouter } from './routers/article_router'
|
||||
import { authRouter } from './routers/auth/auth_router'
|
||||
import { mobileAuthRouter } from './routers/auth/mobile/mobile_auth_router'
|
||||
|
|
@ -42,7 +30,7 @@ import { emailAttachmentRouter } from './routers/svc/email_attachment'
|
|||
import { integrationsServiceRouter } from './routers/svc/integrations'
|
||||
import { linkServiceRouter } from './routers/svc/links'
|
||||
import { newsletterServiceRouter } from './routers/svc/newsletters'
|
||||
import { remindersServiceRouter } from './routers/svc/reminders'
|
||||
// import { remindersServiceRouter } from './routers/svc/reminders'
|
||||
import { rssFeedRouter } from './routers/svc/rss_feed'
|
||||
import { uploadServiceRouter } from './routers/svc/upload'
|
||||
import { webhooksServiceRouter } from './routers/svc/webhooks'
|
||||
|
|
@ -59,19 +47,6 @@ import {
|
|||
|
||||
const PORT = process.env.PORT || 4000
|
||||
|
||||
export const initModels = (kx: Knex, cache = true): DataModels => ({
|
||||
user: new UserModel(kx, cache),
|
||||
article: new ArticleModel(kx, cache),
|
||||
userArticle: new UserArticleModel(kx, cache),
|
||||
userFriends: new UserFriendModel(kx, cache),
|
||||
userPersonalization: new UserPersonalizationModel(kx, cache),
|
||||
articleSavingRequest: new ArticleSavingRequestModel(kx, cache),
|
||||
uploadFile: new UploadFileDataModel(kx, cache),
|
||||
highlight: new HighlightModel(kx, cache),
|
||||
reaction: new ReactionModel(kx, cache),
|
||||
reminder: new ReminderModel(kx, cache),
|
||||
})
|
||||
|
||||
export const AppDataSource = new DataSource({
|
||||
type: 'postgres',
|
||||
host: env.pg.host,
|
||||
|
|
@ -85,6 +60,9 @@ export const AppDataSource = new DataSource({
|
|||
subscribers: [__dirname + '/events/**/*{.js,.ts}'],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
logger: new CustomTypeOrmLogger(),
|
||||
cache: true,
|
||||
connectTimeoutMS: 60000, // 60 seconds
|
||||
maxQueryExecutionTime: 60000, // 60 seconds
|
||||
})
|
||||
|
||||
export const createApp = (): {
|
||||
|
|
@ -164,7 +142,7 @@ export const createApp = (): {
|
|||
app.use('/svc/pubsub/webhooks', webhooksServiceRouter())
|
||||
app.use('/svc/pubsub/integrations', integrationsServiceRouter())
|
||||
app.use('/svc/pubsub/rss-feed', rssFeedRouter())
|
||||
app.use('/svc/reminders', remindersServiceRouter())
|
||||
// app.use('/svc/reminders', remindersServiceRouter())
|
||||
app.use('/svc/email-attachment', emailAttachmentRouter())
|
||||
|
||||
if (env.dev.isLocal) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { setClaims } from '../entity'
|
||||
import { Link } from '../entity/link'
|
||||
import { setClaims } from '../entity/utils'
|
||||
import { AppDataSource } from '../server'
|
||||
|
||||
export const setLinkArchived = async (
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import {
|
|||
updatePage,
|
||||
} from '../elastic/pages'
|
||||
import { ArticleSavingRequestStatus, Label, PageType } from '../elastic/types'
|
||||
import { getRepository } from '../entity'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import {
|
||||
ArticleSavingRequest,
|
||||
CreateArticleSavingRequestErrorCode,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { EntityManager } from 'typeorm'
|
||||
import { EntityManager, In } from 'typeorm'
|
||||
import { StatusType } from '../datalayer/user/model'
|
||||
import { getRepository, userRepository } from '../entity'
|
||||
import { GroupMembership } from '../entity/groups/group_membership'
|
||||
import { Invite } from '../entity/groups/invite'
|
||||
import { Profile } from '../entity/profile'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { SignupErrorCode } from '../generated/graphql'
|
||||
import { AuthProvider } from '../routers/auth/auth_types'
|
||||
import { AppDataSource } from '../server'
|
||||
|
|
@ -15,6 +15,18 @@ import { Filter } from '../entity/filter'
|
|||
import { analytics } from '../utils/analytics'
|
||||
import { env } from '../env'
|
||||
|
||||
const TOP_USERS = [
|
||||
'jacksonh',
|
||||
'nat',
|
||||
'luis',
|
||||
'satindar',
|
||||
'malandrina',
|
||||
'patrick',
|
||||
'alexgutjahr',
|
||||
'hongbowu',
|
||||
]
|
||||
export const MAX_RECORDS_LIMIT = 1000
|
||||
|
||||
export const createUser = async (input: {
|
||||
provider: AuthProvider
|
||||
sourceUserId?: string
|
||||
|
|
@ -166,9 +178,19 @@ const validateInvite = async (
|
|||
}
|
||||
|
||||
export const getUserByEmail = async (email: string): Promise<User | null> => {
|
||||
return getRepository(User)
|
||||
return userRepository
|
||||
.createQueryBuilder('user')
|
||||
.leftJoinAndSelect('user.profile', 'profile')
|
||||
.where('LOWER(email) = LOWER(:email)', { email }) // case insensitive
|
||||
.getOne()
|
||||
}
|
||||
|
||||
export const getTopUsers = async (): Promise<User[]> => {
|
||||
return userRepository
|
||||
.createQueryBuilder()
|
||||
.where({
|
||||
profile: { username: In(TOP_USERS) },
|
||||
})
|
||||
.limit(MAX_RECORDS_LIMIT)
|
||||
.getMany()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { IsNull, Not } from 'typeorm'
|
||||
import { getRepository } from '../entity'
|
||||
import { Feature } from '../entity/feature'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { AppDataSource } from '../server'
|
||||
import { logger } from '../utils/logger'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity'
|
||||
import { Follower } from '../entity/follower'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { User } from '../entity/user'
|
||||
|
||||
export const getUserFollowers = async (
|
||||
user: User,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { nanoid } from 'nanoid'
|
||||
import { getRepository } from '../entity'
|
||||
import { Group } from '../entity/groups/group'
|
||||
import { GroupMembership } from '../entity/groups/group_membership'
|
||||
import { Invite } from '../entity/groups/invite'
|
||||
import { RuleActionType } from '../entity/rule'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { homePageURL } from '../env'
|
||||
import { RecommendationGroup, User as GraphqlUser } from '../generated/graphql'
|
||||
import { AppDataSource } from '../server'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios'
|
||||
import { HighlightType, Page } from '../../elastic/types'
|
||||
import { getRepository } from '../../entity'
|
||||
import { Integration } from '../../entity/integration'
|
||||
import { getRepository } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import { wait } from '../../utils/helpers'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import DataLoader from 'dataloader'
|
|||
import { In } from 'typeorm'
|
||||
import { addLabelInPage } from '../elastic/labels'
|
||||
import { PageContext } from '../elastic/types'
|
||||
import { getRepository } from '../entity'
|
||||
import { Label } from '../entity/label'
|
||||
import { Link } from '../entity/link'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { CreateLabelInput } from '../generated/graphql'
|
||||
import { generateRandomColor } from '../utils/helpers'
|
||||
import { logger } from '../utils/logger'
|
||||
|
|
@ -117,18 +117,10 @@ export const createLabels = async (
|
|||
ctx: PageContext,
|
||||
labels: CreateLabelInput[]
|
||||
): Promise<Label[]> => {
|
||||
const user = await getRepository(User).findOneBy({
|
||||
id: ctx.uid,
|
||||
})
|
||||
if (!user) {
|
||||
logger.error('user not found')
|
||||
return []
|
||||
}
|
||||
|
||||
const labelEntities = await getRepository(Label)
|
||||
.createQueryBuilder()
|
||||
.where({
|
||||
user: { id: user.id },
|
||||
user: { id: ctx.uid },
|
||||
})
|
||||
.andWhere('LOWER(name) IN (:...names)', {
|
||||
names: labels.map((l) => l.name.toLowerCase()),
|
||||
|
|
@ -148,7 +140,7 @@ export const createLabels = async (
|
|||
description: l.description,
|
||||
color: l.color || generateRandomColor(),
|
||||
internal: isLabelInternal(l.name),
|
||||
user,
|
||||
user: { id: ctx.uid },
|
||||
}))
|
||||
)
|
||||
return [...labelEntities, ...newLabelEntities]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { NewsletterEmail } from '../entity/newsletter_email'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { getRepository } from '../entity'
|
||||
import { NewsletterEmail } from '../entity/newsletter_email'
|
||||
import { User } from '../entity/user'
|
||||
import { env } from '../env'
|
||||
import {
|
||||
CreateNewsletterEmailErrorCode,
|
||||
SubscriptionStatus,
|
||||
} from '../generated/graphql'
|
||||
import { env } from '../env'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import addressparser = require('nodemailer/lib/addressparser')
|
||||
|
||||
const parsedAddress = (emailAddress: string): string | undefined => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getRepository } from '../entity'
|
||||
import { ReceivedEmail } from '../entity/received_email'
|
||||
import { getRepository } from '../entity/utils'
|
||||
|
||||
export const saveReceivedEmail = async (
|
||||
from: string,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { IsNull, Not } from 'typeorm'
|
||||
import { EntityManager, IsNull, Not } from 'typeorm'
|
||||
import { getPageById } from '../elastic/pages'
|
||||
import { getRepository } from '../entity'
|
||||
import { Reminder } from '../entity/reminder'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
export interface PageReminder {
|
||||
|
|
@ -52,3 +52,23 @@ export const getPagesWithReminder = async (
|
|||
|
||||
return results
|
||||
}
|
||||
|
||||
export const setRemindersComplete = async (
|
||||
tx: EntityManager,
|
||||
userId: string,
|
||||
remindAt: Date
|
||||
): Promise<Reminder | null> => {
|
||||
const updateResult = await tx
|
||||
.createQueryBuilder()
|
||||
.where({ userId, remindAt })
|
||||
.update(Reminder)
|
||||
.set({ status: 'COMPLETED' })
|
||||
.returning('*')
|
||||
.execute()
|
||||
|
||||
if (updateResult.generatedMaps.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return updateResult.generatedMaps[0] as Reminder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { getPageById } from '../elastic/pages'
|
||||
import { getRepository } from '../entity'
|
||||
import { AbuseReport } from '../entity/reports/abuse_report'
|
||||
import { ContentDisplayReport } from '../entity/reports/content_display_report'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { ReportItemInput, ReportType } from '../generated/graphql'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Rule, RuleAction } from '../entity/rule'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { ILike } from 'typeorm'
|
||||
import { getRepository } from '../entity'
|
||||
import { Rule, RuleAction } from '../entity/rule'
|
||||
|
||||
export const createRule = async (
|
||||
userId: string,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Knex } from 'knex'
|
||||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import { UserData } from '../datalayer/user/model'
|
||||
import { updatePage } from '../elastic/pages'
|
||||
import { getRepository } from '../entity'
|
||||
import { UploadFile } from '../entity/upload_file'
|
||||
import { homePageURL } from '../env'
|
||||
import {
|
||||
ArticleSavingRequestStatus,
|
||||
|
|
@ -9,33 +9,30 @@ import {
|
|||
SaveFileInput,
|
||||
SaveResult,
|
||||
} from '../generated/graphql'
|
||||
import { DataModels } from '../resolvers/types'
|
||||
import { WithDataSourcesContext } from '../resolvers/types'
|
||||
import { AppDataSource } from '../server'
|
||||
import { logger } from '../utils/logger'
|
||||
import { getStorageFileDetails } from '../utils/uploads'
|
||||
import { createLabels } from './labels'
|
||||
|
||||
type SaveContext = {
|
||||
pubsub: PubsubClient
|
||||
models: DataModels
|
||||
authTrx: <TResult>(
|
||||
cb: (tx: Knex.Transaction) => TResult,
|
||||
userRole?: string
|
||||
) => Promise<TResult>
|
||||
uid: string
|
||||
export const setFileUploadComplete = async (
|
||||
id: string,
|
||||
em = AppDataSource.createEntityManager()
|
||||
): Promise<UploadFile | null> => {
|
||||
return em.getRepository(UploadFile).save({ id, status: 'COMPLETED' })
|
||||
}
|
||||
|
||||
export const saveFile = async (
|
||||
ctx: SaveContext,
|
||||
saver: UserData,
|
||||
ctx: WithDataSourcesContext,
|
||||
user: UserData,
|
||||
input: SaveFileInput
|
||||
): Promise<SaveResult> => {
|
||||
logger.info('saving file with input', input)
|
||||
const pageId = input.clientRequestId
|
||||
const uploadFile = await ctx.models.uploadFile.getWhere({
|
||||
const uploadFile = await getRepository(UploadFile).findOneBy({
|
||||
id: input.uploadFileId,
|
||||
userId: saver.id,
|
||||
user: { id: ctx.uid },
|
||||
})
|
||||
|
||||
if (!uploadFile) {
|
||||
return {
|
||||
errorCodes: [SaveErrorCode.Unauthorized],
|
||||
|
|
@ -45,7 +42,7 @@ export const saveFile = async (
|
|||
await getStorageFileDetails(input.uploadFileId, uploadFile.fileName)
|
||||
|
||||
const uploadFileData = await ctx.authTrx(async (tx) => {
|
||||
return ctx.models.uploadFile.setFileUploadComplete(input.uploadFileId, tx)
|
||||
return setFileUploadComplete(input.uploadFileId, tx)
|
||||
})
|
||||
|
||||
if (!uploadFileData) {
|
||||
|
|
@ -59,7 +56,7 @@ export const saveFile = async (
|
|||
input.state === ArticleSavingRequestStatus.Archived ? new Date() : null
|
||||
// add labels to page
|
||||
const labels = input.labels
|
||||
? await createLabels({ ...ctx, uid: saver.id }, input.labels)
|
||||
? await createLabels(ctx, input.labels)
|
||||
: undefined
|
||||
if (input.state || input.labels) {
|
||||
const updated = await updatePage(
|
||||
|
|
@ -80,7 +77,7 @@ export const saveFile = async (
|
|||
|
||||
return {
|
||||
clientRequestId: input.clientRequestId,
|
||||
url: `${homePageURL()}/${saver.profile.username}/links/${
|
||||
url: `${homePageURL()}/${user.profile.username}/links/${
|
||||
input.clientRequestId
|
||||
}`,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Readability } from '@omnivore/readability'
|
||||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import { addHighlightToPage } from '../elastic/highlights'
|
||||
import { createPage, getPageByParam, updatePage } from '../elastic/pages'
|
||||
import { ArticleSavingRequestStatus, Page, PageType } from '../elastic/types'
|
||||
import { User } from '../entity/user'
|
||||
import { homePageURL } from '../env'
|
||||
import {
|
||||
HighlightType,
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
SavePageInput,
|
||||
SaveResult,
|
||||
} from '../generated/graphql'
|
||||
import { DataModels } from '../resolvers/types'
|
||||
import { WithDataSourcesContext } from '../resolvers/types'
|
||||
import { enqueueThumbnailTask } from '../utils/createTask'
|
||||
import {
|
||||
cleanUrl,
|
||||
|
|
@ -27,18 +27,6 @@ import { parsePreparedContent } from '../utils/parser'
|
|||
import { createPageSaveRequest } from './create_page_save_request'
|
||||
import { createLabels } from './labels'
|
||||
|
||||
type SaveContext = {
|
||||
pubsub: PubsubClient
|
||||
models: DataModels
|
||||
uid: string
|
||||
refresh?: boolean
|
||||
}
|
||||
|
||||
type SaverUserData = {
|
||||
userId: string
|
||||
username: string
|
||||
}
|
||||
|
||||
// where we can use APIs to fetch their underlying content.
|
||||
const FORCE_PUPPETEER_URLS = [
|
||||
TWEET_URL_REGEX,
|
||||
|
|
@ -65,19 +53,9 @@ const shouldParseInBackend = (input: SavePageInput): boolean => {
|
|||
)
|
||||
}
|
||||
|
||||
export const createSavingRequest = (
|
||||
ctx: SaveContext,
|
||||
clientRequestId: string
|
||||
) => {
|
||||
return ctx.models.articleSavingRequest.create({
|
||||
userId: ctx.uid,
|
||||
id: clientRequestId,
|
||||
})
|
||||
}
|
||||
|
||||
export const savePage = async (
|
||||
ctx: SaveContext,
|
||||
saver: SaverUserData,
|
||||
ctx: WithDataSourcesContext,
|
||||
user: User,
|
||||
input: SavePageInput
|
||||
): Promise<SaveResult> => {
|
||||
const parseResult = await parsePreparedContent(
|
||||
|
|
@ -97,7 +75,7 @@ export const savePage = async (
|
|||
const articleToSave = parsedContentToPage({
|
||||
url: input.url,
|
||||
title: input.title,
|
||||
userId: saver.userId,
|
||||
userId: user.id,
|
||||
pageId,
|
||||
slug,
|
||||
croppedPathname,
|
||||
|
|
@ -124,7 +102,7 @@ export const savePage = async (
|
|||
if (shouldParseInBackend(input)) {
|
||||
try {
|
||||
await createPageSaveRequest({
|
||||
userId: saver.userId,
|
||||
userId: user.id,
|
||||
url: articleToSave.url,
|
||||
pubsub: ctx.pubsub,
|
||||
articleSavingRequestId: input.clientRequestId,
|
||||
|
|
@ -140,7 +118,7 @@ export const savePage = async (
|
|||
} else {
|
||||
// check if the page already exists
|
||||
const existingPage = await getPageByParam({
|
||||
userId: saver.userId,
|
||||
userId: user.id,
|
||||
url: articleToSave.url,
|
||||
})
|
||||
if (existingPage) {
|
||||
|
|
@ -151,7 +129,7 @@ export const savePage = async (
|
|||
) {
|
||||
return {
|
||||
clientRequestId: pageId,
|
||||
url: `${homePageURL()}/${saver.username}/${slug}`,
|
||||
url: `${homePageURL()}/${user.profile.username}/${slug}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +173,7 @@ export const savePage = async (
|
|||
if (!isImported) {
|
||||
try {
|
||||
// create a task to update thumbnail and pre-cache all images
|
||||
const taskId = await enqueueThumbnailTask(saver.userId, slug)
|
||||
const taskId = await enqueueThumbnailTask(user.id, slug)
|
||||
logger.info('Created thumbnail task', { taskId })
|
||||
} catch (e) {
|
||||
logger.error('Failed to create thumbnail task', e)
|
||||
|
|
@ -222,7 +200,7 @@ export const savePage = async (
|
|||
|
||||
return {
|
||||
clientRequestId: pageId,
|
||||
url: `${homePageURL()}/${saver.username}/${slug}`,
|
||||
url: `${homePageURL()}/${user.profile.username}/${slug}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { PubsubClient } from '../datalayer/pubsub'
|
||||
import { ArticleSavingRequestStatus } from '../elastic/types'
|
||||
import { getRepository } from '../entity'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { homePageURL } from '../env'
|
||||
import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql'
|
||||
import { logger } from '../utils/logger'
|
||||
|
|
@ -24,7 +24,7 @@ export const saveUrl = async (
|
|||
input.state === ArticleSavingRequestStatus.Archived ? new Date() : null
|
||||
// add labels to page
|
||||
const labels = input.labels
|
||||
? await createLabels({ ...ctx, uid: ctx.uid }, input.labels)
|
||||
? await createLabels(ctx, input.labels)
|
||||
: undefined
|
||||
|
||||
const pageSaveRequest = await createPageSaveRequest({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getRepository } from '../entity'
|
||||
import { SearchHistory } from '../entity/search_history'
|
||||
import { getRepository } from '../entity/utils'
|
||||
|
||||
export const getRecentSearches = async (
|
||||
userId: string
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import axios from 'axios'
|
||||
import { getRepository } from '../entity'
|
||||
import { NewsletterEmail } from '../entity/newsletter_email'
|
||||
import { Subscription } from '../entity/subscription'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { SubscriptionStatus, SubscriptionType } from '../generated/graphql'
|
||||
import { logger } from '../utils/logger'
|
||||
import { sendEmail } from '../utils/sendEmail'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { UserDeviceToken } from '../entity/user_device_tokens'
|
||||
import { getRepository, setClaims } from '../entity'
|
||||
import { User } from '../entity/user'
|
||||
import { SetDeviceTokenErrorCode } from '../generated/graphql'
|
||||
import { getRepository, setClaims } from '../entity/utils'
|
||||
import { analytics } from '../utils/analytics'
|
||||
import { UserDeviceToken } from '../entity/user_device_tokens'
|
||||
import { env } from '../env'
|
||||
import { SetDeviceTokenErrorCode } from '../generated/graphql'
|
||||
import { AppDataSource } from '../server'
|
||||
import { analytics } from '../utils/analytics'
|
||||
|
||||
export const getDeviceToken = async (
|
||||
id: string
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import express from 'express'
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { promisify } from 'util'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getRepository } from '../entity'
|
||||
import { ApiKey } from '../entity/api_key'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { Claims, ClaimsToSet } from '../resolvers/types'
|
||||
import { logger } from './logger'
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import path from 'path'
|
|||
import _ from 'underscore'
|
||||
import slugify from 'voca/slugify'
|
||||
import wordsCounter from 'word-counting'
|
||||
import { RegistrationType, UserData } from '../datalayer/user/model'
|
||||
import { updatePage } from '../elastic/pages'
|
||||
import { ArticleSavingRequestStatus, Page } from '../elastic/types'
|
||||
import { User } from '../entity/user'
|
||||
import { RegistrationType, User } from '../entity/user'
|
||||
import {
|
||||
ArticleSavingRequest,
|
||||
CreateArticleError,
|
||||
|
|
@ -116,7 +115,7 @@ export const findDelimiter = (
|
|||
// FIXME: Remove this Date stub after nullable types will be fixed
|
||||
export const userDataToUser = (
|
||||
user: Merge<
|
||||
UserData,
|
||||
User,
|
||||
{
|
||||
isFriend?: boolean
|
||||
followersCount?: number
|
||||
|
|
@ -156,15 +155,11 @@ export const userDataToUser = (
|
|||
followersCount: user.followersCount || 0,
|
||||
isFullUser: true,
|
||||
viewerIsFollowing: user.viewerIsFollowing || user.isFriend || false,
|
||||
picture: user.profile.picture_url,
|
||||
picture: user.profile.pictureUrl,
|
||||
sharedArticles: [],
|
||||
sharedArticlesCount: user.sharedArticlesCount || 0,
|
||||
sharedHighlightsCount: user.sharedHighlightsCount || 0,
|
||||
sharedNotesCount: user.sharedNotesCount || 0,
|
||||
profile: {
|
||||
...user.profile,
|
||||
pictureUrl: user.profile.picture_url,
|
||||
},
|
||||
})
|
||||
|
||||
export const generateSlug = (title: string): string => {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import { ILike } from 'typeorm'
|
|||
import { promisify } from 'util'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { Highlight } from '../elastic/types'
|
||||
import { getRepository } from '../entity'
|
||||
import { User } from '../entity/user'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { PageType, PreparedDocumentInput } from '../generated/graphql'
|
||||
import { ArticleFormat } from '../resolvers/article'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Postgrator from 'postgrator'
|
||||
import { FindOptionsWhere } from 'typeorm'
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
|
||||
import { getRepository, setClaims } from '../src/entity'
|
||||
import { Integration } from '../src/entity/integration'
|
||||
import { Label } from '../src/entity/label'
|
||||
import { Link } from '../src/entity/link'
|
||||
|
|
@ -11,7 +12,6 @@ import { Reminder } from '../src/entity/reminder'
|
|||
import { Subscription } from '../src/entity/subscription'
|
||||
import { User } from '../src/entity/user'
|
||||
import { UserDeviceToken } from '../src/entity/user_device_tokens'
|
||||
import { getRepository, setClaims } from '../src/entity/utils'
|
||||
import { SubscriptionStatus, SubscriptionType } from '../src/generated/graphql'
|
||||
import { AppDataSource } from '../src/server'
|
||||
import { createUser } from '../src/services/create_user'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { createTestUser, deleteTestUser } from '../db'
|
|||
import { graphqlRequest, request } from '../util'
|
||||
import { expect } from 'chai'
|
||||
import supertest from 'supertest'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { ApiKey } from '../../src/entity/api_key'
|
||||
|
||||
const testAPIKey = (apiKey: string): supertest.Test => {
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import {
|
|||
PageContext,
|
||||
PageType,
|
||||
} from '../../src/elastic/types'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { UploadFile } from '../../src/entity/upload_file'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import {
|
||||
BulkActionType,
|
||||
SyncUpdatedItemEdge,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { expect } from 'chai'
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import 'mocha'
|
||||
import sinon, { SinonFakeTimers } from 'sinon'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { Feature } from '../../src/entity/feature'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { env } from '../../src/env'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import { graphqlRequest, request } from '../util'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { createTestUser, deleteTestIntegrations, deleteTestUser } from '../db'
|
|||
import { generateFakeUuid, graphqlRequest, request } from '../util'
|
||||
import { SetIntegrationErrorCode } from '../../src/generated/graphql'
|
||||
import chai, { expect } from 'chai'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { Integration } from '../../src/entity/integration'
|
||||
import nock from 'nock'
|
||||
import { READWISE_API_URL } from '../../src/services/integrations/readwise'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
Page,
|
||||
PageContext,
|
||||
} from '../../src/elastic/types'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { deletePage, getPageById } from '../../src/elastic/pages'
|
||||
import { createPubSubClient } from '../../src/datalayer/pubsub'
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
SubscriptionStatus,
|
||||
} from '../../src/generated/graphql'
|
||||
import 'mocha'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
|
||||
describe('Newsletters API', () => {
|
||||
let user: User
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { expect } from 'chai'
|
|||
import { User } from '../../src/entity/user'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import { graphqlRequest, request } from '../util'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { ReceivedEmail } from '../../src/entity/received_email'
|
||||
import { NewsletterEmail } from '../../src/entity/newsletter_email'
|
||||
import sinon from 'sinon'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { PageContext } from '../../src/elastic/types'
|
|||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import { graphqlRequest, request } from '../util'
|
||||
import { createPubSubClient } from '../../src/datalayer/pubsub'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { SearchHistory } from '../../src/entity/search_history'
|
||||
|
||||
describe('recent_searches resolver', () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { createTestElasticPage, graphqlRequest, request } from '../util'
|
|||
import { ReportType } from '../../src/generated/graphql'
|
||||
import { ContentDisplayReport } from '../../src/entity/reports/content_display_report'
|
||||
import { expect } from 'chai'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
|
||||
describe('Report API', () => {
|
||||
let user: User
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { expect } from 'chai'
|
|||
import { graphqlRequest, request } from '../util'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { Rule, RuleAction, RuleActionType } from '../../src/entity/rule'
|
||||
|
||||
describe('Rules Resolver', () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import sinonChai from 'sinon-chai'
|
|||
import { NewsletterEmail } from '../../src/entity/newsletter_email'
|
||||
import { Subscription } from '../../src/entity/subscription'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import {
|
||||
SubscriptionStatus,
|
||||
SubscriptionType,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { UserDeviceToken } from '../../src/entity/user_device_tokens'
|
|||
import { SetDeviceTokenErrorCode } from '../../src/generated/graphql'
|
||||
import 'mocha'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
|
||||
describe('Device tokens API', () => {
|
||||
let authToken: string
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Page } from '../../src/entity/page'
|
|||
import { Link } from '../../src/entity/link'
|
||||
import { Highlight } from '../../src/entity/highlight'
|
||||
import 'mocha'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { User } from '../../src/entity/user'
|
||||
|
||||
describe('User feed article API', () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'mocha'
|
|||
import { User } from '../../src/entity/user'
|
||||
import { WebhookEvent } from '../../src/generated/graphql'
|
||||
import { Webhook } from '../../src/entity/webhook'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
|
||||
describe('Webhooks API', () => {
|
||||
let user: User
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import sinonChai from 'sinon-chai'
|
|||
import supertest from 'supertest'
|
||||
import { StatusType } from '../../src/datalayer/user/model'
|
||||
import { searchPages } from '../../src/elastic/pages'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { AuthProvider } from '../../src/routers/auth/auth_types'
|
||||
import { createPendingUserToken } from '../../src/routers/auth/jwt_helpers'
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { request } from '../util'
|
|||
import * as parser from '../../src/utils/parser'
|
||||
import * as sendNotification from '../../src/utils/sendNotification'
|
||||
import * as sendEmail from '../../src/utils/sendEmail'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { ReceivedEmail } from '../../src/entity/received_email'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import {
|
|||
Page,
|
||||
PageContext,
|
||||
} from '../../src/elastic/types'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { Integration, IntegrationType } from '../../src/entity/integration'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { env } from '../../src/env'
|
||||
import { getHighlightUrl } from '../../src/services/highlights'
|
||||
import { READWISE_API_URL } from '../../src/services/integrations/readwise'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { createTestUser, deleteTestUser } from '../db'
|
|||
import { request } from '../util'
|
||||
import { User } from '../../src/entity/user'
|
||||
import 'mocha'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { Webhook } from '../../src/entity/webhook'
|
||||
import { expect } from 'chai'
|
||||
import nock from 'nock'
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@ import 'mocha'
|
|||
import chai, { expect } from 'chai'
|
||||
import { createTestUser, deleteTestUser } from '../db'
|
||||
import sinonChai from 'sinon-chai'
|
||||
import sinon from 'sinon'
|
||||
import * as util from '../../src/utils/sendEmail'
|
||||
import { MailDataRequired } from '@sendgrid/helpers/classes/mail'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { ContentDisplayReport } from '../../src/entity/reports/content_display_report'
|
||||
import { saveContentDisplayReport } from '../../src/services/reports'
|
||||
import { ReportType } from '../../src/generated/graphql'
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
getUserFollowers,
|
||||
getUserFollowing,
|
||||
} from '../../src/services/followers'
|
||||
import { StatusType } from '../../src/datalayer/user/model'
|
||||
import sinonChai from 'sinon-chai'
|
||||
import sinon from 'sinon'
|
||||
import * as util from '../../src/utils/sendEmail'
|
||||
|
|
@ -19,6 +18,7 @@ import { MailDataRequired } from '@sendgrid/helpers/classes/mail'
|
|||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { Filter } from "../../src/entity/filter"
|
||||
import { getRepository } from '../../src/entity'
|
||||
|
||||
chai.use(sinonChai)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { LinkLabel } from '../../src/entity/link_label'
|
|||
import { Label } from '../../src/entity/label'
|
||||
import { Link } from '../../src/entity/link'
|
||||
import { labelsLoader } from '../../src/services/labels'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { User } from '../../src/entity/user'
|
||||
|
||||
describe('batch get labels from linkIds', () => {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import 'mocha'
|
|||
import nock from 'nock'
|
||||
import { createPubSubClient } from '../../src/datalayer/pubsub'
|
||||
import { getPageByParam } from '../../src/elastic/pages'
|
||||
import { getRepository } from '../../src/entity'
|
||||
import { NewsletterEmail } from '../../src/entity/newsletter_email'
|
||||
import { ReceivedEmail } from '../../src/entity/received_email'
|
||||
import { Subscription } from '../../src/entity/subscription'
|
||||
import { User } from '../../src/entity/user'
|
||||
import { getRepository } from '../../src/entity/utils'
|
||||
import { createNewsletterEmail } from '../../src/services/newsletters'
|
||||
import { SaveContext } from '../../src/services/save_email'
|
||||
import { saveNewsletterEmail } from '../../src/services/save_newsletter_email'
|
||||
|
|
|
|||
Loading…
Reference in a new issue