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 rewards from './rewards'
import referrals from './referrals' import referrals from './referrals'
import price from './price' import price from './price'
import { GraphQLJSONObject } from 'graphql-type-json' import { GraphQLJSONObject as JSONObject } from 'graphql-type-json'
import admin from './admin' 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, 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 { GraphQLError } from 'graphql'
import { inviteSchema, ssValidate } from '../../lib/validate' import { inviteSchema, ssValidate } from '../../lib/validate'
import { msatsToSats } from '../../lib/format'
export default { export default {
Query: { Query: {
@ -59,7 +60,7 @@ export default {
}, },
poor: async (invite, args, { me, models }) => { poor: async (invite, args, { me, models }) => {
const user = await models.user.findUnique({ where: { id: invite.userId } }) 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) const filter = await commentFilterClause(me, models)
if (me) { if (me) {
const [{ item_comments_with_me: comments }] = await models.$queryRawUnsafe( 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 return comments
} }
const [{ item_comments: comments }] = await models.$queryRawUnsafe( 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 return comments
} }
@ -272,7 +272,7 @@ export default {
itemRepetition: async (parent, { parentId }, { me, models }) => { itemRepetition: async (parent, { parentId }, { me, models }) => {
if (!me) return 0 if (!me) return 0
// how many of the parents starting at parentId belong to me // 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)) Number(parentId), Number(me.id))
return count return count
@ -621,7 +621,7 @@ export default {
throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } }) throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } })
} }
const [item] = await serialize(models, 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))) sub || 'bitcoin', Number(id), title, text, Number(boost || 0), options, Number(fwdUser?.id)))
await createMentions(item, models) await createMentions(item, models)
@ -629,7 +629,7 @@ export default {
return item return item
} else { } else {
const [item] = await serialize(models, 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))) sub || 'bitcoin', title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id)))
await createMentions(item, models) await createMentions(item, models)
@ -659,12 +659,12 @@ export default {
} }
([item] = await serialize(models, ([item] = await serialize(models,
models.$queryRawUnsafe( 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))) Number(id), title, url, text, Number(maxBid), company, loc, remote, Number(logo), status)))
} else { } else {
([item] = await serialize(models, ([item] = await serialize(models,
models.$queryRawUnsafe( 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)))) title, url, text, Number(me.id), Number(maxBid), company, loc, remote, Number(logo))))
} }
@ -702,7 +702,7 @@ export default {
} }
await serialize(models, 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))) Number(id), Number(me.id)))
return id return id
@ -724,7 +724,7 @@ export default {
throw new GraphQLError('cannot zap your self', { extensions: { code: 'BAD_INPUT' } }) 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 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}` : ''}` 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' } }) 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 return true
} }
@ -818,7 +818,7 @@ export default {
} }
const options = await models.$queryRaw` 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" coalesce(bool_or("PollVote"."userId" = ${me?.id}), 'f') as "meVoted"
FROM "PollOption" FROM "PollOption"
LEFT JOIN "PollVote" on "PollVote"."pollOptionId" = "PollOption".id LEFT JOIN "PollVote" on "PollVote"."pollOptionId" = "PollOption".id
@ -826,6 +826,7 @@ export default {
GROUP BY "PollOption".id GROUP BY "PollOption".id
ORDER BY "PollOption".id ASC ORDER BY "PollOption".id ASC
` `
const poll = {} const poll = {}
poll.options = options poll.options = options
poll.meVoted = options.some(o => o.meVoted) poll.meVoted = options.some(o => o.meVoted)
@ -856,7 +857,9 @@ export default {
}, },
meSats: async (item, args, { me, models }) => { meSats: async (item, args, { me, models }) => {
if (!me) return 0 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({ const { _sum: { msats } } = await models.itemAct.aggregate({
_sum: { _sum: {
@ -1035,7 +1038,7 @@ export const updateItem = async (parent, { id, data: { sub, title, url, text, bo
const [item] = await serialize(models, const [item] = await serialize(models,
models.$queryRawUnsafe( 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, old.parentId ? null : sub || 'bitcoin', Number(id), title, url, text,
Number(boost || 0), bounty ? Number(bounty) : null, Number(fwdUser?.id))) 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( const [item] = await serialize(
models, models,
models.$queryRawUnsafe( 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', parentId ? null : sub || 'bitcoin',
title, title,
url, url,

View File

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

View File

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

View File

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

View File

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

View File

@ -211,7 +211,7 @@ export default {
const [inv] = await serialize(models, const [inv] = await serialize(models,
models.$queryRaw`SELECT * FROM create_invoice(${invoice.id}, ${invoice.request}, 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 return inv
} catch (error) { } catch (error) {

View File

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

View File

@ -15,7 +15,7 @@ import referrals from './referrals'
import price from './price' import price from './price'
import admin from './admin' import admin from './admin'
const link = gql` const common = gql`
type Query { type Query {
_: Boolean _: Boolean
} }
@ -27,7 +27,10 @@ const link = gql`
type Subscription { type Subscription {
_: Boolean _: 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] sub, upload, growth, rewards, referrals, price, admin]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ const createUserFilter = (tag) => {
const createItemUrl = async ({ id }) => { const createItemUrl = async ({ id }) => {
const [rootItem] = await models.$queryRawUnsafe( 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) COMMENT_DEPTH_LIMIT + 1, Number(id)
) )
return `/items/${rootItem.id}` + (rootItem.id !== id ? `?commentId=${id}` : '') return `/items/${rootItem.id}` + (rootItem.id !== id ? `?commentId=${id}` : '')

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ const apolloServer = new ApolloServer({
plugins: [{ plugins: [{
requestDidStart (initialRequestContext) { requestDidStart (initialRequestContext) {
return { return {
executionDidStart (executionRequestContext) { executionDidStart () {
return { return {
willResolveField ({ source, args, context, info }) { willResolveField ({ source, args, context, info }) {
const start = process.hrtime.bigint() const start = process.hrtime.bigint()
@ -28,6 +28,11 @@ const apolloServer = new ApolloServer({
console.log(`Field ${info.parentType.name}.${info.fieldName} failed with ${error}`) 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, await serialize(models,
models.$queryRaw`SELECT * FROM create_invoice(${invoice.id}, ${invoice.request}, 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({ return res.status(200).json({
pr: invoice.request, pr: invoice.request,

View File

@ -33,7 +33,7 @@ export async function getServerSideProps ({ req, res, query: { id, error = null
// attempt to send gift // attempt to send gift
// catch any errors and just ignore them for now // catch any errors and just ignore them for now
await serialize(models, 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) { } catch (e) {
console.log(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? streak Int?
subs String[] subs String[]
hideCowboyHat Boolean @default(false) 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[] Bookmarks Bookmark[]
Donation Donation[] Donation Donation[]
Earn Earn[] Earn Earn[]
@ -79,6 +75,10 @@ model User {
Upload Upload[] @relation("Uploads") Upload Upload[] @relation("Uploads")
nostrRelays UserNostrRelay[] nostrRelays UserNostrRelay[]
withdrawls Withdrawl[] 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") referrees User[] @relation("referrals")
@@index([createdAt], map: "users.created_at_index") @@index([createdAt], map: "users.created_at_index")
@ -200,6 +200,7 @@ model Message {
user User @relation(fields: [userId], references: [id], onDelete: Cascade) 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 { model Item {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
@ -243,15 +244,15 @@ model Item {
bountyPaidTo Int[] bountyPaidTo Int[]
upvotes Int @default(0) upvotes Int @default(0)
weightedComments Float @default(0) weightedComments Float @default(0)
Bookmark Bookmark[]
fwdUser User? @relation("FwdItem", fields: [fwdUserId], references: [id]) fwdUser User? @relation("FwdItem", fields: [fwdUserId], references: [id])
parent Item? @relation("ParentChildren", fields: [parentId], references: [id]) parent Item? @relation("ParentChildren", fields: [parentId], references: [id])
children Item[] @relation("ParentChildren")
pin Pin? @relation(fields: [pinId], references: [id]) pin Pin? @relation(fields: [pinId], references: [id])
root Item? @relation("RootDescendant", fields: [rootId], references: [id]) root Item? @relation("RootDescendant", fields: [rootId], references: [id])
descendants Item[] @relation("RootDescendant")
sub Sub? @relation(fields: [subName], references: [name]) sub Sub? @relation(fields: [subName], references: [name])
user User @relation("UserItems", fields: [userId], references: [id], onDelete: Cascade) user User @relation("UserItems", fields: [userId], references: [id], onDelete: Cascade)
Bookmark Bookmark[]
children Item[] @relation("ParentChildren")
descendants Item[] @relation("RootDescendant")
actions ItemAct[] actions ItemAct[]
mentions Mention[] mentions Mention[]
PollOption PollOption[] PollOption PollOption[]
@ -265,8 +266,8 @@ model Item {
@@index([freebie], map: "Item.freebie_index") @@index([freebie], map: "Item.freebie_index")
@@index([maxBid], map: "Item.maxBid_index") @@index([maxBid], map: "Item.maxBid_index")
@@index([parentId], map: "Item.parentId_index") @@index([parentId], map: "Item.parentId_index")
@@index([path], map: "Item.path_index") @@index([path], map: "Item.path_index", type: Gist)
@@index([path], map: "Item.path_index0") @@index([path], map: "Item.path_index0", type: Gist)
@@index([pinId], map: "Item.pinId_index") @@index([pinId], map: "Item.pinId_index")
@@index([rootId], map: "Item.rootId_index") @@index([rootId], map: "Item.rootId_index")
@@index([statusUpdatedAt], map: "Item.statusUpdatedAt_index") @@index([statusUpdatedAt], map: "Item.statusUpdatedAt_index")

View File

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

View File

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

View File

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