fix easy types

This commit is contained in:
Ulf Gebhardt 2026-02-23 10:28:04 +01:00
parent fc5d3aca8e
commit a3db2ae89e
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
37 changed files with 337 additions and 379 deletions

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable @typescript-eslint/require-await */
import { hashSync } from 'bcryptjs' import { hashSync } from 'bcryptjs'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
@ -18,7 +17,7 @@ const defaultAdmin = {
const createDefaultAdminUser = async () => { const createDefaultAdminUser = async () => {
const driver = getDriver() const driver = getDriver()
const session = driver.session() const session = driver.session()
const createAdminTxResultPromise = session.writeTransaction(async (txc) => { const createAdminTxResultPromise = session.writeTransaction((txc) => {
txc.run( txc.run(
`MERGE (e:EmailAddress { `MERGE (e:EmailAddress {
email: "${defaultAdmin.email}", email: "${defaultAdmin.email}",

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -38,10 +37,10 @@ const swap = async function (next) {
} }
} }
export async function up(next) { export function up(next) {
swap(next) swap(next)
} }
export async function down(next) { export function down(next) {
swap(next) swap(next)
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -42,7 +41,7 @@ export async function up(next) {
} }
} }
export async function down(next) { export function down(next) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Irreversible migration') console.log('Irreversible migration')
next() next()

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -44,7 +43,7 @@ export async function up(next) {
} }
} }
export async function down(next) { export function down(next) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Irreversible migration') console.log('Irreversible migration')
next() next()

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
@ -65,6 +64,6 @@ export async function up(next) {
} }
} }
export async function down() { export function down() {
throw new Error('Irreversible migration') throw new Error('Irreversible migration')
} }

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-base-to-string */ /* eslint-disable @typescript-eslint/no-base-to-string */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { getDriver } from '@db/neo4j' import { getDriver } from '@db/neo4j'
@ -31,9 +30,9 @@ export async function up(_next) {
eventEnd = date.toISOString() eventEnd = date.toISOString()
} }
await transaction.run(` await transaction.run(`
MATCH (e:Event { id: '${id}' }) MATCH (e:Event { id: '${String(id)}' })
SET e.eventStart = '${eventStart}' SET e.eventStart = '${String(eventStart)}'
SET (CASE WHEN exists(e.eventEnd) THEN e END).eventEnd = '${eventEnd}' SET (CASE WHEN exists(e.eventEnd) THEN e END).eventEnd = '${String(eventEnd)}'
RETURN e RETURN e
`) `)
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
import CONFIG from '@config/index' import CONFIG from '@config/index'
@ -16,7 +15,7 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
process.exit(0) process.exit(0)
// eslint-disable-next-line no-catch-all/no-catch-all // eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) { } catch (err) {
console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`) // eslint-disable-line no-console console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${String(err)}`) // eslint-disable-line no-console
process.exit(1) process.exit(1)
} }
})() })()

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
import CONFIG from '@config/index' import CONFIG from '@config/index'
@ -16,7 +15,7 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
process.exit(0) process.exit(0)
// eslint-disable-next-line no-catch-all/no-catch-all // eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) { } catch (err) {
console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`) // eslint-disable-line no-console console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${String(err)}`) // eslint-disable-line no-console
process.exit(1) process.exit(1)
} }
})() })()

View File

@ -3,7 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable n/no-unpublished-import */ /* eslint-disable n/no-unpublished-import */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-confusing-void-expression */ /* eslint-disable @typescript-eslint/no-confusing-void-expression */
import { faker } from '@faker-js/faker' import { faker } from '@faker-js/faker'

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -29,7 +28,7 @@ export const defaultVerificationBadge = {
export default { export default {
Query: { Query: {
Badge: async (object, args, context, resolveInfo) => Badge: (object, args, context, resolveInfo) =>
neo4jgraphql(object, args, context, resolveInfo), neo4jgraphql(object, args, context, resolveInfo),
}, },
@ -172,7 +171,7 @@ export default {
}, },
}, },
Badge: { Badge: {
isDefault: async (parent, _params, _context, _resolveInfo) => isDefault: (parent, _params, _context, _resolveInfo) =>
[defaultTrophyBadge.id, defaultVerificationBadge.id].includes(parent.id), [defaultTrophyBadge.id, defaultVerificationBadge.id].includes(parent.id),
}, },
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import scrape from './embeds/scraper' import scrape from './embeds/scraper'
@ -6,7 +5,7 @@ import { undefinedToNullResolver } from './helpers/Resolver'
export default { export default {
Query: { Query: {
embed: async (_object, { url }, _context, _resolveInfo) => { embed: (_object, { url }, _context, _resolveInfo) => {
return scrape(url) return scrape(url)
}, },
}, },
@ -25,7 +24,7 @@ export default {
'lang', 'lang',
'html', 'html',
]), ]),
sources: async (parent, _params, _context, _resolveInfo) => { sources: (parent, _params, _context, _resolveInfo) => {
return typeof parent.sources === 'undefined' ? [] : parent.sources return typeof parent.sources === 'undefined' ? [] : parent.sources
}, },
}, },

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
@ -53,7 +52,7 @@ const fetchEmbed = async (url) => {
json = await response.json() json = await response.json()
// eslint-disable-next-line no-catch-all/no-catch-all // eslint-disable-next-line no-catch-all/no-catch-all
} catch (err) { } catch (err) {
error(`Error fetching embed data: ${err.message}`) error(`Error fetching embed data: ${String(err.message)}`)
return {} return {}
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -6,7 +5,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable @typescript-eslint/no-use-before-define */
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories' import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories'
@ -20,6 +18,35 @@ import { createOrUpdateLocations } from './users/location'
import type { Context } from '@src/context' import type { Context } from '@src/context'
const removeUserFromGroupWriteTxResultPromise = async (session, groupId, userId) => {
return session.writeTransaction(async (transaction) => {
const removeUserFromGroupCypher = `
MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId})
DELETE membership
WITH user, group
OPTIONAL MATCH (author:User)-[:WROTE]->(p:Post)-[:IN]->(group)
WHERE NOT group.groupType = 'public'
AND NOT author.id = $userId
WITH user, collect(p) AS posts
FOREACH (post IN posts |
MERGE (user)-[:CANNOT_SEE]->(post))
RETURN user {.*}, NULL as membership
`
const transactionResponse = await transaction.run(removeUserFromGroupCypher, {
groupId,
userId,
})
const [result] = transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
if (!result) {
throw new UserInputError('User is not a member of this group')
}
return result
})
}
export default { export default {
Query: { Query: {
Group: async (_object, params, context: Context, _resolveInfo) => { Group: async (_object, params, context: Context, _resolveInfo) => {
@ -488,13 +515,13 @@ export default {
membersCount: '<-[:MEMBER_OF]-(related:User)', membersCount: '<-[:MEMBER_OF]-(related:User)',
}, },
}), }),
name: async (parent, _args, context: Context, _resolveInfo) => { name: (parent, _args, context: Context, _resolveInfo) => {
if (!context.user) { if (!context.user) {
return parent.groupType === 'hidden' ? '' : parent.name return parent.groupType === 'hidden' ? '' : parent.name
} }
return parent.name return parent.name
}, },
about: async (parent, _args, context: Context, _resolveInfo) => { about: (parent, _args, context: Context, _resolveInfo) => {
if (!context.user) { if (!context.user) {
return parent.groupType === 'hidden' ? '' : parent.about return parent.groupType === 'hidden' ? '' : parent.about
} }
@ -502,32 +529,3 @@ export default {
}, },
}, },
} }
const removeUserFromGroupWriteTxResultPromise = async (session, groupId, userId) => {
return session.writeTransaction(async (transaction) => {
const removeUserFromGroupCypher = `
MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId})
DELETE membership
WITH user, group
OPTIONAL MATCH (author:User)-[:WROTE]->(p:Post)-[:IN]->(group)
WHERE NOT group.groupType = 'public'
AND NOT author.id = $userId
WITH user, collect(p) AS posts
FOREACH (post IN posts |
MERGE (user)-[:CANNOT_SEE]->(post))
RETURN user {.*}, NULL as membership
`
const transactionResponse = await transaction.run(removeUserFromGroupCypher, {
groupId,
userId,
})
const [result] = transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
if (!result) {
throw new UserInputError('User is not a member of this group')
}
return result
})
}

