Dependency inject `me` into post validation schemas to enforce no forwarding posts to self (#485)
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
This commit is contained in:
parent
16a6f93708
commit
1a6dc879a2
|
@ -621,7 +621,7 @@ export default {
|
||||||
return await models.item.update({ where: { id: Number(id) }, data })
|
return await models.item.update({ where: { id: Number(id) }, data })
|
||||||
},
|
},
|
||||||
upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
upsertLink: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
||||||
await ssValidate(linkSchema, item, models)
|
await ssValidate(linkSchema, item, models, me)
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return await updateItem(parent, { id, ...item }, { me, models })
|
return await updateItem(parent, { id, ...item }, { me, models })
|
||||||
|
@ -630,7 +630,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
upsertDiscussion: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
upsertDiscussion: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
||||||
await ssValidate(discussionSchema, item, models)
|
await ssValidate(discussionSchema, item, models, me)
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return await updateItem(parent, { id, ...item }, { me, models })
|
return await updateItem(parent, { id, ...item }, { me, models })
|
||||||
|
@ -639,7 +639,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
upsertBounty: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
upsertBounty: async (parent, { id, hash, hmac, ...item }, { me, models, lnd }) => {
|
||||||
await ssValidate(bountySchema, item, models)
|
await ssValidate(bountySchema, item, models, me)
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return await updateItem(parent, { id, ...item }, { me, models })
|
return await updateItem(parent, { id, ...item }, { me, models })
|
||||||
|
@ -656,7 +656,7 @@ export default {
|
||||||
})
|
})
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
await ssValidate(pollSchema, item, models, optionCount)
|
await ssValidate(pollSchema, item, models, me, optionCount)
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return await updateItem(parent, { id, ...item }, { me, models })
|
return await updateItem(parent, { id, ...item }, { me, models })
|
||||||
|
|
|
@ -11,6 +11,7 @@ import CancelButton from './cancel-button'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { normalizeForwards } from '../lib/form'
|
import { normalizeForwards } from '../lib/form'
|
||||||
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
||||||
|
import { useMe } from './me'
|
||||||
|
|
||||||
export function BountyForm ({
|
export function BountyForm ({
|
||||||
item,
|
item,
|
||||||
|
@ -25,7 +26,8 @@ export function BountyForm ({
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const client = useApolloClient()
|
const client = useApolloClient()
|
||||||
const schema = bountySchema(client)
|
const me = useMe()
|
||||||
|
const schema = bountySchema(client, me)
|
||||||
const [upsertBounty] = useMutation(
|
const [upsertBounty] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
mutation upsertBounty(
|
mutation upsertBounty(
|
||||||
|
|
|
@ -15,6 +15,7 @@ import CancelButton from './cancel-button'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { normalizeForwards } from '../lib/form'
|
import { normalizeForwards } from '../lib/form'
|
||||||
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
||||||
|
import { useMe } from './me'
|
||||||
|
|
||||||
export function DiscussionForm ({
|
export function DiscussionForm ({
|
||||||
item, sub, editThreshold, titleLabel = 'title',
|
item, sub, editThreshold, titleLabel = 'title',
|
||||||
|
@ -23,11 +24,11 @@ export function DiscussionForm ({
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const client = useApolloClient()
|
const client = useApolloClient()
|
||||||
const schema = discussionSchema(client)
|
const me = useMe()
|
||||||
|
const schema = discussionSchema(client, me)
|
||||||
// if Web Share Target API was used
|
// if Web Share Target API was used
|
||||||
const shareTitle = router.query.title
|
const shareTitle = router.query.title
|
||||||
|
|
||||||
// const me = useMe()
|
|
||||||
const [upsertDiscussion] = useMutation(
|
const [upsertDiscussion] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
mutation upsertDiscussion($sub: String, $id: ID, $title: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) {
|
mutation upsertDiscussion($sub: String, $id: ID, $title: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) {
|
||||||
|
|
|
@ -16,11 +16,13 @@ import { SubSelectInitial } from './sub-select-form'
|
||||||
import CancelButton from './cancel-button'
|
import CancelButton from './cancel-button'
|
||||||
import { normalizeForwards } from '../lib/form'
|
import { normalizeForwards } from '../lib/form'
|
||||||
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
import { MAX_TITLE_LENGTH } from '../lib/constants'
|
||||||
|
import { useMe } from './me'
|
||||||
|
|
||||||
export function LinkForm ({ item, sub, editThreshold, children }) {
|
export function LinkForm ({ item, sub, editThreshold, children }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const client = useApolloClient()
|
const client = useApolloClient()
|
||||||
const schema = linkSchema(client)
|
const me = useMe()
|
||||||
|
const schema = linkSchema(client, me)
|
||||||
// if Web Share Target API was used
|
// if Web Share Target API was used
|
||||||
const shareUrl = router.query.url
|
const shareUrl = router.query.url
|
||||||
const shareTitle = router.query.title
|
const shareTitle = router.query.title
|
||||||
|
|
|
@ -12,11 +12,13 @@ import { SubSelectInitial } from './sub-select-form'
|
||||||
import CancelButton from './cancel-button'
|
import CancelButton from './cancel-button'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { normalizeForwards } from '../lib/form'
|
import { normalizeForwards } from '../lib/form'
|
||||||
|
import { useMe } from './me'
|
||||||
|
|
||||||
export function PollForm ({ item, sub, editThreshold, children }) {
|
export function PollForm ({ item, sub, editThreshold, children }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const client = useApolloClient()
|
const client = useApolloClient()
|
||||||
const schema = pollSchema(client)
|
const me = useMe()
|
||||||
|
const schema = pollSchema(client, me)
|
||||||
|
|
||||||
const [upsertPoll] = useMutation(
|
const [upsertPoll] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
|
|
|
@ -58,7 +58,7 @@ async function usernameExists (client, name) {
|
||||||
return !!user
|
return !!user
|
||||||
}
|
}
|
||||||
|
|
||||||
export function advPostSchemaMembers (client) {
|
export function advPostSchemaMembers (client, me) {
|
||||||
return {
|
return {
|
||||||
boost: intValidator
|
boost: intValidator
|
||||||
.min(BOOST_MIN, `must be blank or at least ${BOOST_MIN}`).test({
|
.min(BOOST_MIN, `must be blank or at least ${BOOST_MIN}`).test({
|
||||||
|
@ -66,17 +66,24 @@ export function advPostSchemaMembers (client) {
|
||||||
test: async boost => !boost || boost % BOOST_MIN === 0,
|
test: async boost => !boost || boost % BOOST_MIN === 0,
|
||||||
message: `must be divisble be ${BOOST_MIN}`
|
message: `must be divisble be ${BOOST_MIN}`
|
||||||
}),
|
}),
|
||||||
// XXX this lets you forward to youself (it's financially equivalent but it should be disallowed)
|
|
||||||
forward: array()
|
forward: array()
|
||||||
.max(MAX_FORWARDS, `you can only configure ${MAX_FORWARDS} forward recipients`)
|
.max(MAX_FORWARDS, `you can only configure ${MAX_FORWARDS} forward recipients`)
|
||||||
.of(object().shape({
|
.of(object().shape({
|
||||||
nym: string().required('must specify a stacker').test({
|
nym: string().required('must specify a stacker')
|
||||||
|
.test({
|
||||||
name: 'nym',
|
name: 'nym',
|
||||||
test: async name => {
|
test: async name => {
|
||||||
if (!name || !name.length) return true
|
if (!name || !name.length) return false
|
||||||
return await usernameExists(client, name)
|
return await usernameExists(client, name)
|
||||||
},
|
},
|
||||||
message: 'stacker does not exist'
|
message: 'stacker does not exist'
|
||||||
|
})
|
||||||
|
.test({
|
||||||
|
name: 'self',
|
||||||
|
test: async name => {
|
||||||
|
return me?.name !== name
|
||||||
|
},
|
||||||
|
message: 'cannot forward to yourself'
|
||||||
}),
|
}),
|
||||||
pct: intValidator.required('must specify a percentage').min(1, 'percentage must be at least 1').max(100, 'percentage must not exceed 100')
|
pct: intValidator.required('must specify a percentage').min(1, 'percentage must be at least 1').max(100, 'percentage must not exceed 100')
|
||||||
}))
|
}))
|
||||||
|
@ -100,35 +107,35 @@ export function subSelectSchemaMembers (client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bountySchema (client) {
|
export function bountySchema (client, me) {
|
||||||
return object({
|
return object({
|
||||||
title: titleValidator,
|
title: titleValidator,
|
||||||
bounty: intValidator
|
bounty: intValidator
|
||||||
.min(1000, 'must be at least 1000')
|
.min(1000, 'must be at least 1000')
|
||||||
.max(1000000, 'must be at most 1m'),
|
.max(1000000, 'must be at most 1m'),
|
||||||
...advPostSchemaMembers(client),
|
...advPostSchemaMembers(client, me),
|
||||||
...subSelectSchemaMembers()
|
...subSelectSchemaMembers()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function discussionSchema (client) {
|
export function discussionSchema (client, me) {
|
||||||
return object({
|
return object({
|
||||||
title: titleValidator,
|
title: titleValidator,
|
||||||
...advPostSchemaMembers(client),
|
...advPostSchemaMembers(client, me),
|
||||||
...subSelectSchemaMembers()
|
...subSelectSchemaMembers()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function linkSchema (client) {
|
export function linkSchema (client, me) {
|
||||||
return object({
|
return object({
|
||||||
title: titleValidator,
|
title: titleValidator,
|
||||||
url: string().matches(URL_REGEXP, 'invalid url').required('required'),
|
url: string().matches(URL_REGEXP, 'invalid url').required('required'),
|
||||||
...advPostSchemaMembers(client),
|
...advPostSchemaMembers(client, me),
|
||||||
...subSelectSchemaMembers()
|
...subSelectSchemaMembers()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pollSchema (client, numExistingChoices = 0) {
|
export function pollSchema (client, me, numExistingChoices = 0) {
|
||||||
return object({
|
return object({
|
||||||
title: titleValidator,
|
title: titleValidator,
|
||||||
options: array().of(
|
options: array().of(
|
||||||
|
@ -144,7 +151,7 @@ export function pollSchema (client, numExistingChoices = 0) {
|
||||||
message: `at least ${MIN_POLL_NUM_CHOICES} choices required`,
|
message: `at least ${MIN_POLL_NUM_CHOICES} choices required`,
|
||||||
test: arr => arr.length >= MIN_POLL_NUM_CHOICES - numExistingChoices
|
test: arr => arr.length >= MIN_POLL_NUM_CHOICES - numExistingChoices
|
||||||
}),
|
}),
|
||||||
...advPostSchemaMembers(client),
|
...advPostSchemaMembers(client, me),
|
||||||
...subSelectSchemaMembers()
|
...subSelectSchemaMembers()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue