Throw errors which extend GraphQLError (#1386)
This commit is contained in:
parent
ec5241ad29
commit
821ac60de5
|
@ -1,7 +1,7 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GqlAuthorizationError } from '@/lib/error'
|
||||||
|
|
||||||
export default function assertApiKeyNotPermitted ({ me }) {
|
export default function assertApiKeyNotPermitted ({ me }) {
|
||||||
if (me?.apiKey === true) {
|
if (me?.apiKey === true) {
|
||||||
throw new GraphQLError('this operation is not allowed to be performed via API Key', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthorizationError('this operation is not allowed to be performed via API Key')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { inviteSchema, ssValidate } from '@/lib/validate'
|
import { inviteSchema, ssValidate } from '@/lib/validate'
|
||||||
import { msatsToSats } from '@/lib/format'
|
import { msatsToSats } from '@/lib/format'
|
||||||
import assertApiKeyNotPermitted from './apiKey'
|
import assertApiKeyNotPermitted from './apiKey'
|
||||||
|
import { GqlAuthenticationError } from '@/lib/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
invites: async (parent, args, { me, models }) => {
|
invites: async (parent, args, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.invite.findMany({
|
return await models.invite.findMany({
|
||||||
|
@ -31,7 +31,7 @@ export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createInvite: async (parent, { gift, limit }, { me, models }) => {
|
createInvite: async (parent, { gift, limit }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export default {
|
||||||
},
|
},
|
||||||
revokeInvite: async (parent, { id }, { me, models }) => {
|
revokeInvite: async (parent, { id }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.invite.update({
|
return await models.invite.update({
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { ensureProtocol, removeTracking, stripTrailingSlash } from '@/lib/url'
|
import { ensureProtocol, removeTracking, stripTrailingSlash } from '@/lib/url'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
||||||
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
||||||
|
@ -20,6 +19,7 @@ import { uploadIdsFromText } from './image'
|
||||||
import assertGofacYourself from './ofac'
|
import assertGofacYourself from './ofac'
|
||||||
import assertApiKeyNotPermitted from './apiKey'
|
import assertApiKeyNotPermitted from './apiKey'
|
||||||
import performPaidAction from '../paidAction'
|
import performPaidAction from '../paidAction'
|
||||||
|
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
function commentsOrderByClause (me, models, sort) {
|
function commentsOrderByClause (me, models, sort) {
|
||||||
if (sort === 'recent') {
|
if (sort === 'recent') {
|
||||||
|
@ -333,12 +333,12 @@ export default {
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
case 'user':
|
case 'user':
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new GraphQLError('must supply name', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('must supply name')
|
||||||
}
|
}
|
||||||
|
|
||||||
user ??= await models.user.findUnique({ where: { name } })
|
user ??= await models.user.findUnique({ where: { name } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('no user has that name')
|
||||||
}
|
}
|
||||||
|
|
||||||
table = type === 'bookmarks' ? 'Bookmark' : 'Item'
|
table = type === 'bookmarks' ? 'Bookmark' : 'Item'
|
||||||
|
@ -657,7 +657,7 @@ export default {
|
||||||
},
|
},
|
||||||
pinItem: async (parent, { id }, { me, models }) => {
|
pinItem: async (parent, { id }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const [item] = await models.$queryRawUnsafe(
|
const [item] = await models.$queryRawUnsafe(
|
||||||
|
@ -671,7 +671,7 @@ export default {
|
||||||
|
|
||||||
// OPs can only pin top level replies
|
// OPs can only pin top level replies
|
||||||
if (item.path.split('.').length > 2) {
|
if (item.path.split('.').length > 2) {
|
||||||
throw new GraphQLError('can only pin root replies', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('can only pin root replies')
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await models.item.findUnique({
|
const root = await models.item.findUnique({
|
||||||
|
@ -682,7 +682,7 @@ export default {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (root.userId !== Number(me.id)) {
|
if (root.userId !== Number(me.id)) {
|
||||||
throw new GraphQLError('not your post', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('not your post')
|
||||||
}
|
}
|
||||||
} else if (item.subName) {
|
} else if (item.subName) {
|
||||||
args.push(item.subName)
|
args.push(item.subName)
|
||||||
|
@ -690,10 +690,10 @@ export default {
|
||||||
// only territory founder can pin posts
|
// only territory founder can pin posts
|
||||||
const sub = await models.sub.findUnique({ where: { name: item.subName } })
|
const sub = await models.sub.findUnique({ where: { name: item.subName } })
|
||||||
if (Number(me.id) !== sub.userId) {
|
if (Number(me.id) !== sub.userId) {
|
||||||
throw new GraphQLError('not your sub', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('not your sub')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new GraphQLError('item must have subName or parentId', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('item must have subName or parentId')
|
||||||
}
|
}
|
||||||
|
|
||||||
let pinId
|
let pinId
|
||||||
|
@ -723,7 +723,7 @@ export default {
|
||||||
}`, ...args)
|
}`, ...args)
|
||||||
|
|
||||||
if (npins >= 3) {
|
if (npins >= 3) {
|
||||||
throw new GraphQLError('max 3 pins allowed', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('max 3 pins allowed')
|
||||||
}
|
}
|
||||||
|
|
||||||
const [{ pinId: newPinId }] = await models.$queryRawUnsafe(`
|
const [{ pinId: newPinId }] = await models.$queryRawUnsafe(`
|
||||||
|
@ -757,10 +757,10 @@ export default {
|
||||||
deleteItem: async (parent, { id }, { me, models }) => {
|
deleteItem: async (parent, { id }, { me, models }) => {
|
||||||
const old = await models.item.findUnique({ where: { id: Number(id) } })
|
const old = await models.item.findUnique({ where: { id: Number(id) } })
|
||||||
if (Number(old.userId) !== Number(me?.id)) {
|
if (Number(old.userId) !== Number(me?.id)) {
|
||||||
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('item does not belong to you')
|
||||||
}
|
}
|
||||||
if (old.bio) {
|
if (old.bio) {
|
||||||
throw new GraphQLError('cannot delete bio', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot delete bio')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await deleteItemByAuthor({ models, id, item: old })
|
return await deleteItemByAuthor({ models, id, item: old })
|
||||||
|
@ -812,7 +812,7 @@ export default {
|
||||||
},
|
},
|
||||||
upsertJob: async (parent, { id, ...item }, { me, models, lnd }) => {
|
upsertJob: async (parent, { id, ...item }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in to create job', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
item.location = item.location?.toLowerCase() === 'remote' ? undefined : item.location
|
item.location = item.location?.toLowerCase() === 'remote' ? undefined : item.location
|
||||||
|
@ -841,7 +841,7 @@ export default {
|
||||||
},
|
},
|
||||||
updateNoteId: async (parent, { id, noteId }, { me, models }) => {
|
updateNoteId: async (parent, { id, noteId }, { me, models }) => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
throw new GraphQLError('id required', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('id required')
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.item.update({
|
await models.item.update({
|
||||||
|
@ -853,7 +853,7 @@ export default {
|
||||||
},
|
},
|
||||||
pollVote: async (parent, { id }, { me, models, lnd }) => {
|
pollVote: async (parent, { id }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await performPaidAction('POLL_VOTE', { id }, { me, models, lnd })
|
return await performPaidAction('POLL_VOTE', { id }, { me, models, lnd })
|
||||||
|
@ -869,24 +869,24 @@ export default {
|
||||||
WHERE id = $1`, Number(id))
|
WHERE id = $1`, Number(id))
|
||||||
|
|
||||||
if (item.deletedAt) {
|
if (item.deletedAt) {
|
||||||
throw new GraphQLError('item is deleted', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('item is deleted')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.invoiceActionState && item.invoiceActionState !== 'PAID') {
|
if (item.invoiceActionState && item.invoiceActionState !== 'PAID') {
|
||||||
throw new GraphQLError('cannot act on unpaid item', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot act on unpaid item')
|
||||||
}
|
}
|
||||||
|
|
||||||
// disallow self tips except anons
|
// disallow self tips except anons
|
||||||
if (me) {
|
if (me) {
|
||||||
if (Number(item.userId) === Number(me.id)) {
|
if (Number(item.userId) === Number(me.id)) {
|
||||||
throw new GraphQLError('cannot zap your self', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot zap yourself')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow tips if me is one of the forward user recipients
|
// Disallow tips if me is one of the forward user recipients
|
||||||
if (act === 'TIP') {
|
if (act === 'TIP') {
|
||||||
const existingForwards = await models.itemForward.findMany({ where: { itemId: Number(id) } })
|
const existingForwards = await models.itemForward.findMany({ where: { itemId: Number(id) } })
|
||||||
if (existingForwards.some(fwd => Number(fwd.userId) === Number(me.id))) {
|
if (existingForwards.some(fwd => Number(fwd.userId) === Number(me.id))) {
|
||||||
throw new GraphQLError('cannot zap a post for which you are forwarded zaps', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot zap a post for which you are forwarded zaps')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -896,12 +896,12 @@ export default {
|
||||||
} else if (act === 'DONT_LIKE_THIS') {
|
} else if (act === 'DONT_LIKE_THIS') {
|
||||||
return await performPaidAction('DOWN_ZAP', { id, sats }, { me, models, lnd })
|
return await performPaidAction('DOWN_ZAP', { id, sats }, { me, models, lnd })
|
||||||
} else {
|
} else {
|
||||||
throw new GraphQLError('unknown act', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('unknown act')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleOutlaw: async (parent, { id }, { me, models }) => {
|
toggleOutlaw: async (parent, { id }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = await models.item.findUnique({
|
const item = await models.item.findUnique({
|
||||||
|
@ -919,7 +919,7 @@ export default {
|
||||||
const sub = item.sub || item.root?.sub
|
const sub = item.sub || item.root?.sub
|
||||||
|
|
||||||
if (Number(sub.userId) !== Number(me.id)) {
|
if (Number(sub.userId) !== Number(me.id)) {
|
||||||
throw new GraphQLError('you cant do this broh', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('you cant do this broh')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.outlawed) {
|
if (item.outlawed) {
|
||||||
|
@ -1262,11 +1262,11 @@ export const updateItem = async (parent, { sub: subName, forward, ...item }, { m
|
||||||
const old = await models.item.findUnique({ where: { id: Number(item.id) }, include: { sub: true } })
|
const old = await models.item.findUnique({ where: { id: Number(item.id) }, include: { sub: true } })
|
||||||
|
|
||||||
if (old.deletedAt) {
|
if (old.deletedAt) {
|
||||||
throw new GraphQLError('item is deleted', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('item is deleted')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old.invoiceActionState && old.invoiceActionState !== 'PAID') {
|
if (old.invoiceActionState && old.invoiceActionState !== 'PAID') {
|
||||||
throw new GraphQLError('cannot edit unpaid item', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot edit unpaid item')
|
||||||
}
|
}
|
||||||
|
|
||||||
// author can always edit their own item
|
// author can always edit their own item
|
||||||
|
@ -1278,14 +1278,14 @@ export const updateItem = async (parent, { sub: subName, forward, ...item }, { m
|
||||||
const adminEdit = SN_USER_IDS.includes(mid) && allowEdit
|
const adminEdit = SN_USER_IDS.includes(mid) && allowEdit
|
||||||
|
|
||||||
if (!isMine && !adminEdit) {
|
if (!isMine && !adminEdit) {
|
||||||
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('item does not belong to you')
|
||||||
}
|
}
|
||||||
|
|
||||||
const differentSub = subName && old.subName !== subName
|
const differentSub = subName && old.subName !== subName
|
||||||
if (differentSub) {
|
if (differentSub) {
|
||||||
const sub = await models.sub.findUnique({ where: { name: subName } })
|
const sub = await models.sub.findUnique({ where: { name: subName } })
|
||||||
if (sub.baseCost > old.sub.baseCost) {
|
if (sub.baseCost > old.sub.baseCost) {
|
||||||
throw new GraphQLError('cannot change to a more expensive sub', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot change to a more expensive sub')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1299,7 +1299,7 @@ export const updateItem = async (parent, { sub: subName, forward, ...item }, { m
|
||||||
const timer = Date.now() < new Date(old.invoicePaidAt ?? old.createdAt).getTime() + 10 * 60_000
|
const timer = Date.now() < new Date(old.invoicePaidAt ?? old.createdAt).getTime() + 10 * 60_000
|
||||||
|
|
||||||
if (!allowEdit && !myBio && !timer && !isJob(item)) {
|
if (!allowEdit && !myBio && !timer && !isJob(item)) {
|
||||||
throw new GraphQLError('item can no longer be editted', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('item can no longer be edited')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.url && !isJob(item)) {
|
if (item.url && !isJob(item)) {
|
||||||
|
@ -1343,7 +1343,7 @@ export const createItem = async (parent, { forward, ...item }, { me, models, lnd
|
||||||
if (item.parentId) {
|
if (item.parentId) {
|
||||||
const parent = await models.item.findUnique({ where: { id: parseInt(item.parentId) } })
|
const parent = await models.item.findUnique({ where: { id: parseInt(item.parentId) } })
|
||||||
if (parent.invoiceActionState && parent.invoiceActionState !== 'PAID') {
|
if (parent.invoiceActionState && parent.invoiceActionState !== 'PAID') {
|
||||||
throw new GraphQLError('cannot comment on unpaid item', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot comment on unpaid item')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import { bech32 } from 'bech32'
|
import { bech32 } from 'bech32'
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import assertGofacYourself from './ofac'
|
import assertGofacYourself from './ofac'
|
||||||
import assertApiKeyNotPermitted from './apiKey'
|
import assertApiKeyNotPermitted from './apiKey'
|
||||||
|
import { GqlAuthenticationError } from '@/lib/error'
|
||||||
|
|
||||||
function encodedUrl (iurl, tag, k1) {
|
function encodedUrl (iurl, tag, k1) {
|
||||||
const url = new URL(iurl)
|
const url = new URL(iurl)
|
||||||
|
@ -35,7 +35,7 @@ export default {
|
||||||
await assertGofacYourself({ models, headers })
|
await assertGofacYourself({ models, headers })
|
||||||
|
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
|
@ -11,7 +11,7 @@ export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createMessage: async (parent, { text }, { me, models }) => {
|
createMessage: async (parent, { text }, { me, models }) => {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
throw new GraphQLError('Must have text', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('must have text')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.message.create({
|
return await models.message.create({
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { decodeCursor, LIMIT, nextNoteCursorEncoded } from '@/lib/cursor'
|
import { decodeCursor, LIMIT, nextNoteCursorEncoded } from '@/lib/cursor'
|
||||||
import { getItem, filterClause, whereClause, muteClause, activeOrMine } from './item'
|
import { getItem, filterClause, whereClause, muteClause, activeOrMine } from './item'
|
||||||
import { getInvoice, getWithdrawl } from './wallet'
|
import { getInvoice, getWithdrawl } from './wallet'
|
||||||
import { pushSubscriptionSchema, ssValidate } from '@/lib/validate'
|
import { pushSubscriptionSchema, ssValidate } from '@/lib/validate'
|
||||||
import { replyToSubscription } from '@/lib/webPush'
|
import { replyToSubscription } from '@/lib/webPush'
|
||||||
import { getSub } from './sub'
|
import { getSub } from './sub'
|
||||||
|
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
notifications: async (parent, { cursor, inc }, { me, models }) => {
|
notifications: async (parent, { cursor, inc }, { me, models }) => {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const meFull = await models.user.findUnique({ where: { id: me.id } })
|
const meFull = await models.user.findUnique({ where: { id: me.id } })
|
||||||
|
@ -382,7 +382,7 @@ export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
savePushSubscription: async (parent, { endpoint, p256dh, auth, oldEndpoint }, { me, models }) => {
|
savePushSubscription: async (parent, { endpoint, p256dh, auth, oldEndpoint }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await ssValidate(pushSubscriptionSchema, { endpoint, p256dh, auth })
|
await ssValidate(pushSubscriptionSchema, { endpoint, p256dh, auth })
|
||||||
|
@ -406,12 +406,12 @@ export default {
|
||||||
},
|
},
|
||||||
deletePushSubscription: async (parent, { endpoint }, { me, models }) => {
|
deletePushSubscription: async (parent, { endpoint }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscription = await models.pushSubscription.findFirst({ where: { endpoint, userId: Number(me.id) } })
|
const subscription = await models.pushSubscription.findFirst({ where: { endpoint, userId: Number(me.id) } })
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
throw new GraphQLError('endpoint not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('endpoint not found')
|
||||||
}
|
}
|
||||||
const deletedSubscription = await models.pushSubscription.delete({ where: { id: subscription.id } })
|
const deletedSubscription = await models.pushSubscription.delete({ where: { id: subscription.id } })
|
||||||
console.log(`[webPush] deleted subscription ${deletedSubscription.id} of user ${deletedSubscription.userId} due to client request`)
|
console.log(`[webPush] deleted subscription ${deletedSubscription.id} of user ${deletedSubscription.userId} due to client request`)
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import { GraphQLError } from 'graphql'
|
import { GqlAuthorizationError } from '@/lib/error'
|
||||||
|
|
||||||
// this function makes america more secure apparently
|
// this function makes america more secure apparently
|
||||||
export default async function assertGofacYourself ({ models, headers, ip }) {
|
export default async function assertGofacYourself ({ models, headers, ip }) {
|
||||||
const country = await gOFACYourself({ models, headers, ip })
|
const country = await gOFACYourself({ models, headers, ip })
|
||||||
if (!country) return
|
if (!country) return
|
||||||
|
|
||||||
throw new GraphQLError(
|
throw new GqlAuthorizationError(`Your IP address is in ${country}. We cannot provide financial services to residents of ${country}.`)
|
||||||
`Your IP address is in ${country}. We cannot provide financial services to residents of ${country}.`,
|
|
||||||
{ extensions: { code: 'FORBIDDEN' } })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function gOFACYourself ({ models, headers = {}, ip }) {
|
export async function gOFACYourself ({ models, headers = {}, ip }) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { timeUnitForRange, whenRange } from '@/lib/time'
|
import { timeUnitForRange, whenRange } from '@/lib/time'
|
||||||
import { viewGroup } from './growth'
|
import { viewGroup } from './growth'
|
||||||
|
import { GqlAuthenticationError } from '@/lib/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
referrals: async (parent, { when, from, to }, { models, me }) => {
|
referrals: async (parent, { when, from, to }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const range = whenRange(when, from, to)
|
const range = whenRange(when, from, to)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { amountSchema, ssValidate } from '@/lib/validate'
|
import { amountSchema, ssValidate } from '@/lib/validate'
|
||||||
import { getItem } from './item'
|
import { getItem } from './item'
|
||||||
import { topUsers } from './user'
|
import { topUsers } from './user'
|
||||||
import performPaidAction from '../paidAction'
|
import performPaidAction from '../paidAction'
|
||||||
|
import { GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
let rewardCache
|
let rewardCache
|
||||||
|
|
||||||
|
@ -63,21 +63,21 @@ async function getMonthlyRewards (when, models) {
|
||||||
async function getRewards (when, models) {
|
async function getRewards (when, models) {
|
||||||
if (when) {
|
if (when) {
|
||||||
if (when.length > 1) {
|
if (when.length > 1) {
|
||||||
throw new GraphQLError('too many dates', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('too many dates')
|
||||||
}
|
}
|
||||||
when.forEach(w => {
|
when.forEach(w => {
|
||||||
if (isNaN(new Date(w))) {
|
if (isNaN(new Date(w))) {
|
||||||
throw new GraphQLError('invalid date', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('invalid date')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (new Date(when[0]) > new Date(when[when.length - 1])) {
|
if (new Date(when[0]) > new Date(when[when.length - 1])) {
|
||||||
throw new GraphQLError('bad date range', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('bad date range')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new Date(when[0]).getTime() > new Date('2024-03-01').getTime() && new Date(when[0]).getTime() < new Date('2024-05-02').getTime()) {
|
if (new Date(when[0]).getTime() > new Date('2024-03-01').getTime() && new Date(when[0]).getTime() < new Date('2024-05-02').getTime()) {
|
||||||
// after 3/1/2024 and until 5/1/2024, we reward monthly on the 1st
|
// after 3/1/2024 and until 5/1/2024, we reward monthly on the 1st
|
||||||
if (new Date(when[0]).getUTCDate() !== 1) {
|
if (new Date(when[0]).getUTCDate() !== 1) {
|
||||||
throw new GraphQLError('invalid reward date', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('bad reward date')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getMonthlyRewards(when, models)
|
return await getMonthlyRewards(when, models)
|
||||||
|
@ -119,11 +119,11 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!when || when.length > 2) {
|
if (!when || when.length > 2) {
|
||||||
throw new GraphQLError('invalid date range', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('bad date range')
|
||||||
}
|
}
|
||||||
for (const w of when) {
|
for (const w of when) {
|
||||||
if (isNaN(new Date(w))) {
|
if (isNaN(new Date(w))) {
|
||||||
throw new GraphQLError('invalid date', { extensions: { code: 'BAD_USER_INPUT' } })
|
throw new GqlInputError('invalid date')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import retry from 'async-retry'
|
import retry from 'async-retry'
|
||||||
import Prisma from '@prisma/client'
|
import Prisma from '@prisma/client'
|
||||||
import { msatsToSats, numWithUnits } from '@/lib/format'
|
import { msatsToSats, numWithUnits } from '@/lib/format'
|
||||||
import { BALANCE_LIMIT_MSATS } from '@/lib/constants'
|
import { BALANCE_LIMIT_MSATS } from '@/lib/constants'
|
||||||
|
import { GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
export default async function serialize (trx, { models, lnd }) {
|
export default async function serialize (trx, { models, lnd }) {
|
||||||
// wrap first argument in array if not array already
|
// wrap first argument in array if not array already
|
||||||
|
@ -28,7 +28,7 @@ export default async function serialize (trx, { models, lnd }) {
|
||||||
// have to check the error message
|
// have to check the error message
|
||||||
if (error.message.includes('SN_INSUFFICIENT_FUNDS') ||
|
if (error.message.includes('SN_INSUFFICIENT_FUNDS') ||
|
||||||
error.message.includes('\\"users\\" violates check constraint \\"msats_positive\\"')) {
|
error.message.includes('\\"users\\" violates check constraint \\"msats_positive\\"')) {
|
||||||
bail(new GraphQLError('insufficient funds', { extensions: { code: 'BAD_INPUT' } }))
|
bail(new GqlInputError('insufficient funds'))
|
||||||
}
|
}
|
||||||
if (error.message.includes('SN_NOT_SERIALIZABLE')) {
|
if (error.message.includes('SN_NOT_SERIALIZABLE')) {
|
||||||
bail(new Error('wallet balance transaction is not serializable'))
|
bail(new Error('wallet balance transaction is not serializable'))
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { whenRange } from '@/lib/time'
|
import { whenRange } from '@/lib/time'
|
||||||
import { ssValidate, territorySchema } from '@/lib/validate'
|
import { ssValidate, territorySchema } from '@/lib/validate'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
||||||
import { viewGroup } from './growth'
|
import { viewGroup } from './growth'
|
||||||
import { notifyTerritoryTransfer } from '@/lib/webPush'
|
import { notifyTerritoryTransfer } from '@/lib/webPush'
|
||||||
import performPaidAction from '../paidAction'
|
import performPaidAction from '../paidAction'
|
||||||
|
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
export async function getSub (parent, { name }, { models, me }) {
|
export async function getSub (parent, { name }, { models, me }) {
|
||||||
if (!name) return null
|
if (!name) return null
|
||||||
|
@ -108,12 +108,12 @@ export default {
|
||||||
},
|
},
|
||||||
userSubs: async (_parent, { name, cursor, when, by, from, to, limit = LIMIT }, { models }) => {
|
userSubs: async (_parent, { name, cursor, when, by, from, to, limit = LIMIT }, { models }) => {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new GraphQLError('must supply user name', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('must supply user name')
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await models.user.findUnique({ where: { name } })
|
const user = await models.user.findUnique({ where: { name } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('no user has that name')
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
|
@ -154,7 +154,7 @@ export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
upsertSub: async (parent, { ...data }, { me, models, lnd }) => {
|
upsertSub: async (parent, { ...data }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await ssValidate(territorySchema, data, { models, me, sub: { name: data.oldName } })
|
await ssValidate(territorySchema, data, { models, me, sub: { name: data.oldName } })
|
||||||
|
@ -174,11 +174,11 @@ export default {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!sub) {
|
if (!sub) {
|
||||||
throw new GraphQLError('sub not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub.userId !== me.id) {
|
if (sub.userId !== me.id) {
|
||||||
throw new GraphQLError('you do not own this sub', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('you do not own this sub')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub.status === 'ACTIVE') {
|
if (sub.status === 'ACTIVE') {
|
||||||
|
@ -189,7 +189,7 @@ export default {
|
||||||
},
|
},
|
||||||
toggleMuteSub: async (parent, { name }, { me, models }) => {
|
toggleMuteSub: async (parent, { name }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const lookupData = { userId: Number(me.id), subName: name }
|
const lookupData = { userId: Number(me.id), subName: name }
|
||||||
|
@ -205,7 +205,7 @@ export default {
|
||||||
},
|
},
|
||||||
toggleSubSubscription: async (sub, { name }, { me, models }) => {
|
toggleSubSubscription: async (sub, { name }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const lookupData = { userId: me.id, subName: name }
|
const lookupData = { userId: me.id, subName: name }
|
||||||
|
@ -221,7 +221,7 @@ export default {
|
||||||
},
|
},
|
||||||
transferTerritory: async (parent, { subName, userName }, { me, models }) => {
|
transferTerritory: async (parent, { subName, userName }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const sub = await models.sub.findUnique({
|
const sub = await models.sub.findUnique({
|
||||||
|
@ -230,18 +230,18 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!sub) {
|
if (!sub) {
|
||||||
throw new GraphQLError('sub not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub not found')
|
||||||
}
|
}
|
||||||
if (sub.userId !== me.id) {
|
if (sub.userId !== me.id) {
|
||||||
throw new GraphQLError('you do not own this sub', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('you do not own this sub')
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await models.user.findFirst({ where: { name: userName } })
|
const user = await models.user.findFirst({ where: { name: userName } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new GraphQLError('user not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('user not found')
|
||||||
}
|
}
|
||||||
if (user.id === me.id) {
|
if (user.id === me.id) {
|
||||||
throw new GraphQLError('cannot transfer territory to yourself', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('cannot transfer territory to yourself')
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, updatedSub] = await models.$transaction([
|
const [, updatedSub] = await models.$transaction([
|
||||||
|
@ -255,7 +255,7 @@ export default {
|
||||||
},
|
},
|
||||||
unarchiveTerritory: async (parent, { ...data }, { me, models, lnd }) => {
|
unarchiveTerritory: async (parent, { ...data }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name } = data
|
const { name } = data
|
||||||
|
@ -264,16 +264,16 @@ export default {
|
||||||
|
|
||||||
const oldSub = await models.sub.findUnique({ where: { name } })
|
const oldSub = await models.sub.findUnique({ where: { name } })
|
||||||
if (!oldSub) {
|
if (!oldSub) {
|
||||||
throw new GraphQLError('sub not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub not found')
|
||||||
}
|
}
|
||||||
if (oldSub.status !== 'STOPPED') {
|
if (oldSub.status !== 'STOPPED') {
|
||||||
throw new GraphQLError('sub is not archived', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub is not archived')
|
||||||
}
|
}
|
||||||
if (oldSub.billingType === 'ONCE') {
|
if (oldSub.billingType === 'ONCE') {
|
||||||
// sanity check. this should never happen but leaving this comment here
|
// sanity check. this should never happen but leaving this comment here
|
||||||
// to stop error propagation just in case and document that this should never happen.
|
// to stop error propagation just in case and document that this should never happen.
|
||||||
// #defensivecode
|
// #defensivecode
|
||||||
throw new GraphQLError('sub should not be archived', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub should not be archived')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await performPaidAction('TERRITORY_UNARCHIVE', data, { me, models, lnd })
|
return await performPaidAction('TERRITORY_UNARCHIVE', data, { me, models, lnd })
|
||||||
|
@ -319,7 +319,7 @@ async function createSub (parent, data, { me, models, lnd }) {
|
||||||
return await performPaidAction('TERRITORY_CREATE', data, { me, models, lnd })
|
return await performPaidAction('TERRITORY_CREATE', data, { me, models, lnd })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'P2002') {
|
if (error.code === 'P2002') {
|
||||||
throw new GraphQLError('name taken', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('name taken')
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
@ -339,14 +339,14 @@ async function updateSub (parent, { oldName, ...data }, { me, models, lnd }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!oldSub) {
|
if (!oldSub) {
|
||||||
throw new GraphQLError('sub not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('sub not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await performPaidAction('TERRITORY_UPDATE', { oldName, ...data }, { me, models, lnd })
|
return await performPaidAction('TERRITORY_UPDATE', { oldName, ...data }, { me, models, lnd })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'P2002') {
|
if (error.code === 'P2002') {
|
||||||
throw new GraphQLError('name taken', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('name taken')
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { USER_ID, IMAGE_PIXELS_MAX, UPLOAD_SIZE_MAX, UPLOAD_SIZE_MAX_AVATAR, UPLOAD_TYPES_ALLOW } from '@/lib/constants'
|
import { USER_ID, IMAGE_PIXELS_MAX, UPLOAD_SIZE_MAX, UPLOAD_SIZE_MAX_AVATAR, UPLOAD_TYPES_ALLOW } from '@/lib/constants'
|
||||||
import { createPresignedPost } from '@/api/s3'
|
import { createPresignedPost } from '@/api/s3'
|
||||||
|
import { GqlAuthenticationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
getSignedPOST: async (parent, { type, size, width, height, avatar }, { models, me }) => {
|
getSignedPOST: async (parent, { type, size, width, height, avatar }, { models, me }) => {
|
||||||
if (UPLOAD_TYPES_ALLOW.indexOf(type) === -1) {
|
if (UPLOAD_TYPES_ALLOW.indexOf(type) === -1) {
|
||||||
throw new GraphQLError(`image must be ${UPLOAD_TYPES_ALLOW.map(t => t.replace('image/', '')).join(', ')}`, { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError(`image must be ${UPLOAD_TYPES_ALLOW.map(t => t.replace('image/', '')).join(', ')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > UPLOAD_SIZE_MAX) {
|
if (size > UPLOAD_SIZE_MAX) {
|
||||||
throw new GraphQLError(`image must be less than ${UPLOAD_SIZE_MAX / (1024 ** 2)} megabytes`, { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError(`image must be less than ${UPLOAD_SIZE_MAX / (1024 ** 2)} megabytes`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatar && size > UPLOAD_SIZE_MAX_AVATAR) {
|
if (avatar && size > UPLOAD_SIZE_MAX_AVATAR) {
|
||||||
throw new GraphQLError(`image must be less than ${UPLOAD_SIZE_MAX_AVATAR / (1024 ** 2)} megabytes`, { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError(`image must be less than ${UPLOAD_SIZE_MAX_AVATAR / (1024 ** 2)} megabytes`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width * height > IMAGE_PIXELS_MAX) {
|
if (width * height > IMAGE_PIXELS_MAX) {
|
||||||
throw new GraphQLError(`image must be less than ${IMAGE_PIXELS_MAX} pixels`, { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError(`image must be less than ${IMAGE_PIXELS_MAX} pixels`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const imgParams = {
|
const imgParams = {
|
||||||
|
@ -31,7 +31,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
if (!me) throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
if (!me) throw new GqlAuthenticationError()
|
||||||
imgParams.paid = undefined
|
imgParams.paid = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { readFile } from 'fs/promises'
|
import { readFile } from 'fs/promises'
|
||||||
import { join, resolve } from 'path'
|
import { join, resolve } from 'path'
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
||||||
import { msatsToSats } from '@/lib/format'
|
import { msatsToSats } from '@/lib/format'
|
||||||
import { bioSchema, emailSchema, settingsSchema, ssValidate, userSchema } from '@/lib/validate'
|
import { bioSchema, emailSchema, settingsSchema, ssValidate, userSchema } from '@/lib/validate'
|
||||||
|
@ -11,6 +10,7 @@ import { timeUnitForRange, whenRange } from '@/lib/time'
|
||||||
import assertApiKeyNotPermitted from './apiKey'
|
import assertApiKeyNotPermitted from './apiKey'
|
||||||
import { hashEmail } from '@/lib/crypto'
|
import { hashEmail } from '@/lib/crypto'
|
||||||
import { isMuted } from '@/lib/user'
|
import { isMuted } from '@/lib/user'
|
||||||
|
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
const contributors = new Set()
|
const contributors = new Set()
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ export default {
|
||||||
},
|
},
|
||||||
settings: async (parent, args, { models, me }) => {
|
settings: async (parent, args, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.user.findUnique({ where: { id: me.id } })
|
return await models.user.findUnique({ where: { id: me.id } })
|
||||||
|
@ -144,7 +144,7 @@ export default {
|
||||||
},
|
},
|
||||||
mySubscribedUsers: async (parent, { cursor }, { models, me }) => {
|
mySubscribedUsers: async (parent, { cursor }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('You must be logged in to view subscribed users', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
|
@ -165,7 +165,7 @@ export default {
|
||||||
},
|
},
|
||||||
myMutedUsers: async (parent, { cursor }, { models, me }) => {
|
myMutedUsers: async (parent, { cursor }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('You must be logged in to view muted users', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
|
@ -624,7 +624,7 @@ export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
disableFreebies: async (parent, args, { me, models }) => {
|
disableFreebies: async (parent, args, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable freebies if it hasn't been set yet
|
// disable freebies if it hasn't been set yet
|
||||||
|
@ -644,7 +644,7 @@ export default {
|
||||||
},
|
},
|
||||||
setName: async (parent, data, { me, models }) => {
|
setName: async (parent, data, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await ssValidate(userSchema, data, { models })
|
await ssValidate(userSchema, data, { models })
|
||||||
|
@ -654,14 +654,14 @@ export default {
|
||||||
return data.name
|
return data.name
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'P2002') {
|
if (error.code === 'P2002') {
|
||||||
throw new GraphQLError('name taken', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('name taken')
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setSettings: async (parent, { settings: { nostrRelays, ...data } }, { me, models }) => {
|
setSettings: async (parent, { settings: { nostrRelays, ...data } }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await ssValidate(settingsSchema, { nostrRelays, ...data })
|
await ssValidate(settingsSchema, { nostrRelays, ...data })
|
||||||
|
@ -687,7 +687,7 @@ export default {
|
||||||
},
|
},
|
||||||
setWalkthrough: async (parent, { upvotePopover, tipPopover }, { me, models }) => {
|
setWalkthrough: async (parent, { upvotePopover, tipPopover }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.user.update({ where: { id: me.id }, data: { upvotePopover, tipPopover } })
|
await models.user.update({ where: { id: me.id }, data: { upvotePopover, tipPopover } })
|
||||||
|
@ -696,7 +696,7 @@ export default {
|
||||||
},
|
},
|
||||||
setPhoto: async (parent, { photoId }, { me, models }) => {
|
setPhoto: async (parent, { photoId }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.user.update({
|
await models.user.update({
|
||||||
|
@ -708,7 +708,7 @@ export default {
|
||||||
},
|
},
|
||||||
upsertBio: async (parent, { bio }, { me, models }) => {
|
upsertBio: async (parent, { bio }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await ssValidate(bioSchema, { bio })
|
await ssValidate(bioSchema, { bio })
|
||||||
|
@ -725,12 +725,12 @@ export default {
|
||||||
},
|
},
|
||||||
generateApiKey: async (parent, { id }, { models, me }) => {
|
generateApiKey: async (parent, { id }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await models.user.findUnique({ where: { id: me.id } })
|
const user = await models.user.findUnique({ where: { id: me.id } })
|
||||||
if (!user.apiKeyEnabled) {
|
if (!user.apiKeyEnabled) {
|
||||||
throw new GraphQLError('you are not allowed to generate api keys', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthorizationError('you are not allowed to generate api keys')
|
||||||
}
|
}
|
||||||
|
|
||||||
// I trust postgres CSPRNG more than the one from JS
|
// I trust postgres CSPRNG more than the one from JS
|
||||||
|
@ -745,14 +745,14 @@ export default {
|
||||||
},
|
},
|
||||||
deleteApiKey: async (parent, { id }, { models, me }) => {
|
deleteApiKey: async (parent, { id }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.user.update({ where: { id: me.id }, data: { apiKeyHash: null } })
|
return await models.user.update({ where: { id: me.id }, data: { apiKeyHash: null } })
|
||||||
},
|
},
|
||||||
unlinkAuth: async (parent, { authType }, { models, me }) => {
|
unlinkAuth: async (parent, { authType }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
|
@ -761,7 +761,7 @@ export default {
|
||||||
user = await models.user.findUnique({ where: { id: me.id } })
|
user = await models.user.findUnique({ where: { id: me.id } })
|
||||||
const account = await models.account.findFirst({ where: { userId: me.id, provider: authType } })
|
const account = await models.account.findFirst({ where: { userId: me.id, provider: authType } })
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new GraphQLError('no such account', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('no such account')
|
||||||
}
|
}
|
||||||
await models.account.delete({ where: { id: account.id } })
|
await models.account.delete({ where: { id: account.id } })
|
||||||
if (authType === 'twitter') {
|
if (authType === 'twitter') {
|
||||||
|
@ -776,14 +776,14 @@ export default {
|
||||||
} else if (authType === 'email') {
|
} else if (authType === 'email') {
|
||||||
user = await models.user.update({ where: { id: me.id }, data: { email: null, emailVerified: null, emailHash: null } })
|
user = await models.user.update({ where: { id: me.id }, data: { email: null, emailVerified: null, emailHash: null } })
|
||||||
} else {
|
} else {
|
||||||
throw new GraphQLError('no such account', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('no such account')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await authMethods(user, undefined, { models, me })
|
return await authMethods(user, undefined, { models, me })
|
||||||
},
|
},
|
||||||
linkUnverifiedEmail: async (parent, { email }, { models, me }) => {
|
linkUnverifiedEmail: async (parent, { email }, { models, me }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
|
@ -796,7 +796,7 @@ export default {
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'P2002') {
|
if (error.code === 'P2002') {
|
||||||
throw new GraphQLError('email taken', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('email taken')
|
||||||
}
|
}
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
@ -809,12 +809,12 @@ export default {
|
||||||
const muted = await isMuted({ models, muterId: me?.id, mutedId: id })
|
const muted = await isMuted({ models, muterId: me?.id, mutedId: id })
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (muted && !existing.postsSubscribedAt) {
|
if (muted && !existing.postsSubscribedAt) {
|
||||||
throw new GraphQLError("you can't subscribe to a stacker that you've muted", { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError("you can't subscribe to a stacker that you've muted")
|
||||||
}
|
}
|
||||||
await models.userSubscription.update({ where: { followerId_followeeId: lookupData }, data: { postsSubscribedAt: existing.postsSubscribedAt ? null : new Date() } })
|
await models.userSubscription.update({ where: { followerId_followeeId: lookupData }, data: { postsSubscribedAt: existing.postsSubscribedAt ? null : new Date() } })
|
||||||
} else {
|
} else {
|
||||||
if (muted) {
|
if (muted) {
|
||||||
throw new GraphQLError("you can't subscribe to a stacker that you've muted", { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError("you can't subscribe to a stacker that you've muted")
|
||||||
}
|
}
|
||||||
await models.userSubscription.create({ data: { ...lookupData, postsSubscribedAt: new Date() } })
|
await models.userSubscription.create({ data: { ...lookupData, postsSubscribedAt: new Date() } })
|
||||||
}
|
}
|
||||||
|
@ -826,12 +826,12 @@ export default {
|
||||||
const muted = await isMuted({ models, muterId: me?.id, mutedId: id })
|
const muted = await isMuted({ models, muterId: me?.id, mutedId: id })
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (muted && !existing.commentsSubscribedAt) {
|
if (muted && !existing.commentsSubscribedAt) {
|
||||||
throw new GraphQLError("you can't subscribe to a stacker that you've muted", { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError("you can't subscribe to a stacker that you've muted")
|
||||||
}
|
}
|
||||||
await models.userSubscription.update({ where: { followerId_followeeId: lookupData }, data: { commentsSubscribedAt: existing.commentsSubscribedAt ? null : new Date() } })
|
await models.userSubscription.update({ where: { followerId_followeeId: lookupData }, data: { commentsSubscribedAt: existing.commentsSubscribedAt ? null : new Date() } })
|
||||||
} else {
|
} else {
|
||||||
if (muted) {
|
if (muted) {
|
||||||
throw new GraphQLError("you can't subscribe to a stacker that you've muted", { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError("you can't subscribe to a stacker that you've muted")
|
||||||
}
|
}
|
||||||
await models.userSubscription.create({ data: { ...lookupData, commentsSubscribedAt: new Date() } })
|
await models.userSubscription.create({ data: { ...lookupData, commentsSubscribedAt: new Date() } })
|
||||||
}
|
}
|
||||||
|
@ -854,7 +854,7 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (subscription?.postsSubscribedAt || subscription?.commentsSubscribedAt) {
|
if (subscription?.postsSubscribedAt || subscription?.commentsSubscribedAt) {
|
||||||
throw new GraphQLError("you can't mute a stacker to whom you've subscribed", { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError("you can't mute a stacker to whom you've subscribed")
|
||||||
}
|
}
|
||||||
await models.mute.create({ data: { ...lookupData } })
|
await models.mute.create({ data: { ...lookupData } })
|
||||||
}
|
}
|
||||||
|
@ -862,7 +862,7 @@ export default {
|
||||||
},
|
},
|
||||||
hideWelcomeBanner: async (parent, data, { me, models }) => {
|
hideWelcomeBanner: async (parent, data, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
|
await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, getInvoice as getInvoiceFromLnd, getNode, deletePayment, getPayment, getIdentity } from 'ln-service'
|
import { createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, getInvoice as getInvoiceFromLnd, getNode, deletePayment, getPayment, getIdentity } from 'ln-service'
|
||||||
import { GraphQLError } from 'graphql'
|
|
||||||
import crypto, { timingSafeEqual } from 'crypto'
|
import crypto, { timingSafeEqual } from 'crypto'
|
||||||
import serialize from './serial'
|
import serialize from './serial'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
||||||
|
@ -15,6 +14,7 @@ import { finalizeHodlInvoice } from 'worker/wallet'
|
||||||
import walletDefs from 'wallets/server'
|
import walletDefs from 'wallets/server'
|
||||||
import { generateResolverName, generateTypeDefName } from '@/lib/wallet'
|
import { generateResolverName, generateTypeDefName } from '@/lib/wallet'
|
||||||
import { lnAddrOptions } from '@/lib/lnurl'
|
import { lnAddrOptions } from '@/lib/lnurl'
|
||||||
|
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
|
||||||
|
|
||||||
function injectResolvers (resolvers) {
|
function injectResolvers (resolvers) {
|
||||||
console.group('injected GraphQL resolvers:')
|
console.group('injected GraphQL resolvers:')
|
||||||
|
@ -54,17 +54,17 @@ export async function getInvoice (parent, { id }, { me, models, lnd }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!inv) {
|
if (!inv) {
|
||||||
throw new GraphQLError('invoice not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('invoice not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inv.user.id === USER_ID.anon) {
|
if (inv.user.id === USER_ID.anon) {
|
||||||
return inv
|
return inv
|
||||||
}
|
}
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
if (inv.user.id !== me.id) {
|
if (inv.user.id !== me.id) {
|
||||||
throw new GraphQLError('not ur invoice', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('not ur invoice')
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -85,7 +85,7 @@ export async function getInvoice (parent, { id }, { me, models, lnd }) {
|
||||||
|
|
||||||
export async function getWithdrawl (parent, { id }, { me, models, lnd }) {
|
export async function getWithdrawl (parent, { id }, { me, models, lnd }) {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const wdrwl = await models.withdrawl.findUnique({
|
const wdrwl = await models.withdrawl.findUnique({
|
||||||
|
@ -99,11 +99,11 @@ export async function getWithdrawl (parent, { id }, { me, models, lnd }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!wdrwl) {
|
if (!wdrwl) {
|
||||||
throw new GraphQLError('withdrawal not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('withdrawal not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wdrwl.user.id !== me.id) {
|
if (wdrwl.user.id !== me.id) {
|
||||||
throw new GraphQLError('not ur withdrawal', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlInputError('not ur withdrawal')
|
||||||
}
|
}
|
||||||
|
|
||||||
return wdrwl
|
return wdrwl
|
||||||
|
@ -119,7 +119,7 @@ const resolvers = {
|
||||||
invoice: getInvoice,
|
invoice: getInvoice,
|
||||||
wallet: async (parent, { id }, { me, models }) => {
|
wallet: async (parent, { id }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.wallet.findUnique({
|
return await models.wallet.findUnique({
|
||||||
|
@ -131,7 +131,7 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
walletByType: async (parent, { type }, { me, models }) => {
|
walletByType: async (parent, { type }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const wallet = await models.wallet.findFirst({
|
const wallet = await models.wallet.findFirst({
|
||||||
|
@ -144,7 +144,7 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
wallets: async (parent, args, { me, models }) => {
|
wallets: async (parent, args, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.wallet.findMany({
|
return await models.wallet.findMany({
|
||||||
|
@ -156,7 +156,7 @@ const resolvers = {
|
||||||
withdrawl: getWithdrawl,
|
withdrawl: getWithdrawl,
|
||||||
numBolt11s: async (parent, args, { me, models, lnd }) => {
|
numBolt11s: async (parent, args, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.withdrawl.count({
|
return await models.withdrawl.count({
|
||||||
|
@ -172,7 +172,7 @@ const resolvers = {
|
||||||
walletHistory: async (parent, { cursor, inc }, { me, models, lnd }) => {
|
walletHistory: async (parent, { cursor, inc }, { me, models, lnd }) => {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const include = new Set(inc?.split(','))
|
const include = new Set(inc?.split(','))
|
||||||
|
@ -337,7 +337,7 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
walletLogs: async (parent, args, { me, models }) => {
|
walletLogs: async (parent, args, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return await models.walletLog.findMany({
|
return await models.walletLog.findMany({
|
||||||
|
@ -413,14 +413,14 @@ const resolvers = {
|
||||||
cancelInvoice: async (parent, { hash, hmac }, { models, lnd, boss }) => {
|
cancelInvoice: async (parent, { hash, hmac }, { models, lnd, boss }) => {
|
||||||
const hmac2 = createHmac(hash)
|
const hmac2 = createHmac(hash)
|
||||||
if (!timingSafeEqual(Buffer.from(hmac), Buffer.from(hmac2))) {
|
if (!timingSafeEqual(Buffer.from(hmac), Buffer.from(hmac2))) {
|
||||||
throw new GraphQLError('bad hmac', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthorizationError('bad hmac')
|
||||||
}
|
}
|
||||||
await finalizeHodlInvoice({ data: { hash }, lnd, models, boss })
|
await finalizeHodlInvoice({ data: { hash }, lnd, models, boss })
|
||||||
return await models.invoice.findFirst({ where: { hash } })
|
return await models.invoice.findFirst({ where: { hash } })
|
||||||
},
|
},
|
||||||
dropBolt11: async (parent, { id }, { me, models, lnd }) => {
|
dropBolt11: async (parent, { id }, { me, models, lnd }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const retention = `${INVOICE_RETENTION_DAYS} days`
|
const retention = `${INVOICE_RETENTION_DAYS} days`
|
||||||
|
@ -450,19 +450,19 @@ const resolvers = {
|
||||||
where: { id: invoice.id },
|
where: { id: invoice.id },
|
||||||
data: { hash: invoice.hash, bolt11: invoice.bolt11 }
|
data: { hash: invoice.hash, bolt11: invoice.bolt11 }
|
||||||
})
|
})
|
||||||
throw new GraphQLError('failed to drop bolt11 from lnd', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('failed to drop bolt11 from lnd')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { id }
|
return { id }
|
||||||
},
|
},
|
||||||
removeWallet: async (parent, { id }, { me, models }) => {
|
removeWallet: async (parent, { id }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
const wallet = await models.wallet.findUnique({ where: { userId: me.id, id: Number(id) } })
|
const wallet = await models.wallet.findUnique({ where: { userId: me.id, id: Number(id) } })
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
throw new GraphQLError('wallet not found', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('wallet not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.$transaction([
|
await models.$transaction([
|
||||||
|
@ -475,7 +475,7 @@ const resolvers = {
|
||||||
},
|
},
|
||||||
deleteWalletLogs: async (parent, { wallet }, { me, models }) => {
|
deleteWalletLogs: async (parent, { wallet }, { me, models }) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.walletLog.deleteMany({ where: { userId: me.id, wallet } })
|
await models.walletLog.deleteMany({ where: { userId: me.id, wallet } })
|
||||||
|
@ -575,7 +575,7 @@ export const addWalletLog = async ({ wallet, level, message }, { models }) => {
|
||||||
async function upsertWallet (
|
async function upsertWallet (
|
||||||
{ wallet, testCreateInvoice }, { settings, data, priorityOnly }, { me, models }) {
|
{ wallet, testCreateInvoice }, { settings, data, priorityOnly }, { me, models }) {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
|
@ -588,7 +588,7 @@ async function upsertWallet (
|
||||||
wallet = { ...wallet, userId: me.id }
|
wallet = { ...wallet, userId: me.id }
|
||||||
await addWalletLog({ wallet, level: 'ERROR', message }, { models })
|
await addWalletLog({ wallet, level: 'ERROR', message }, { models })
|
||||||
await addWalletLog({ wallet, level: 'INFO', message: 'receives disabled' }, { models })
|
await addWalletLog({ wallet, level: 'INFO', message: 'receives disabled' }, { models })
|
||||||
throw new GraphQLError(message, { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +674,7 @@ export async function createWithdrawal (parent, { invoice, maxFee }, { me, model
|
||||||
decoded = await decodePaymentRequest({ lnd, request: invoice })
|
decoded = await decodePaymentRequest({ lnd, request: invoice })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
throw new GraphQLError('could not decode invoice', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('could not decode invoice')
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -692,7 +692,7 @@ export async function createWithdrawal (parent, { invoice, maxFee }, { me, model
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decoded.mtokens || BigInt(decoded.mtokens) <= 0) {
|
if (!decoded.mtokens || BigInt(decoded.mtokens) <= 0) {
|
||||||
throw new GraphQLError('your invoice must specify an amount', { extensions: { code: 'BAD_INPUT' } })
|
throw new GqlInputError('your invoice must specify an amount')
|
||||||
}
|
}
|
||||||
|
|
||||||
const msatsFee = Number(maxFee) * 1000
|
const msatsFee = Number(maxFee) * 1000
|
||||||
|
@ -721,7 +721,7 @@ export async function createWithdrawal (parent, { invoice, maxFee }, { me, model
|
||||||
export async function sendToLnAddr (parent, { addr, amount, maxFee, comment, ...payer },
|
export async function sendToLnAddr (parent, { addr, amount, maxFee, comment, ...payer },
|
||||||
{ me, models, lnd, headers }) {
|
{ me, models, lnd, headers }) {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } })
|
throw new GqlAuthenticationError()
|
||||||
}
|
}
|
||||||
assertApiKeyNotPermitted({ me })
|
assertApiKeyNotPermitted({ me })
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { GraphQLError } from 'graphql'
|
||||||
|
|
||||||
|
export const E_FORBIDDEN = 'E_FORBIDDEN'
|
||||||
|
export const E_UNAUTHENTICATED = 'E_UNAUTHENTICATED'
|
||||||
|
export const E_BAD_INPUT = 'E_BAD_INPUT'
|
||||||
|
|
||||||
|
export class GqlAuthorizationError extends GraphQLError {
|
||||||
|
constructor (message) {
|
||||||
|
super(message, { extensions: { code: E_FORBIDDEN } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GqlAuthenticationError extends GraphQLError {
|
||||||
|
constructor () {
|
||||||
|
super('you must be logged in', { extensions: { code: E_UNAUTHENTICATED } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GqlInputError extends GraphQLError {
|
||||||
|
constructor (message) {
|
||||||
|
super(message, { extensions: { code: E_BAD_INPUT } })
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue