upgrade to prisma 4

This commit is contained in:
keyan 2023-07-26 19:18:42 -05:00
parent 5fb5b5c5e5
commit 7542dd6cc4
33 changed files with 193 additions and 128 deletions

View File

@ -12,8 +12,36 @@ import search from './search'
import rewards from './rewards'
import referrals from './referrals'
import price from './price'
import { GraphQLJSONObject } from 'graphql-type-json'
import { GraphQLJSONObject as JSONObject } from 'graphql-type-json'
import admin from './admin'
import { GraphQLScalarType, Kind } from 'graphql'
const date = new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize (value) {
if (value instanceof Date) {
return value.toISOString() // Convert outgoing Date to string for JSON
} else if (typeof value === 'string') {
return value
}
throw Error('GraphQL Date Scalar serializer expected a `Date` object got `' + typeof value + '` ' + value)
},
parseValue (value) {
if (typeof value === 'string') {
return new Date(value) // Convert incoming string to Date
}
throw new Error('GraphQL Date Scalar parser expected a `string`')
},
parseLiteral (ast) {
if (ast.kind === Kind.STRING) {
// Convert hard-coded AST string to integer and then to Date
return new Date(ast.value)
}
// Invalid hard-coded value (not an integer)
return null
}
})
export default [user, item, message, wallet, lnurl, notifications, invite, sub,
upload, search, growth, rewards, referrals, price, admin, { JSONObject: GraphQLJSONObject }]
upload, search, growth, rewards, referrals, price, admin, { JSONObject }, { Date: date }]

View File

@ -1,5 +1,6 @@
import { GraphQLError } from 'graphql'
import { inviteSchema, ssValidate } from '../../lib/validate'
import { msatsToSats } from '../../lib/format'
export default {
Query: {
@ -59,7 +60,7 @@ export default {
},
poor: async (invite, args, { me, models }) => {
const user = await models.user.findUnique({ where: { id: invite.userId } })
return user.msats < invite.gift * 1000
return msatsToSats(user.msats) < invite.gift
}
}
}

View File

@ -53,12 +53,12 @@ async function comments (me, models, id, sort) {
const filter = await commentFilterClause(me, models)
if (me) {
const [{ item_comments_with_me: comments }] = await models.$queryRawUnsafe(
'SELECT item_comments_with_me($1, $2, $3, $4, $5)', Number(id), Number(me.id), COMMENT_DEPTH_LIMIT, filter, orderBy)
'SELECT item_comments_with_me($1::INTEGER, $2::INTEGER, $3::INTEGER, $4, $5)', Number(id), Number(me.id), COMMENT_DEPTH_LIMIT, filter, orderBy)
return comments
}
const [{ item_comments: comments }] = await models.$queryRawUnsafe(
'SELECT item_comments($1, $2, $3, $4)', Number(id), COMMENT_DEPTH_LIMIT, filter, orderBy)
'SELECT item_comments($1::INTEGER, $2::INTEGER, $3, $4)', Number(id), COMMENT_DEPTH_LIMIT, filter, orderBy)
return comments
}
@ -272,7 +272,7 @@ export default {
itemRepetition: async (parent, { parentId }, { me, models }) => {
if (!me) return 0
// how many of the parents starting at parentId belong to me
const [{ item_spam: count }] = await models.$queryRawUnsafe(`SELECT item_spam($1, $2, '${ITEM_SPAM_INTERVAL}')`,
const [{ item_spam: count }] = await models.$queryRawUnsafe(`SELECT item_spam($1::INTEGER, $2::INTEGER, '${ITEM_SPAM_INTERVAL}')`,
Number(parentId), Number(me.id))
return count
@ -621,7 +621,7 @@ export default {
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
}
const [item] = await serialize(models,
models.$queryRawUnsafe(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6, $7) AS "Item"`,
models.$queryRawUnsafe(`${SELECT} FROM update_poll($1, $2::INTEGER, $3, $4, $5::INTEGER, $6, $7::INTEGER) AS "Item"`,
sub || 'bitcoin', Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id)))
await createMentions(item, models)
@ -629,7 +629,7 @@ export default {
return item
} else {
const [item] = await serialize(models,
models.$queryRawUnsafe(`${SELECT} FROM create_poll($1, $2, $3, $4, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
models.$queryRawUnsafe(`${SELECT} FROM create_poll($1, $2, $3, $4::INTEGER, $5::INTEGER, $6::INTEGER, $7, $8::INTEGER, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
sub || 'bitcoin', title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id)))
await createMentions(item, models)
@ -659,12 +659,12 @@ export default {
}
([item] = await serialize(models,
models.$queryRawUnsafe(
`${SELECT} FROM update_job($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) AS "Item"`,
`${SELECT} FROM update_job($1::INTEGER, $2, $3, $4, $5::INTEGER, $6, $7, $8, $9::INTEGER, $10::"Status") AS "Item"`,
Number(id), title, url, text, Number(maxBid), company, loc, remote, Number(logo), status)))
} else {
([item] = await serialize(models,
models.$queryRawUnsafe(
`${SELECT} FROM create_job($1, $2, $3, $4, $5, $6, $7, $8, $9) AS "Item"`,
`${SELECT} FROM create_job($1, $2, $3, $4::INTEGER, $5::INTEGER, $6, $7, $8, $9::INTEGER) AS "Item"`,
title, url, text, Number(me.id), Number(maxBid), company, loc, remote, Number(logo))))
}
@ -702,7 +702,7 @@ export default {
}
await serialize(models,
models.$queryRawUnsafe(`${SELECT} FROM poll_vote($1, $2) AS "Item"`,
models.$queryRawUnsafe(`${SELECT} FROM poll_vote($1::INTEGER, $2::INTEGER) AS "Item"`,
Number(id), Number(me.id)))
return id
@ -724,7 +724,7 @@ export default {
throw new GraphQLError('cannot zap your self', { extensions: { code: 'BAD_INPUT' } })
}
const [{ item_act: vote }] = await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}, ${me.id}, 'TIP', ${Number(sats)})`)
const [{ item_act: vote }] = await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER, ${me.id}::INTEGER, 'TIP', ${Number(sats)}::INTEGER)`)
const updatedItem = await models.item.findUnique({ where: { id: Number(id) } })
const title = `your ${updatedItem.title ? 'post' : 'reply'} ${updatedItem.fwdUser ? 'forwarded' : 'stacked'} ${Math.floor(Number(updatedItem.msats) / 1000)} sats${updatedItem.fwdUser ? ` to @${updatedItem.fwdUser.name}` : ''}`
@ -755,7 +755,7 @@ export default {
throw new GraphQLError('cannot downvote your self', { extensions: { code: 'BAD_INPUT' } })
}
await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}, ${me.id}, 'DONT_LIKE_THIS', ${DONT_LIKE_THIS_COST})`)
await serialize(models, models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER, ${me.id}::INTEGER, 'DONT_LIKE_THIS', ${DONT_LIKE_THIS_COST}::INTEGER)`)
return true
}
@ -818,7 +818,7 @@ export default {
}
const options = await models.$queryRaw`
SELECT "PollOption".id, option, count("PollVote"."userId") as count,
SELECT "PollOption".id, option, count("PollVote"."userId")::INTEGER as count,
coalesce(bool_or("PollVote"."userId" = ${me?.id}), 'f') as "meVoted"
FROM "PollOption"
LEFT JOIN "PollVote" on "PollVote"."pollOptionId" = "PollOption".id
@ -826,6 +826,7 @@ export default {
GROUP BY "PollOption".id
ORDER BY "PollOption".id ASC
`
const poll = {}
poll.options = options
poll.meVoted = options.some(o => o.meVoted)
@ -856,7 +857,9 @@ export default {
},
meSats: async (item, args, { me, models }) => {
if (!me) return 0
if (typeof item.meMsats === 'number') return msatsToSats(item.meMsats)
if (typeof item.meMsats !== 'undefined') {
return msatsToSats(item.meMsats)
}
const { _sum: { msats } } = await models.itemAct.aggregate({
_sum: {
@ -1035,7 +1038,7 @@ export const updateItem = async (parent, { id, data: { sub, title, url, text, bo
const [item] = await serialize(models,
models.$queryRawUnsafe(
`${SELECT} FROM update_item($1, $2, $3, $4, $5, $6, $7, $8) AS "Item"`,
`${SELECT} FROM update_item($1, $2::INTEGER, $3, $4, $5, $6::INTEGER, $7::INTEGER, $8::INTEGER) AS "Item"`,
old.parentId ? null : sub || 'bitcoin', Number(id), title, url, text,
Number(boost || 0), bounty ? Number(bounty) : null, Number(fwdUser?.id)))
@ -1071,7 +1074,7 @@ const createItem = async (parent, { sub, title, url, text, boost, forward, bount
const [item] = await serialize(
models,
models.$queryRawUnsafe(
`${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, $8, $9, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
`${SELECT} FROM create_item($1, $2, $3, $4, $5::INTEGER, $6::INTEGER, $7::INTEGER, $8::INTEGER, $9::INTEGER, '${ITEM_SPAM_INTERVAL}') AS "Item"`,
parentId ? null : sub || 'bitcoin',
title,
url,

View File

@ -16,7 +16,7 @@ export default {
`, Number(me.id))
const [{ totalReferrals }] = await models.$queryRawUnsafe(`
SELECT count(*) as "totalReferrals"
SELECT count(*)::INTEGER as "totalReferrals"
FROM users
WHERE ${intervalClause(when, 'users', true)}
"referrerId" = $1

View File

@ -43,7 +43,7 @@ export default {
await serialize(models,
models.$queryRawUnsafe(
'SELECT donate($1, $2)',
'SELECT donate($1::INTEGER, $2::INTEGER)',
sats, Number(me.id)))
return sats

View File

@ -1,13 +1,13 @@
const { GraphQLError } = require('graphql')
const retry = require('async-retry')
const Prisma = require('@prisma/client')
async function serialize (models, call) {
return await retry(async bail => {
try {
const [, result] = await models.$transaction([
models.$executeRawUnsafe(SERIALIZE),
call
])
const [, result] = await models.$transaction(
[models.$executeRaw`SELECT ASSERT_SERIALIZED()`, call],
{ isolationLevel: Prisma.TransactionIsolationLevel.Serializable })
return result
} catch (error) {
console.log(error)
@ -56,6 +56,4 @@ async function serialize (models, call) {
})
}
const SERIALIZE = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'
module.exports = serialize

View File

@ -193,7 +193,7 @@ export default {
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
} else if (by === 'posts') {
users = await models.$queryRawUnsafe(`
SELECT users.*, count(*) as nposts
SELECT users.*, count(*)::INTEGER as nposts
FROM users
JOIN "Item" on "Item"."userId" = users.id
WHERE "Item".created_at <= $1 AND "Item"."parentId" IS NULL
@ -205,7 +205,7 @@ export default {
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
} else if (by === 'comments') {
users = await models.$queryRawUnsafe(`
SELECT users.*, count(*) as ncomments
SELECT users.*, count(*)::INTEGER as ncomments
FROM users
JOIN "Item" on "Item"."userId" = users.id
WHERE "Item".created_at <= $1 AND "Item"."parentId" IS NOT NULL
@ -217,7 +217,7 @@ export default {
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
} else if (by === 'referrals') {
users = await models.$queryRawUnsafe(`
SELECT users.*, count(*) as referrals
SELECT users.*, count(*)::INTEGER as referrals
FROM users
JOIN "users" referree on users.id = referree."referrerId"
WHERE referree.created_at <= $1
@ -503,7 +503,7 @@ export default {
await updateItem(parent, { id: user.bioId, data: { text: bio, title: `@${user.name}'s bio` } }, { me, models })
} else {
const [item] = await serialize(models,
models.$queryRawUnsafe(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`,
models.$queryRawUnsafe(`${SELECT} FROM create_bio($1, $2, $3::INTEGER) AS "Item"`,
`@${user.name}'s bio`, bio, Number(me.id)))
await createMentions(item, models)
}
@ -579,7 +579,7 @@ export default {
return max
},
nitems: async (user, { when }, { models }) => {
if (typeof user.nitems === 'number') {
if (typeof user.nitems !== 'undefined') {
return user.nitems
}
@ -593,7 +593,7 @@ export default {
})
},
nposts: async (user, { when }, { models }) => {
if (typeof user.nposts === 'number') {
if (typeof user.nposts !== 'undefined') {
return user.nposts
}
@ -608,7 +608,7 @@ export default {
})
},
ncomments: async (user, { when }, { models }) => {
if (typeof user.ncomments === 'number') {
if (typeof user.ncomments !== 'undefined') {
return user.ncomments
}
@ -623,7 +623,7 @@ export default {
})
},
nbookmarks: async (user, { when }, { models }) => {
if (typeof user.nBookmarks === 'number') {
if (typeof user.nBookmarks !== 'undefined') {
return user.nBookmarks
}
@ -637,7 +637,7 @@ export default {
})
},
stacked: async (user, { when }, { models }) => {
if (typeof user.stacked === 'number') {
if (typeof user.stacked !== 'undefined') {
return user.stacked
}
@ -669,7 +669,7 @@ export default {
return 0
},
spent: async (user, { when }, { models }) => {
if (typeof user.spent === 'number') {
if (typeof user.spent !== 'undefined') {
return user.spent
}
@ -688,9 +688,10 @@ export default {
return (msats && msatsToSats(msats)) || 0
},
referrals: async (user, { when }, { models }) => {
if (typeof user.referrals === 'number') {
if (typeof user.referrals !== 'undefined') {
return user.referrals
}
return await models.user.count({
where: {
referrerId: user.id,

View File

@ -211,7 +211,7 @@ export default {
const [inv] = await serialize(models,
models.$queryRaw`SELECT * FROM create_invoice(${invoice.id}, ${invoice.request},
${expiresAt}, ${amount * 1000}, ${me.id}, ${description})`)
${expiresAt}, ${amount * 1000}, ${me.id}::INTEGER, ${description})`)
return inv
} catch (error) {

View File

@ -16,7 +16,7 @@ export default gql`
}
type TimeData {
time: String!
time: Date!
data: [NameValue!]!
}
`

View File

@ -15,7 +15,7 @@ import referrals from './referrals'
import price from './price'
import admin from './admin'
const link = gql`
const common = gql`
type Query {
_: Boolean
}
@ -27,7 +27,10 @@ const link = gql`
type Subscription {
_: Boolean
}
scalar JSONObject
scalar Date
`
export default [link, user, item, message, wallet, lnurl, notifications, invite,
export default [common, user, item, message, wallet, lnurl, notifications, invite,
sub, upload, growth, rewards, referrals, price, admin]

View File

@ -13,7 +13,7 @@ export default gql`
type Invite {
id: ID!
createdAt: String!
createdAt: Date!
invitees: [User!]!
gift: Int!
limit: Int

View File

@ -65,9 +65,9 @@ export default gql`
type Item {
id: ID!
createdAt: String!
updatedAt: String!
deletedAt: String
createdAt: Date!
updatedAt: Date!
deletedAt: Date
title: String
searchTitle: String
url: String
@ -87,7 +87,7 @@ export default gql`
bountyPaidTo: [Int]
sats: Int!
commentSats: Int!
lastCommentAt: String
lastCommentAt: Date
upvotes: Int!
wvotes: Float!
meSats: Int!

View File

@ -13,7 +13,7 @@ export default gql`
type LnAuth {
id: ID!
createdAt: String!
createdAt: Date!
k1: String!
pubkey: String
encodedUrl: String!
@ -22,7 +22,7 @@ export default gql`
type LnWith {
id: ID!
createdAt: String!
createdAt: Date!
k1: String!
user: User!
withdrawalId: Int

View File

@ -14,32 +14,32 @@ export default gql`
id: ID!
earnedSats: Int!
item: Item!
sortTime: String!
sortTime: Date!
}
type Reply {
id: ID!
item: Item!
sortTime: String!
sortTime: Date!
}
type Mention {
id: ID!
mention: Boolean!
item: Item!
sortTime: String!
sortTime: Date!
}
type Invitification {
id: ID!
invite: Invite!
sortTime: String!
sortTime: Date!
}
type JobChanged {
id: ID!
item: Item!
sortTime: String!
sortTime: Date!
}
type EarnSources {
@ -52,14 +52,14 @@ export default gql`
type Streak {
id: ID!
sortTime: String!
sortTime: Date!
days: Int
}
type Earn {
id: ID!
earnedSats: Int!
sortTime: String!
sortTime: Date!
sources: EarnSources
}
@ -67,12 +67,12 @@ export default gql`
id: ID!
earnedSats: Int!
invoice: Invoice!
sortTime: String!
sortTime: Date!
}
type Referral {
id: ID!
sortTime: String!
sortTime: Date!
}
union Notification = Reply | Votification | Mention
@ -80,7 +80,7 @@ export default gql`
| Streak
type Notifications {
lastChecked: String
lastChecked: Date
cursor: String
notifications: [Notification!]!
}

View File

@ -8,8 +8,8 @@ export default gql`
type Sub {
name: String!
createdAt: String!
updatedAt: String!
createdAt: Date!
updatedAt: Date!
postTypes: [String!]!
rankingType: String!
baseCost: Int!

View File

@ -1,8 +1,6 @@
import { gql } from 'graphql-tag'
export default gql`
scalar JSONObject
extend type Mutation {
getSignedPOST(type: String!, size: Int!, width: Int!, height: Int!): SignedPost!
}

View File

@ -42,7 +42,7 @@ export default gql`
type User {
id: ID!
createdAt: String!
createdAt: Date!
name: String
nitems(when: String): Int!
nposts(when: String): Int!

View File

@ -16,17 +16,17 @@ export default gql`
type Invoice {
id: ID!
createdAt: String!
createdAt: Date!
bolt11: String!
expiresAt: String!
expiresAt: Date!
cancelled: Boolean!
confirmedAt: String
confirmedAt: Date
satsReceived: Int
}
type Withdrawl {
id: ID!
createdAt: String!
createdAt: Date!
hash: String!
bolt11: String!
satsPaying: Int!
@ -40,7 +40,7 @@ export default gql`
id: ID!
factId: ID!
bolt11: String
createdAt: String!
createdAt: Date!
sats: Float!
satsFee: Float
status: String

View File

@ -34,7 +34,7 @@ const createUserFilter = (tag) => {
const createItemUrl = async ({ id }) => {
const [rootItem] = await models.$queryRawUnsafe(
'SELECT subpath(path, -LEAST(nlevel(path), $1), 1)::text AS id FROM "Item" WHERE id = $2',
'SELECT subpath(path, -LEAST(nlevel(path), $1::INTEGER), 1)::text AS id FROM "Item" WHERE id = $2::INTEGER',
COMMENT_DEPTH_LIMIT + 1, Number(id)
)
return `/items/${rootItem.id}` + (rootItem.id !== id ? `?commentId=${id}` : '')

View File

@ -126,10 +126,12 @@ export default function JobForm ({ item, sub }) {
clear
/>
</Col>
<Checkbox
label={<div className='fw-bold'>remote</div>} name='remote' hiddenLabel
groupClassName={styles.inlineCheckGroup}
/>
<Col className='d-flex ps-0' xs='auto'>
<Checkbox
label={<div className='fw-bold'>remote</div>} name='remote' hiddenLabel
groupClassName={styles.inlineCheckGroup}
/>
</Col>
</Row>
<MarkdownInput
topLevel

View File

@ -28,6 +28,10 @@ export const ITEM_SORTS = ['votes', 'comments', 'sats']
export const WHENS = ['day', 'week', 'month', 'year', 'forever']
export const ITEM_TYPES = context => {
if (context === 'jobs') {
return ['posts', 'comments', 'all', 'freebies']
}
const items = ['all', 'posts', 'comments', 'bounties', 'links', 'discussions', 'polls']
if (!context) {
items.push('bios', 'jobs')

View File

@ -21,5 +21,5 @@ export const msatsToSatsDecimal = msats => {
if (msats === null || msats === undefined) {
return null
}
return fixedDecimal(msats / 1000.0, 3)
return fixedDecimal(Number(msats) / 1000.0, 3)
}

View File

@ -172,7 +172,7 @@ export const jobSchema = object({
v => !v?.match(/\bremote\b/gi))
.when('remote', {
is: (value) => !value,
then: string().required('required').trim()
then: schema => schema.required('required').trim()
})
})

64
package-lock.json generated
View File

@ -14,7 +14,7 @@
"@graphql-tools/schema": "^10.0.0",
"@noble/curves": "^1.1.0",
"@opensearch-project/opensearch": "^2.3.1",
"@prisma/client": "^3.15.2",
"@prisma/client": "^4.16.2",
"@synonymdev/slashtags-auth": "^1.0.0-alpha.5",
"@synonymdev/slashtags-sdk": "^1.0.0-alpha.36",
"acorn": "^8.10.0",
@ -53,7 +53,7 @@
"page-metadata-parser": "^1.1.4",
"pageres": "^7.1.0",
"pg-boss": "^9.0.3",
"prisma": "^3.15.2",
"prisma": "^4.16.2",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-avatar-editor": "^13.0.0",
@ -2982,15 +2982,15 @@
}
},
"node_modules/@prisma/client": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz",
"integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.2.tgz",
"integrity": "sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==",
"hasInstallScript": true,
"dependencies": {
"@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines-version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
},
"engines": {
"node": ">=12.6"
"node": ">=14.17"
},
"peerDependencies": {
"prisma": "*"
@ -3002,15 +3002,15 @@
}
},
"node_modules/@prisma/engines": {
"version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz",
"integrity": "sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.2.tgz",
"integrity": "sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==",
"hasInstallScript": true
},
"node_modules/@prisma/engines-version": {
"version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz",
"integrity": "sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w=="
"version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz",
"integrity": "sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg=="
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
@ -15025,19 +15025,19 @@
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"node_modules/prisma": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz",
"integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.2.tgz",
"integrity": "sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==",
"hasInstallScript": true,
"dependencies": {
"@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines": "4.16.2"
},
"bin": {
"prisma": "build/index.js",
"prisma2": "build/index.js"
},
"engines": {
"node": ">=12.6"
"node": ">=14.17"
}
},
"node_modules/prismjs": {
@ -22131,22 +22131,22 @@
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
},
"@prisma/client": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz",
"integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.2.tgz",
"integrity": "sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==",
"requires": {
"@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines-version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
}
},
"@prisma/engines": {
"version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz",
"integrity": "sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg=="
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.2.tgz",
"integrity": "sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw=="
},
"@prisma/engines-version": {
"version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz",
"integrity": "sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w=="
"version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz",
"integrity": "sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg=="
},
"@protobufjs/aspromise": {
"version": "1.1.2",
@ -30717,11 +30717,11 @@
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"prisma": {
"version": "3.15.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz",
"integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==",
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.2.tgz",
"integrity": "sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==",
"requires": {
"@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines": "4.16.2"
}
},
"prismjs": {

View File

@ -15,7 +15,7 @@
"@graphql-tools/schema": "^10.0.0",
"@noble/curves": "^1.1.0",
"@opensearch-project/opensearch": "^2.3.1",
"@prisma/client": "^3.15.2",
"@prisma/client": "^4.16.2",
"@synonymdev/slashtags-auth": "^1.0.0-alpha.5",
"@synonymdev/slashtags-sdk": "^1.0.0-alpha.36",
"acorn": "^8.10.0",
@ -54,7 +54,7 @@
"page-metadata-parser": "^1.1.4",
"pageres": "^7.1.0",
"pg-boss": "^9.0.3",
"prisma": "^3.15.2",
"prisma": "^4.16.2",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-avatar-editor": "^13.0.0",

View File

@ -14,7 +14,7 @@ const apolloServer = new ApolloServer({
plugins: [{
requestDidStart (initialRequestContext) {
return {
executionDidStart (executionRequestContext) {
executionDidStart () {
return {
willResolveField ({ source, args, context, info }) {
const start = process.hrtime.bigint()
@ -28,6 +28,11 @@ const apolloServer = new ApolloServer({
console.log(`Field ${info.parentType.name}.${info.fieldName} failed with ${error}`)
}
}
},
async executionDidEnd (err) {
if (err) {
console.error('hey bud', err)
}
}
}
}

View File

@ -47,7 +47,7 @@ export default async ({ query: { username, amount, nostr } }, res) => {
await serialize(models,
models.$queryRaw`SELECT * FROM create_invoice(${invoice.id}, ${invoice.request},
${expiresAt}, ${Number(amount)}, ${user.id}, ${noteStr || description})`)
${expiresAt}, ${Number(amount)}, ${user.id}::INTEGER, ${noteStr || description})`)
return res.status(200).json({
pr: invoice.request,

View File

@ -33,7 +33,7 @@ export async function getServerSideProps ({ req, res, query: { id, error = null
// attempt to send gift
// catch any errors and just ignore them for now
await serialize(models,
models.$queryRawUnsafe('SELECT invite_drain($1, $2)', session.user.id, id))
models.$queryRawUnsafe('SELECT invite_drain($1::INTEGER, $2::INTEGER)', session.user.id, id))
} catch (e) {
console.log(e)
}

View File

@ -0,0 +1,22 @@
-- multiple create_polls
DROP FUNCTION IF EXISTS create_poll(title text, poll_cost integer, boost integer, user_id integer, options text[]);
DROP FUNCTION IF EXISTS create_poll(title text, text text, poll_cost integer, boost integer, user_id integer, options text[]);
-- multiple create_items
DROP FUNCTION IF EXISTS create_item(title text, url text, text text, boost integer, parent_id integer, user_id integer);
DROP FUNCTION IF EXISTS create_item(title text, url text, text text, boost integer, parent_id integer, user_id integer, fwd_user_id integer, spam_within interval);
-- multiple earn
DROP FUNCTION IF EXISTS earn(user_id integer, earn_msats int);
-- multiple run_auction
DROP FUNCTION IF EXISTS run_auction(item_id integer, bid integer);
-- multiple update_items
DROP FUNCTION IF EXISTS update_item(item_id integer, item_title text, item_url text, item_text text, boost integer, fwd_user_id integer);
-- multiple update_polls
DROP FUNCTION IF EXISTS update_poll(id integer, title text, text text, boost integer, options text[], fwd_user_id integer);
-- unused vote
DROP FUNCTION IF EXISTS vote(item_id integer, username text, vote_sats integer);

View File

@ -56,10 +56,6 @@ model User {
streak Int?
subs String[]
hideCowboyHat Boolean @default(false)
bio Item? @relation(fields: [bioId], references: [id])
invite Invite? @relation(fields: [inviteId], references: [id])
photo Upload? @relation(fields: [photoId], references: [id])
referrer User? @relation("referrals", fields: [referrerId], references: [id])
Bookmarks Bookmark[]
Donation Donation[]
Earn Earn[]
@ -79,6 +75,10 @@ model User {
Upload Upload[] @relation("Uploads")
nostrRelays UserNostrRelay[]
withdrawls Withdrawl[]
bio Item? @relation(fields: [bioId], references: [id])
invite Invite? @relation(fields: [inviteId], references: [id])
photo Upload? @relation(fields: [photoId], references: [id])
referrer User? @relation("referrals", fields: [referrerId], references: [id])
referrees User[] @relation("referrals")
@@index([createdAt], map: "users.created_at_index")
@ -200,6 +200,7 @@ model Message {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model Item {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map("created_at")
@ -243,15 +244,15 @@ model Item {
bountyPaidTo Int[]
upvotes Int @default(0)
weightedComments Float @default(0)
Bookmark Bookmark[]
fwdUser User? @relation("FwdItem", fields: [fwdUserId], references: [id])
parent Item? @relation("ParentChildren", fields: [parentId], references: [id])
children Item[] @relation("ParentChildren")
pin Pin? @relation(fields: [pinId], references: [id])
root Item? @relation("RootDescendant", fields: [rootId], references: [id])
descendants Item[] @relation("RootDescendant")
sub Sub? @relation(fields: [subName], references: [name])
user User @relation("UserItems", fields: [userId], references: [id], onDelete: Cascade)
Bookmark Bookmark[]
children Item[] @relation("ParentChildren")
descendants Item[] @relation("RootDescendant")
actions ItemAct[]
mentions Mention[]
PollOption PollOption[]
@ -265,8 +266,8 @@ model Item {
@@index([freebie], map: "Item.freebie_index")
@@index([maxBid], map: "Item.maxBid_index")
@@index([parentId], map: "Item.parentId_index")
@@index([path], map: "Item.path_index")
@@index([path], map: "Item.path_index0")
@@index([path], map: "Item.path_index", type: Gist)
@@index([path], map: "Item.path_index0", type: Gist)
@@index([pinId], map: "Item.pinId_index")
@@index([rootId], map: "Item.rootId_index")
@@index([statusUpdatedAt], map: "Item.statusUpdatedAt_index")

View File

@ -262,7 +262,7 @@ div[contenteditable]:disabled,
.form-control[readonly] {
background-color: var(--theme-inputDisabledBg);
border-color: var(--theme-borderColor);
color: var(--theme-color);
color: var(--theme-borderColor);
opacity: 1;
}
@ -489,7 +489,6 @@ div[contenteditable] {
font-weight: 500;
}
.dropdown-item.active {
text-shadow: 0 0 10px var(--bs-primary);
}

View File

@ -20,7 +20,7 @@ function auction ({ models }) {
// for each item, run serialized auction function
items.forEach(async item => {
await serialize(models,
models.$executeRaw`SELECT run_auction(${item.id})`)
models.$executeRaw`SELECT run_auction(${item.id}::INTEGER)`)
})
console.log('done', name)

View File

@ -21,7 +21,7 @@ function earn ({ models }) {
SELECT coalesce(sum(sats), 0) as sum
FROM "Donation"
WHERE created_at > now_utc() - INTERVAL '1 day'`
sum += donatedSum * 1000
sum += donatedSum * 1000n
/*
How earnings (used to) work:
@ -88,13 +88,13 @@ function earn ({ models }) {
const now = new Date(new Date().getTime())
// this is just a sanity check because it seems like a good idea
let total = 0
let total = 0n
// for each earner, serialize earnings
// we do this for each earner because we don't need to serialize
// all earner updates together
earners.forEach(async earner => {
const earnings = Math.floor(earner.proportion * sum)
const earnings = BigInt(Math.floor(earner.proportion * sum))
total += earnings
if (total > sum) {
console.log('total exceeds sum', name)
@ -103,8 +103,8 @@ function earn ({ models }) {
if (earnings > 0) {
await serialize(models,
models.$executeRaw`SELECT earn(${earner.userId}, ${earnings},
${now}, ${earner.type}, ${earner.id}, ${earner.rank})`)
models.$executeRaw`SELECT earn(${earner.userId}::INTEGER, ${earnings},
${now}::timestamp without time zone, ${earner.type}::"EarnType", ${earner.id}::INTEGER, ${earner.rank}::INTEGER)`)
}
})