View File

@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/no-dynamic-delete */ /* eslint-disable @typescript-eslint/no-dynamic-delete */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -11,7 +9,7 @@
export const undefinedToNullResolver = (list) => { export const undefinedToNullResolver = (list) => {
const resolvers = {} const resolvers = {}
list.forEach((key) => { list.forEach((key) => {
resolvers[key] = async (parent) => { resolvers[key] = (parent) => {
return typeof parent[key] === 'undefined' ? null : parent[key] return typeof parent[key] === 'undefined' ? null : parent[key]
} }
}) })
@ -37,7 +35,7 @@ export default function Resolver(type, options: any = {}) {
try { try {
let response = await session.readTransaction(async (txc) => { let response = await session.readTransaction(async (txc) => {
const cypher = ` const cypher = `
MATCH(:${type} {${idAttribute}: $id})${connection} MATCH(:${String(type)} {${String(idAttribute)}: $id})${String(connection)}
RETURN related {.*} as related RETURN related {.*} as related
` `
const result = await txc.run(cypher, { id, cypherParams }) const result = await txc.run(cypher, { id, cypherParams })
@ -62,7 +60,7 @@ export default function Resolver(type, options: any = {}) {
try { try {
return await session.readTransaction(async (txc) => { return await session.readTransaction(async (txc) => {
const nodeCondition = condition.replace('this', 'this {id: $id}') const nodeCondition = condition.replace('this', 'this {id: $id}')
const cypher = `${nodeCondition} as ${key}` const cypher = `${String(nodeCondition)} as ${key}`
const result = await txc.run(cypher, { id, cypherParams }) const result = await txc.run(cypher, { id, cypherParams })
const [response] = result.records.map((r) => r.get(key)) const [response] = result.records.map((r) => r.get(key))
return response return response
@ -85,7 +83,7 @@ export default function Resolver(type, options: any = {}) {
return await session.readTransaction(async (txc) => { return await session.readTransaction(async (txc) => {
const id = parent[idAttribute] const id = parent[idAttribute]
const cypher = ` const cypher = `
MATCH(u:${type} {${idAttribute}: $id})${connection} MATCH(u:${String(type)} {${String(idAttribute)}: $id})${String(connection)}
RETURN COUNT(DISTINCT(related)) as count RETURN COUNT(DISTINCT(related)) as count
` `
const result = await txc.run(cypher, { id, cypherParams }) const result = await txc.run(cypher, { id, cypherParams })
@ -141,7 +139,7 @@ export const convertObjectToCypherMapLiteral = (params, addSpaceInfrontIfMapIsNo
let mapLiteral = '' let mapLiteral = ''
paramsEntries.forEach((ele, index) => { paramsEntries.forEach((ele, index) => {
mapLiteral += index === 0 ? '{' : '' mapLiteral += index === 0 ? '{' : ''
mapLiteral += `${ele[0]}: "${ele[1]}"` mapLiteral += `${ele[0]}: "${String(ele[1])}"`
mapLiteral += index < paramsEntries.length - 1 ? ', ' : '}' mapLiteral += index < paramsEntries.length - 1 ? ', ' : '}'
}) })
mapLiteral = (addSpaceInfrontIfMapIsNotEmpty && mapLiteral.length > 0 ? ' ' : '') + mapLiteral mapLiteral = (addSpaceInfrontIfMapIsNotEmpty && mapLiteral.length > 0 ? ' ' : '') + mapLiteral

View File

@ -2,9 +2,31 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-use-before-define */
import { UserInputError } from '@graphql/errors' import { UserInputError } from '@graphql/errors'
const validateEventDate = (dateString) => {
const date = new Date(dateString)
if (date.toString() === 'Invalid Date')
throw new UserInputError('Event start date must be a valid date!')
if (date.toISOString() !== dateString)
throw new UserInputError('Event start date must be in ISO format!')
const now = new Date()
if (date.getTime() < now.getTime()) {
throw new UserInputError('Event start date must be in the future!')
}
}
const validateEventEnd = (start, end) => {
const endDate = new Date(end)
if (endDate.toString() === 'Invalid Date')
throw new UserInputError('Event end date must be a valid date!')
if (endDate.toISOString() !== end)
throw new UserInputError('Event end date must be in ISO format!')
const startDate = new Date(start)
if (endDate < startDate)
throw new UserInputError('Event end date must be a after event start date!')
}
export const validateEventParams = (params) => { export const validateEventParams = (params) => {
let locationName = null let locationName = null
if (params.postType && params.postType === 'Event') { if (params.postType && params.postType === 'Event') {
@ -34,26 +56,3 @@ export const validateEventParams = (params) => {
delete params.eventInput delete params.eventInput
return locationName return locationName
} }
const validateEventDate = (dateString) => {
const date = new Date(dateString)
if (date.toString() === 'Invalid Date')
throw new UserInputError('Event start date must be a valid date!')
if (date.toISOString() !== dateString)
throw new UserInputError('Event start date must be in ISO format!')
const now = new Date()
if (date.getTime() < now.getTime()) {
throw new UserInputError('Event start date must be in the future!')
}
}
const validateEventEnd = (start, end) => {
const endDate = new Date(end)
if (endDate.toString() === 'Invalid Date')
throw new UserInputError('Event end date must be a valid date!')
if (endDate.toISOString() !== end)
throw new UserInputError('Event end date must be in ISO format!')
const startDate = new Date(start)
if (endDate < startDate)
throw new UserInputError('Event end date must be a after event start date!')
}

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import crypto from 'node:crypto' import crypto from 'node:crypto'
import { join as joinPath } from 'node:path/posix' import { join as joinPath } from 'node:path/posix'
@ -15,6 +13,16 @@ type UrlResolver = (
}: Pick<Context, 'config'>, }: Pick<Context, 'config'>,
) => string ) => string
const chain: (...methods: UrlResolver[]) => UrlResolver = (...methods) => {
return (parent, args, context) => {
let { url } = parent
for (const method of methods) {
url = method({ url }, args, context)
}
return url
}
}
const pointUrlToImagor: (opts: { transformations: UrlResolver[] }) => UrlResolver = const pointUrlToImagor: (opts: { transformations: UrlResolver[] }) => UrlResolver =
({ transformations }) => ({ transformations }) =>
({ url }, _args, context) => { ({ url }, _args, context) => {
@ -59,22 +67,12 @@ const resize: UrlResolver = ({ url }, { height, width }) => {
if (!(height || width)) { if (!(height || width)) {
return url return url
} }
const window = `/fit-in/${width ?? FALLBACK_MAXIMUM_LENGTH}x${height ?? FALLBACK_MAXIMUM_LENGTH}` const window = `/fit-in/${String(width ?? FALLBACK_MAXIMUM_LENGTH)}x${String(height ?? FALLBACK_MAXIMUM_LENGTH)}`
const newUrl = new URL(url) const newUrl = new URL(url)
newUrl.pathname = window + newUrl.pathname newUrl.pathname = window + newUrl.pathname
return newUrl.href return newUrl.href
} }
const chain: (...methods: UrlResolver[]) => UrlResolver = (...methods) => {
return (parent, args, context) => {
let { url } = parent
for (const method of methods) {
url = method({ url }, args, context)
}
return url
}
}
export default { export default {
Image: { Image: {
...Resolver('Image', { ...Resolver('Image', {

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-shadow */
import path from 'node:path' import path from 'node:path'
@ -41,6 +40,17 @@ export const images = (config: S3Config) => {
return image return image
} }
const uploadImageFile = async (uploadPromise: Promise<FileUpload> | undefined) => {
if (!uploadPromise) return undefined
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const upload = await uploadPromise
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
const { name, ext } = path.parse(upload.filename)
const uniqueFilename = `${uuid()}-${slug(name)}${ext}`
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return await s3.uploadFile({ ...upload, uniqueFilename })
}
const mergeImage: Images['mergeImage'] = async ( const mergeImage: Images['mergeImage'] = async (
resource, resource,
relationshipType, relationshipType,
@ -84,17 +94,6 @@ export const images = (config: S3Config) => {
return mergedImage return mergedImage
} }
const uploadImageFile = async (uploadPromise: Promise<FileUpload> | undefined) => {
if (!uploadPromise) return undefined
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const upload = await uploadPromise
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
const { name, ext } = path.parse(upload.filename)
const uniqueFilename = `${uuid()}-${slug(name)}${ext}`
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return await s3.uploadFile({ ...upload, uniqueFilename })
}
const images = { const images = {
deleteImage, deleteImage,
mergeImage, mergeImage,

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
@ -16,7 +15,7 @@ import Resolver from './helpers/Resolver'
import type { File } from './attachments/attachments' import type { File } from './attachments/attachments'
const setMessagesAsDistributed = async (undistributedMessagesIds, session) => { const setMessagesAsDistributed = (undistributedMessagesIds, session) => {
return session.writeTransaction(async (transaction) => { return session.writeTransaction(async (transaction) => {
const setDistributedCypher = ` const setDistributedCypher = `
MATCH (m:Message) WHERE m.id IN $undistributedMessagesIds MATCH (m:Message) WHERE m.id IN $undistributedMessagesIds

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -45,8 +43,8 @@ export default {
default: default:
orderByClause = '' orderByClause = ''
} }
const offset = args.offset && typeof args.offset === 'number' ? `SKIP ${args.offset}` : '' const offset = args.offset && typeof args.offset === 'number' ? `SKIP ${String(args.offset)}` : ''
const limit = args.first && typeof args.first === 'number' ? `LIMIT ${args.first}` : '' const limit = args.first && typeof args.first === 'number' ? `LIMIT ${String(args.first)}` : ''
const readTxResultPromise = session.readTransaction(async (transaction) => { const readTxResultPromise = session.readTransaction(async (transaction) => {
const notificationsTransactionResponse = await transaction.run( const notificationsTransactionResponse = await transaction.run(
@ -145,9 +143,9 @@ export default {
}, },
}, },
NOTIFIED: { NOTIFIED: {
id: async (parent) => { id: (parent) => {
// serialize an ID to help the client update the cache // serialize an ID to help the client update the cache
return `${parent.reason}/${parent.from.id}/${parent.to.id}` return `${String(parent.reason)}/${String(parent.from.id)}/${String(parent.to.id)}`
}, },
}, },
} }

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -186,7 +185,7 @@ export default {
SET post.sortDate = toString(datetime()) SET post.sortDate = toString(datetime())
SET post.clickedCount = 0 SET post.clickedCount = 0
SET post.viewedTeaserCount = 0 SET post.viewedTeaserCount = 0
SET post:${params.postType} SET post:${String(params.postType)}
WITH post WITH post
MATCH (author:User {id: $userId}) MATCH (author:User {id: $userId})
MERGE (post)<-[:WROTE]-(author) MERGE (post)<-[:WROTE]-(author)
@ -260,7 +259,7 @@ export default {
updatePostCypher += ` updatePostCypher += `
REMOVE post:Article REMOVE post:Article
REMOVE post:Event REMOVE post:Event
SET post:${params.postType} SET post:${String(params.postType)}
WITH post WITH post
` `
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -80,8 +79,8 @@ export default {
const filterClause = filterClauses.join(' ') const filterClause = filterClauses.join(' ')
const offset = const offset =
params.offset && typeof params.offset === 'number' ? `SKIP ${params.offset}` : '' params.offset && typeof params.offset === 'number' ? `SKIP ${String(params.offset)}` : ''
const limit = params.first && typeof params.first === 'number' ? `LIMIT ${params.first}` : '' const limit = params.first && typeof params.first === 'number' ? `LIMIT ${String(params.first)}` : ''
const reportsReadTxPromise = session.readTransaction(async (transaction) => { const reportsReadTxPromise = session.readTransaction(async (transaction) => {
const reportsTransactionResponse = await transaction.run( const reportsTransactionResponse = await transaction.run(

View File

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/require-await */
export default { export default {
Query: { Query: {
availableRoles: async (_parent, _args, _context, _resolveInfo) => { availableRoles: (_parent, _args, _context, _resolveInfo) => {
return ['admin', 'moderator', 'user'] return ['admin', 'moderator', 'user']
}, },
}, },

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
@ -38,7 +37,7 @@ export default {
}, },
}, },
Query: { Query: {
Room: async (object, params, context, resolveInfo) => { Room: (object, params, context, resolveInfo) => {
if (!params.filter) params.filter = {} if (!params.filter) params.filter = {}
params.filter.users_some = { params.filter.users_some = {
id: context.user.id, id: context.user.id,

View File

@ -1,8 +1,6 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
import { queryString } from './searches/queryString' import { queryString } from './searches/queryString'
@ -10,16 +8,16 @@ import { queryString } from './searches/queryString'
// see http://lucene.apache.org/core/8_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description // see http://lucene.apache.org/core/8_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description
const cypherTemplate = (setup) => ` const cypherTemplate = (setup) => `
CALL db.index.fulltext.queryNodes('${setup.fulltextIndex}', $query) CALL db.index.fulltext.queryNodes('${String(setup.fulltextIndex)}', $query)
YIELD node AS resource, score YIELD node AS resource, score
${setup.match} ${String(setup.match)}
${setup.whereClause} ${String(setup.whereClause)}
${setup.withClause} ${String(setup.withClause)}
RETURN RETURN
${setup.returnClause} ${String(setup.returnClause)}
AS result AS result
SKIP toInteger($skip) SKIP toInteger($skip)
${setup.limit} ${String(setup.limit)}
` `
const simpleWhereClause = const simpleWhereClause =
@ -148,7 +146,7 @@ const multiSearchMap = [
export default { export default {
Query: { Query: {
searchPosts: async (_parent, args, context, _resolveInfo) => { searchPosts: (_parent, args, context, _resolveInfo) => {
const { query, postsOffset, firstPosts } = args const { query, postsOffset, firstPosts } = args
let userId = null let userId = null
if (context.user) userId = context.user.id if (context.user) userId = context.user.id
@ -171,7 +169,7 @@ export default {
}), }),
} }
}, },
searchUsers: async (_parent, args, context, _resolveInfo) => { searchUsers: (_parent, args, context, _resolveInfo) => {
const { query, usersOffset, firstUsers } = args const { query, usersOffset, firstUsers } = args
return { return {
userCount: getSearchResults( userCount: getSearchResults(
@ -190,7 +188,7 @@ export default {
}), }),
} }
}, },
searchHashtags: async (_parent, args, context, _resolveInfo) => { searchHashtags: (_parent, args, context, _resolveInfo) => {
const { query, hashtagsOffset, firstHashtags } = args const { query, hashtagsOffset, firstHashtags } = args
return { return {
hashtagCount: getSearchResults( hashtagCount: getSearchResults(
@ -209,7 +207,7 @@ export default {
}), }),
} }
}, },
searchGroups: async (_parent, args, context, _resolveInfo) => { searchGroups: (_parent, args, context, _resolveInfo) => {
const { query, groupsOffset, firstGroups } = args const { query, groupsOffset, firstGroups } = args
let userId = null let userId = null
if (context.user) userId = context.user.id if (context.user) userId = context.user.id

View File

@ -2,48 +2,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-use-before-define */
export function queryString(str) {
const normalizedString = normalizeWhitespace(str)
const escapedString = escapeSpecialCharacters(normalizedString)
return `
${matchWholeText(escapedString)}
${matchEachWordExactly(escapedString)}
${matchSomeWordsExactly(escapedString)}
${matchBeginningOfWords(escapedString)}
`
}
const matchWholeText = (str, boost = 8) => {
return `"${str}"^${boost}`
}
const matchEachWordExactly = (str, boost = 4) => {
if (!str.includes(' ')) return ''
const tmp = str
.split(' ')
.map((s, i) => (i === 0 ? `"${s}"` : `AND "${s}"`))
.join(' ')
return `(${tmp})^${boost}`
}
const matchSomeWordsExactly = (str, boost = 2) => {
if (!str.includes(' ')) return ''
return str
.split(' ')
.map((s) => `"${s}"^${boost}`)
.join(' ')
}
const matchBeginningOfWords = (str) => {
return str
.split(' ')
.filter((s) => s.length >= 2)
.map((s) => s + '*')
.join(' ')
}
export function normalizeWhitespace(str) { export function normalizeWhitespace(str) {
// delete the first character if it is !, @ or # // delete the first character if it is !, @ or #
@ -56,3 +15,43 @@ export function normalizeWhitespace(str) {
export function escapeSpecialCharacters(str) { export function escapeSpecialCharacters(str) {
return str.replace(/(["[\]&|\\{}+!()^~*?:/-])/g, '\\$1') return str.replace(/(["[\]&|\\{}+!()^~*?:/-])/g, '\\$1')
} }
const matchWholeText = (str, boost = 8) => {
return `"${String(str)}"^${String(boost)}`
}
const matchEachWordExactly = (str, boost = 4) => {
if (!str.includes(' ')) return ''
const tmp = str
.split(' ')
.map((s, i) => (i === 0 ? `"${String(s)}"` : `AND "${String(s)}"`))
.join(' ')
return `(${tmp})^${String(boost)}`
}
const matchSomeWordsExactly = (str, boost = 2) => {
if (!str.includes(' ')) return ''
return str
.split(' ')
.map((s) => `"${String(s)}"^${String(boost)}`)
.join(' ')
}
const matchBeginningOfWords = (str) => {
return str
.split(' ')
.filter((s) => s.length >= 2)
.map((s) => s + '*')
.join(' ')
}
export function queryString(str) {
const normalizedString = normalizeWhitespace(str)
const escapedString = escapeSpecialCharacters(normalizedString)
return `
${matchWholeText(escapedString)}
${matchEachWordExactly(escapedString)}
${matchSomeWordsExactly(escapedString)}
${matchBeginningOfWords(escapedString)}
`
}

View File

@ -2,7 +2,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-use-before-define */
const byCreationDate = (a, b) => {
if (a.createdAt < b.createdAt) return -1
if (a.createdAt > b.createdAt) return 1
return 0
}
export default { export default {
Query: { Query: {
userData: async (_object, _args, context, _resolveInfo) => { userData: async (_object, _args, context, _resolveInfo) => {
@ -53,9 +59,3 @@ export default {
}, },
}, },
} }
const byCreationDate = (a, b) => {
if (a.createdAt < b.createdAt) return -1
if (a.createdAt > b.createdAt) return 1
return 0
}

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -215,11 +214,11 @@ export default {
await Promise.all( await Promise.all(
resource.map(async (node) => { resource.map(async (node) => {
if (!allowedLabels.includes(node)) { if (!allowedLabels.includes(node)) {
throw new UserInputError(`Invalid resource type: ${node}`) throw new UserInputError(`Invalid resource type: ${String(node)}`)
} }
const txResult = await transaction.run( const txResult = await transaction.run(
` `
MATCH (resource:${node})<-[:WROTE]-(author:User {id: $userId}) MATCH (resource:${String(node)})<-[:WROTE]-(author:User {id: $userId})
OPTIONAL MATCH (resource)<-[:COMMENTS]-(comment:Comment) OPTIONAL MATCH (resource)<-[:COMMENTS]-(comment:Comment)
SET resource.deleted = true SET resource.deleted = true
SET resource.content = 'UNAVAILABLE' SET resource.content = 'UNAVAILABLE'
@ -371,7 +370,7 @@ export default {
if (slot >= TROPHY_BADGES_SELECTED_MAX || slot < 0) { if (slot >= TROPHY_BADGES_SELECTED_MAX || slot < 0) {
throw new Error( throw new Error(
`Invalid slot! There is only ${TROPHY_BADGES_SELECTED_MAX} badge-slots to fill`, `Invalid slot! There is only ${String(TROPHY_BADGES_SELECTED_MAX)} badge-slots to fill`,
) )
} }

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/restrict-plus-operands */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
@ -19,7 +18,7 @@ const REQUEST_TIMEOUT = 3000
const createLocation = async (session, mapboxData) => { const createLocation = async (session, mapboxData) => {
const data = { const data = {
id: mapboxData.id + (mapboxData.address ? `-${mapboxData.address}` : ''), id: mapboxData.id + (mapboxData.address ? `-${String(mapboxData.address)}` : ''),
nameEN: mapboxData.text_en, nameEN: mapboxData.text_en,
nameDE: mapboxData.text_de, nameDE: mapboxData.text_de,
nameFR: mapboxData.text_fr, nameFR: mapboxData.text_fr,
@ -77,9 +76,9 @@ export const createOrUpdateLocations = async (
if (locationName !== null) { if (locationName !== null) {
const response: any = await fetch( const response: any = await fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent( `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
locationName, String(locationName),
)}.json?access_token=${ )}.json?access_token=${
context.config.MAPBOX_TOKEN String(context.config.MAPBOX_TOKEN)
}&types=region,place,country,address&language=${locales.join(',')}`, }&types=region,place,country,address&language=${locales.join(',')}`,
{ {
signal: AbortSignal.timeout(REQUEST_TIMEOUT), signal: AbortSignal.timeout(REQUEST_TIMEOUT),
@ -115,7 +114,7 @@ export const createOrUpdateLocations = async (
let parent = data let parent = data
if (parent.address) { if (parent.address) {
parent.id += `-${parent.address}` parent.id += `-${String(parent.address)}`
} }
if (data.context) { if (data.context) {
@ -147,7 +146,7 @@ export const createOrUpdateLocations = async (
await session.writeTransaction((transaction) => { await session.writeTransaction((transaction) => {
return transaction.run( return transaction.run(
` `
MATCH (node:${nodeLabel} {id: $nodeId}) MATCH (node:${String(nodeLabel)} {id: $nodeId})
OPTIONAL MATCH (node)-[relationship:IS_IN]->(:Location) OPTIONAL MATCH (node)-[relationship:IS_IN]->(:Location)
DELETE relationship DELETE relationship
WITH node WITH node
@ -162,7 +161,7 @@ export const createOrUpdateLocations = async (
export const queryLocations = async ({ place, lang }, context: Context) => { export const queryLocations = async ({ place, lang }, context: Context) => {
const res: any = await fetch( const res: any = await fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${context.config.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`, `https://api.mapbox.com/geocoding/v5/mapbox.places/${String(place)}.json?access_token=${String(context.config.MAPBOX_TOKEN)}&types=region,place,country&language=${String(lang)}`,
{ {
signal: AbortSignal.timeout(REQUEST_TIMEOUT), signal: AbortSignal.timeout(REQUEST_TIMEOUT),
}, },

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
@ -9,33 +8,33 @@ import { DESCRIPTION_EXCERPT_HTML_LENGTH } from '@constants/groups'
import type { IMiddlewareResolver } from 'graphql-middleware/dist/types' import type { IMiddlewareResolver } from 'graphql-middleware/dist/types'
const createGroup: IMiddlewareResolver = async (resolve, root, args, context, info) => { const createGroup: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const updateGroup: IMiddlewareResolver = async (resolve, root, args, context, info) => { const updateGroup: IMiddlewareResolver = (resolve, root, args, context, info) => {
if (args.description) if (args.description)
args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const createPost: IMiddlewareResolver = async (resolve, root, args, context, info) => { const createPost: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html args.contentExcerpt = trunc(args.content, 120).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const updatePost: IMiddlewareResolver = async (resolve, root, args, context, info) => { const updatePost: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html args.contentExcerpt = trunc(args.content, 120).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const createComment: IMiddlewareResolver = async (resolve, root, args, context, info) => { const createComment: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 180).html args.contentExcerpt = trunc(args.content, 180).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const updateComment: IMiddlewareResolver = async (resolve, root, args, context, info) => { const updateComment: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 180).html args.contentExcerpt = trunc(args.content, 180).html
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }

View File

@ -4,7 +4,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable security/detect-object-injection */ /* eslint-disable security/detect-object-injection */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/restrict-plus-operands */
import { import {
@ -43,136 +42,6 @@ const publishNotifications = async (
return emailsSent return emailsSent
} }
const handleJoinGroup: IMiddlewareResolver = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context),
'emailNotificationsGroupMemberJoined',
)
}
return user
}
const handleLeaveGroup: IMiddlewareResolver = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyOwnersOfGroup(groupId, userId, 'user_left_group', context),
'emailNotificationsGroupMemberLeft',
)
}
return user
}
const handleChangeGroupMemberRole: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context),
'emailNotificationsGroupMemberRoleChanged',
)
}
return user
}
const handleRemoveUserFromGroup: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context),
'emailNotificationsGroupMemberRemoved',
)
}
return user
}
const handleContentDataOfPost: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId } = args
const idsOfUsers = extractMentionedUsers(args.content)
const post = await resolve(root, args, context, resolveInfo)
if (post) {
const sentEmails: string[] = await publishNotifications(
context,
notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context),
'emailNotificationsMention',
)
sentEmails.concat(
await publishNotifications(
context,
notifyFollowingUsers(post.id, groupId, context),
'emailNotificationsFollowingUsers',
sentEmails,
),
)
await publishNotifications(
context,
notifyGroupMembersOfNewPost(post.id, groupId, context),
'emailNotificationsPostInGroup',
sentEmails,
)
}
return post
}
const handleContentDataOfComment: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { content } = args
let idsOfMentionedUsers = extractMentionedUsers(content)
const comment = await resolve(root, args, context, resolveInfo)
const [postAuthor] = await postAuthorOfComment(comment.id, { context })
idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id)
const sentEmails: string[] = await publishNotifications(
context,
notifyUsersOfMention(
'Comment',
comment.id,
idsOfMentionedUsers,
'mentioned_in_comment',
context,
),
'emailNotificationsMention',
)
await publishNotifications(
context,
notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context),
'emailNotificationsCommentOnObservedPost',
sentEmails,
)
return comment
}
const postAuthorOfComment = async (commentId, { context }) => { const postAuthorOfComment = async (commentId, { context }) => {
const session = context.driver.session() const session = context.driver.session()
let postAuthorId let postAuthorId
@ -448,6 +317,136 @@ const notifyUsersOfComment = async (label, commentId, reason, context) => {
} }
} }
const handleJoinGroup: IMiddlewareResolver = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context),
'emailNotificationsGroupMemberJoined',
)
}
return user
}
const handleLeaveGroup: IMiddlewareResolver = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyOwnersOfGroup(groupId, userId, 'user_left_group', context),
'emailNotificationsGroupMemberLeft',
)
}
return user
}
const handleChangeGroupMemberRole: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context),
'emailNotificationsGroupMemberRoleChanged',
)
}
return user
}
const handleRemoveUserFromGroup: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(
context,
notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context),
'emailNotificationsGroupMemberRemoved',
)
}
return user
}
const handleContentDataOfPost: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { groupId } = args
const idsOfUsers = extractMentionedUsers(args.content)
const post = await resolve(root, args, context, resolveInfo)
if (post) {
const sentEmails: string[] = await publishNotifications(
context,
notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context),
'emailNotificationsMention',
)
sentEmails.concat(
await publishNotifications(
context,
notifyFollowingUsers(post.id, groupId, context),
'emailNotificationsFollowingUsers',
sentEmails,
),
)
await publishNotifications(
context,
notifyGroupMembersOfNewPost(post.id, groupId, context),
'emailNotificationsPostInGroup',
sentEmails,
)
}
return post
}
const handleContentDataOfComment: IMiddlewareResolver = async (
resolve,
root,
args,
context,
resolveInfo,
) => {
const { content } = args
let idsOfMentionedUsers = extractMentionedUsers(content)
const comment = await resolve(root, args, context, resolveInfo)
const [postAuthor] = await postAuthorOfComment(comment.id, { context })
idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id)
const sentEmails: string[] = await publishNotifications(
context,
notifyUsersOfMention(
'Comment',
comment.id,
idsOfMentionedUsers,
'mentioned_in_comment',
context,
),
'emailNotificationsMention',
)
await publishNotifications(
context,
notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context),
'emailNotificationsCommentOnObservedPost',
sentEmails,
)
return comment
}
const handleCreateMessage: IMiddlewareResolver = async ( const handleCreateMessage: IMiddlewareResolver = async (
resolve, resolve,
root, root,

View File

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unnecessary-type-conversion */ /* eslint-disable @typescript-eslint/no-unnecessary-type-conversion */
@ -22,27 +21,27 @@ const neode = getNeode()
const isAuthenticated = rule({ const isAuthenticated = rule({
cache: 'contextual', cache: 'contextual',
})(async (_parent, _args, ctx, _info) => { })((_parent, _args, ctx, _info) => {
return !!ctx?.user?.id return !!ctx?.user?.id
}) })
const isModerator = rule()(async (_parent, _args, { user }: Context, _info) => { const isModerator = rule()((_parent, _args, { user }: Context, _info) => {
return !!(user && (user.role === 'moderator' || user.role === 'admin')) return !!(user && (user.role === 'moderator' || user.role === 'admin'))
}) })
const isAdmin = rule()(async (_parent, _args, { user }: Context, _info) => { const isAdmin = rule()((_parent, _args, { user }: Context, _info) => {
return !!(user?.role === 'admin') return !!(user?.role === 'admin')
}) })
const onlyYourself = rule({ const onlyYourself = rule({
cache: 'no_cache', cache: 'no_cache',
})(async (_parent, args, context: Context, _info) => { })((_parent, args, context: Context, _info) => {
return context.user?.id === args.id return context.user?.id === args.id
}) })
const isMyOwn = rule({ const isMyOwn = rule({
cache: 'no_cache', cache: 'no_cache',
})(async (parent, _args, { user }: Context, _info) => { })((parent, _args, { user }: Context, _info) => {
return !!(user && user.id === parent.id) return !!(user && user.id === parent.id)
}) })
@ -362,21 +361,21 @@ const isAuthor = rule({
const isDeletingOwnAccount = rule({ const isDeletingOwnAccount = rule({
cache: 'no_cache', cache: 'no_cache',
})(async (_parent, args, context: Context, _info) => { })((_parent, args, context: Context, _info) => {
return context.user?.id === args.id return context.user?.id === args.id
}) })
const noEmailFilter = rule({ const noEmailFilter = rule({
cache: 'no_cache', cache: 'no_cache',
})(async (_, args) => { })((_, args) => {
return !('email' in args) return !('email' in args)
}) })
const publicRegistration = rule()( const publicRegistration = rule()(
async (_parent, _args, context: Context) => context.config.PUBLIC_REGISTRATION, (_parent, _args, context: Context) => context.config.PUBLIC_REGISTRATION,
) )
const inviteRegistration = rule()(async (_parent, args, context: Context) => { const inviteRegistration = rule()((_parent, args, context: Context) => {
if (!context.config.INVITE_REGISTRATION) return false if (!context.config.INVITE_REGISTRATION) return false
const { inviteCode } = args const { inviteCode } = args
return validateInviteCode(context, inviteCode) return validateInviteCode(context, inviteCode)

View File

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import slugify from 'slugify' import slugify from 'slugify'
slugify.extend({ Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue', ß: 'ss' }) slugify.extend({ Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue', ß: 'ss' })
@ -16,7 +15,7 @@ export default async function uniqueSlug(str: string, isUnique: IsUnique) {
let uniqueSlug: string let uniqueSlug: string
do { do {
count += 1 count += 1
uniqueSlug = `${slug}-${count}` uniqueSlug = `${slug}-${String(count)}`
} while (!(await isUnique(uniqueSlug))) } while (!(await isUnique(uniqueSlug)))
return uniqueSlug return uniqueSlug
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
@ -8,7 +7,7 @@ const isModerator = ({ user }) => {
return user && (user.role === 'moderator' || user.role === 'admin') return user && (user.role === 'moderator' || user.role === 'admin')
} }
const setDefaultFilters: IMiddlewareResolver = async (resolve, root, args, context, info) => { const setDefaultFilters: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.deleted = false args.deleted = false
if (!isModerator(context)) { if (!isModerator(context)) {
@ -17,7 +16,7 @@ const setDefaultFilters: IMiddlewareResolver = async (resolve, root, args, conte
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const obfuscate: IMiddlewareResolver = async (resolve, root, args, context, info) => { const obfuscate: IMiddlewareResolver = (resolve, root, args, context, info) => {
if (root.deleted || (!isModerator(context) && root.disabled)) { if (root.deleted || (!isModerator(context) && root.disabled)) {
root.content = 'UNAVAILABLE' root.content = 'UNAVAILABLE'
root.contentExcerpt = 'UNAVAILABLE' root.contentExcerpt = 'UNAVAILABLE'
@ -31,7 +30,7 @@ const obfuscate: IMiddlewareResolver = async (resolve, root, args, context, info
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const mutationDefaults: IMiddlewareResolver = async (resolve, root, args, context, info) => { const mutationDefaults: IMiddlewareResolver = (resolve, root, args, context, info) => {
args.disabled = false args.disabled = false
// TODO: remove as soon as our factories don't need this anymore // TODO: remove as soon as our factories don't need this anymore
if (typeof args.deleted !== 'boolean') { if (typeof args.deleted !== 'boolean') {

View File

@ -2,16 +2,15 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import type { IMiddlewareResolver } from 'graphql-middleware/dist/types' import type { IMiddlewareResolver } from 'graphql-middleware/dist/types'
const createRelatedCypher = (relation) => ` const createRelatedCypher = (relation) => `
MATCH (user:User { id: $currentUser}) MATCH (user:User { id: $currentUser})
MATCH (post:Post { id: $postId}) MATCH (post:Post { id: $postId})
OPTIONAL MATCH (post)<-[r:${relation}]-(u:User) OPTIONAL MATCH (post)<-[r:${String(relation)}]-(u:User)
WHERE NOT u.disabled AND NOT u.deleted WHERE NOT u.disabled AND NOT u.deleted
WITH user, post, count(DISTINCT u) AS count WITH user, post, count(DISTINCT u) AS count
MERGE (user)-[relation:${relation} { }]->(post) MERGE (user)-[relation:${String(relation)} { }]->(post)
ON CREATE ON CREATE
SET relation.count = 1, SET relation.count = 1,
relation.createdAt = toString(datetime()), relation.createdAt = toString(datetime()),

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -16,7 +14,7 @@ const validateCreateComment: IMiddlewareResolver = async (resolve, root, args, c
const { postId } = args const { postId } = args
if (!args.content || content.length < COMMENT_MIN_LENGTH) { if (!args.content || content.length < COMMENT_MIN_LENGTH) {
throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`) throw new UserInputError(`Comment must be at least ${String(COMMENT_MIN_LENGTH)} character long!`)
} }
const session = context.driver.session() const session = context.driver.session()
try { try {
@ -43,16 +41,16 @@ const validateCreateComment: IMiddlewareResolver = async (resolve, root, args, c
} }
} }
const validateUpdateComment: IMiddlewareResolver = async (resolve, root, args, context, info) => { const validateUpdateComment: IMiddlewareResolver = (resolve, root, args, context, info) => {
const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim() const content = args.content.replace(/<(?:.|\n)*?>/gm, '').trim()
if (!args.content || content.length < COMMENT_MIN_LENGTH) { if (!args.content || content.length < COMMENT_MIN_LENGTH) {
throw new UserInputError(`Comment must be at least ${COMMENT_MIN_LENGTH} character long!`) throw new UserInputError(`Comment must be at least ${String(COMMENT_MIN_LENGTH)} character long!`)
} }
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
const validateReport: IMiddlewareResolver = async (resolve, root, args, context, info) => { const validateReport: IMiddlewareResolver = (resolve, root, args, context, info) => {
const { resourceId } = args const { resourceId } = args
const { user } = context const { user } = context
if (resourceId === user.id) throw new Error('You cannot report yourself!') if (resourceId === user.id) throw new Error('You cannot report yourself!')
@ -92,14 +90,14 @@ const validateReview: IMiddlewareResolver = async (resolve, root, args, context,
existingReportedResource = existingReportedResource[0] existingReportedResource = existingReportedResource[0]
if (!existingReportedResource.filed) if (!existingReportedResource.filed)
throw new Error( throw new Error(
`Before starting the review process, please report the ${existingReportedResource.label}!`, `Before starting the review process, please report the ${String(existingReportedResource.label)}!`,
) )
const authorId = const authorId =
existingReportedResource.label !== 'User' && existingReportedResource.author existingReportedResource.label !== 'User' && existingReportedResource.author
? existingReportedResource.author.properties.id ? existingReportedResource.author.properties.id
: null : null
if (authorId && authorId === user.id) if (authorId && authorId === user.id)
throw new Error(`You cannot review your own ${existingReportedResource.label}!`) throw new Error(`You cannot review your own ${String(existingReportedResource.label)}!`)
} finally { } finally {
await session.close() await session.close()
} }
@ -107,7 +105,7 @@ const validateReview: IMiddlewareResolver = async (resolve, root, args, context,
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }
export const validateNotifyUsers = async (label: string, reason: string): Promise<void> => { export const validateNotifyUsers = (label: string, reason: string): void => {
const reasonsAllowed = [ const reasonsAllowed = [
'mentioned_in_post', 'mentioned_in_post',
'mentioned_in_comment', 'mentioned_in_comment',
@ -124,10 +122,10 @@ export const validateNotifyUsers = async (label: string, reason: string): Promis
} }
} }
const validateUpdateUser: IMiddlewareResolver = async (resolve, root, params, context, info) => { const validateUpdateUser: IMiddlewareResolver = (resolve, root, params, context, info) => {
const { name } = params const { name } = params
if (typeof name === 'string' && name.trim().length < USERNAME_MIN_LENGTH) if (typeof name === 'string' && name.trim().length < USERNAME_MIN_LENGTH)
throw new UserInputError(`Username must be at least ${USERNAME_MIN_LENGTH} character long!`) throw new UserInputError(`Username must be at least ${String(USERNAME_MIN_LENGTH)} character long!`)
return resolve(root, params, context, info) return resolve(root, params, context, info)
} }

View File

@ -1,7 +1,6 @@
/* eslint-disable security/detect-object-injection */ /* eslint-disable security/detect-object-injection */
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
@ -47,7 +46,7 @@ const fields = [
{ field: 'descriptionExcerpt' }, { field: 'descriptionExcerpt' },
] ]
const mutationXss: IMiddlewareResolver = async (resolve, root, args, context, info) => { const mutationXss: IMiddlewareResolver = (resolve, root, args, context, info) => {
args = walkRecursive(args, fields, info.fieldName, cleanHtml) args = walkRecursive(args, fields, info.fieldName, cleanHtml)
return resolve(root, args, context, info) return resolve(root, args, context, info)
} }

View File

@ -102,8 +102,7 @@ const createServer = async (options?: CreateServerOptions) => {
plugins: [ plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }), ApolloServerPluginDrainHttpServer({ httpServer }),
{ {
// eslint-disable-next-line @typescript-eslint/require-await serverWillStart() {
async serverWillStart() {
return { return {
async drainServer() { async drainServer() {
await serverCleanup.dispose() await serverCleanup.dispose()