diff --git a/api/resolvers/growth.js b/api/resolvers/growth.js index c71c7ce6..771d278a 100644 --- a/api/resolvers/growth.js +++ b/api/resolvers/growth.js @@ -65,7 +65,7 @@ export default { Query: { registrationGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'referrals', 'value', sum(referrals)), json_build_object('name', 'organic', 'value', sum(organic)) @@ -76,7 +76,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'referrals', 'value', count("referrerId")), @@ -89,7 +89,7 @@ export default { }, spenderGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'any', 'value', floor(avg("any"))), json_build_object('name', 'jobs', 'value', floor(avg(jobs))), @@ -104,7 +104,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'any', 'value', count(DISTINCT "userId")), @@ -128,7 +128,7 @@ export default { }, itemGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'posts', 'value', sum(posts)), json_build_object('name', 'comments', 'value', sum(comments)), @@ -140,7 +140,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'comments', 'value', count("parentId")), @@ -154,7 +154,7 @@ export default { }, spendingGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'jobs', 'value', sum(jobs)), json_build_object('name', 'boost', 'value', sum(boost)), @@ -168,7 +168,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'jobs', 'value', coalesce(floor(sum(CASE WHEN act = 'STREAM' THEN msats ELSE 0 END)/1000),0)), @@ -191,7 +191,7 @@ export default { }, stackerGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'any', 'value', floor(avg("any"))), json_build_object('name', 'posts', 'value', floor(avg(posts))), @@ -205,7 +205,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'any', 'value', count(distinct user_id)), @@ -233,7 +233,7 @@ export default { }, stackingGrowth: async (parent, { when }, { models }) => { if (when !== 'day') { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT date_trunc('${timeUnit(when)}', day) as time, json_build_array( json_build_object('name', 'rewards', 'value', sum(rewards)), json_build_object('name', 'posts', 'value', sum(posts)), @@ -246,7 +246,7 @@ export default { ORDER BY time ASC`) } - return await models.$queryRaw( + return await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'rewards', 'value', coalesce(floor(sum(airdrop)/1000),0)), diff --git a/api/resolvers/item.js b/api/resolvers/item.js index b757036d..e894f336 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -52,12 +52,12 @@ async function comments (me, models, id, sort) { const filter = await commentFilterClause(me, models) if (me) { - const [{ item_comments_with_me: comments }] = await models.$queryRaw( + 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) return comments } - const [{ item_comments: comments }] = await models.$queryRaw( + const [{ item_comments: comments }] = await models.$queryRawUnsafe( 'SELECT item_comments($1, $2, $3, $4)', Number(id), COMMENT_DEPTH_LIMIT, filter, orderBy) return comments } @@ -211,7 +211,7 @@ function typeClause (type) { // joining does not preserve the order of the inner query async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args) { if (!me) { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT "Item".*, to_json(users.*) as user FROM ( ${query} @@ -219,7 +219,7 @@ async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args) JOIN users ON "Item"."userId" = users.id ${orderBy}`, ...args) } else { - return await models.$queryRaw(` + return await models.$queryRawUnsafe(` SELECT "Item".*, to_json(users.*) as user, COALESCE("ItemAct"."meMsats", 0) as "meMsats", COALESCE("ItemAct"."meDontLike", false) as "meDontLike", "Bookmark"."itemId" IS NOT NULL AS "meBookmark", "ThreadSubscription"."itemId" IS NOT NULL AS "meSubscription" @@ -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.$queryRaw(`SELECT item_spam($1, $2, '${ITEM_SPAM_INTERVAL}')`, + const [{ item_spam: count }] = await models.$queryRawUnsafe(`SELECT item_spam($1, $2, '${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.$queryRaw(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6, $7) AS "Item"`, + models.$queryRawUnsafe(`${SELECT} FROM update_poll($1, $2, $3, $4, $5, $6, $7) 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.$queryRaw(`${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, $5, $6, $7, $8, '${ITEM_SPAM_INTERVAL}') AS "Item"`, sub || 'bitcoin', title, text, 1, Number(boost || 0), Number(me.id), options, Number(fwdUser?.id))) await createMentions(item, models) @@ -658,12 +658,12 @@ export default { throw new GraphQLError('item does not belong to you', { extensions: { code: 'FORBIDDEN' } }) } ([item] = await serialize(models, - models.$queryRaw( + models.$queryRawUnsafe( `${SELECT} FROM update_job($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) AS "Item"`, Number(id), title, url, text, Number(maxBid), company, loc, remote, Number(logo), status))) } else { ([item] = await serialize(models, - models.$queryRaw( + models.$queryRawUnsafe( `${SELECT} FROM create_job($1, $2, $3, $4, $5, $6, $7, $8, $9) AS "Item"`, title, url, text, Number(me.id), Number(maxBid), company, loc, remote, Number(logo)))) } @@ -678,7 +678,7 @@ export default { // fetch user to get up-to-date name const user = await models.user.findUnique({ where: { id: me.id } }) - const parents = await models.$queryRaw( + const parents = await models.$queryRawUnsafe( 'SELECT DISTINCT p."userId" FROM "Item" i JOIN "Item" p ON p.path @> i.path WHERE i.id = $1 and p."userId" <> $2', Number(item.parentId), Number(me.id)) Promise.allSettled( @@ -702,7 +702,7 @@ export default { } await serialize(models, - models.$queryRaw(`${SELECT} FROM poll_vote($1, $2) AS "Item"`, + models.$queryRawUnsafe(`${SELECT} FROM poll_vote($1, $2) AS "Item"`, Number(id), Number(me.id))) return id @@ -716,7 +716,7 @@ export default { await ssValidate(amountSchema, { amount: sats }) // disallow self tips - const [item] = await models.$queryRaw(` + const [item] = await models.$queryRawUnsafe(` ${SELECT} FROM "Item" WHERE id = $1 AND "userId" = $2`, Number(id), me.id) @@ -747,7 +747,7 @@ export default { } // disallow self down votes - const [item] = await models.$queryRaw(` + const [item] = await models.$queryRawUnsafe(` ${SELECT} FROM "Item" WHERE id = $1 AND "userId" = $2`, Number(id), me.id) @@ -858,8 +858,8 @@ export default { if (!me) return 0 if (typeof item.meMsats === 'number') return msatsToSats(item.meMsats) - const { sum: { msats } } = await models.itemAct.aggregate({ - sum: { + const { _sum: { msats } } = await models.itemAct.aggregate({ + _sum: { msats: true }, where: { @@ -1034,7 +1034,7 @@ export const updateItem = async (parent, { id, data: { sub, title, url, text, bo text = await proxyImages(text) const [item] = await serialize(models, - models.$queryRaw( + models.$queryRawUnsafe( `${SELECT} FROM update_item($1, $2, $3, $4, $5, $6, $7, $8) AS "Item"`, old.parentId ? null : sub || 'bitcoin', Number(id), title, url, text, Number(boost || 0), bounty ? Number(bounty) : null, Number(fwdUser?.id))) @@ -1070,7 +1070,7 @@ const createItem = async (parent, { sub, title, url, text, boost, forward, bount const [item] = await serialize( models, - models.$queryRaw( + models.$queryRawUnsafe( `${SELECT} FROM create_item($1, $2, $3, $4, $5, $6, $7, $8, $9, '${ITEM_SPAM_INTERVAL}') AS "Item"`, parentId ? null : sub || 'bitcoin', title, diff --git a/api/resolvers/notifications.js b/api/resolvers/notifications.js index b53c9e03..26a7d8b7 100644 --- a/api/resolvers/notifications.js +++ b/api/resolvers/notifications.js @@ -194,7 +194,7 @@ export default { } // we do all this crazy subquery stuff to make 'reward' islands - const notifications = await models.$queryRaw( + const notifications = await models.$queryRawUnsafe( `SELECT MAX(id) AS id, MAX("sortTime") AS "sortTime", sum("earnedSats") AS "earnedSats", type, MIN("sortTime") AS "minSortTime" FROM @@ -286,7 +286,7 @@ export default { }, Earn: { sources: async (n, args, { me, models }) => { - const [sources] = await models.$queryRaw(` + const [sources] = await models.$queryRawUnsafe(` SELECT FLOOR(sum(msats) FILTER(WHERE type = 'POST') / 1000) AS posts, FLOOR(sum(msats) FILTER(WHERE type = 'COMMENT') / 1000) AS comments, diff --git a/api/resolvers/referrals.js b/api/resolvers/referrals.js index 2b8cf6bc..86cb4022 100644 --- a/api/resolvers/referrals.js +++ b/api/resolvers/referrals.js @@ -8,21 +8,21 @@ export default { throw new GraphQLError('you must be logged in', { extensions: { code: 'UNAUTHENTICATED' } }) } - const [{ totalSats }] = await models.$queryRaw(` + const [{ totalSats }] = await models.$queryRawUnsafe(` SELECT COALESCE(FLOOR(sum(msats) / 1000), 0) as "totalSats" FROM "ReferralAct" WHERE ${intervalClause(when, 'ReferralAct', true)} "ReferralAct"."referrerId" = $1 `, Number(me.id)) - const [{ totalReferrals }] = await models.$queryRaw(` + const [{ totalReferrals }] = await models.$queryRawUnsafe(` SELECT count(*) as "totalReferrals" FROM users WHERE ${intervalClause(when, 'users', true)} "referrerId" = $1 `, Number(me.id)) - const stats = await models.$queryRaw( + const stats = await models.$queryRawUnsafe( `${withClause(when)} SELECT time, json_build_array( json_build_object('name', 'referrals', 'value', count(*) FILTER (WHERE act = 'REFERREE')), diff --git a/api/resolvers/rewards.js b/api/resolvers/rewards.js index c2da8779..7930f599 100644 --- a/api/resolvers/rewards.js +++ b/api/resolvers/rewards.js @@ -42,7 +42,7 @@ export default { await ssValidate(amountSchema, { amount: sats }) await serialize(models, - models.$queryRaw( + models.$queryRawUnsafe( 'SELECT donate($1, $2)', sats, Number(me.id))) diff --git a/api/resolvers/serial.js b/api/resolvers/serial.js index d6a94e47..b369a71f 100644 --- a/api/resolvers/serial.js +++ b/api/resolvers/serial.js @@ -5,7 +5,7 @@ async function serialize (models, call) { return await retry(async bail => { try { const [, result] = await models.$transaction([ - models.$executeRaw(SERIALIZE), + models.$executeRawUnsafe(SERIALIZE), call ]) return result diff --git a/api/resolvers/user.js b/api/resolvers/user.js index 661ea79d..7c1fbd57 100644 --- a/api/resolvers/user.js +++ b/api/resolvers/user.js @@ -119,7 +119,7 @@ export default { }, topCowboys: async (parent, { cursor }, { models, me }) => { const decodedCursor = decodeCursor(cursor) - const users = await models.$queryRaw(` + const users = await models.$queryRawUnsafe(` SELECT users.*, floor(sum(msats_spent)/1000) as spent, sum(posts) as nposts, sum(comments) as ncomments, sum(referrals) as referrals, floor(sum(msats_stacked)/1000) as stacked @@ -149,7 +149,7 @@ export default { default: column = 'stacked'; break } - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` WITH u AS ( SELECT users.*, floor(sum(msats_spent)/1000) as spent, sum(posts) as nposts, sum(comments) as ncomments, sum(referrals) as referrals, @@ -172,7 +172,7 @@ export default { } if (by === 'spent') { - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` SELECT users.*, sum(sats_spent) as spent FROM ((SELECT "userId", floor(sum("ItemAct".msats)/1000) as sats_spent @@ -192,7 +192,7 @@ export default { OFFSET $2 LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset) } else if (by === 'posts') { - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` SELECT users.*, count(*) as nposts FROM users JOIN "Item" on "Item"."userId" = users.id @@ -204,7 +204,7 @@ export default { OFFSET $2 LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset) } else if (by === 'comments') { - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` SELECT users.*, count(*) as ncomments FROM users JOIN "Item" on "Item"."userId" = users.id @@ -216,7 +216,7 @@ export default { OFFSET $2 LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset) } else if (by === 'referrals') { - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` SELECT users.*, count(*) as referrals FROM users JOIN "users" referree on users.id = referree."referrerId" @@ -228,7 +228,7 @@ export default { OFFSET $2 LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset) } else { - users = await models.$queryRaw(` + users = await models.$queryRawUnsafe(` SELECT u.id, u.name, u.streak, u."photoId", u."hideCowboyHat", floor(sum(amount)/1000) as stacked FROM ((SELECT users.*, "ItemAct".msats as amount @@ -270,7 +270,7 @@ export default { // check if any votes have been cast for them since checkedNotesAt if (user.noteItemSats) { - const votes = await models.$queryRaw(` + const votes = await models.$queryRawUnsafe(` SELECT 1 FROM "Item" JOIN "ItemAct" ON @@ -286,7 +286,7 @@ export default { } // check if they have any replies since checkedNotesAt - const newReplies = await models.$queryRaw(` + const newReplies = await models.$queryRawUnsafe(` SELECT 1 FROM "Item" JOIN "Item" p ON @@ -302,7 +302,7 @@ export default { } // break out thread subscription to decrease the search space of the already expensive reply query - const newtsubs = await models.$queryRaw(` + const newtsubs = await models.$queryRawUnsafe(` SELECT 1 FROM "ThreadSubscription" JOIN "Item" p ON "ThreadSubscription"."itemId" = p.id @@ -318,7 +318,7 @@ export default { // check if they have any mentions since checkedNotesAt if (user.noteMentions) { - const newMentions = await models.$queryRaw(` + const newMentions = await models.$queryRawUnsafe(` SELECT "Item".id, "Item".created_at FROM "Mention" JOIN "Item" ON "Mention"."itemId" = "Item".id @@ -379,7 +379,7 @@ export default { // check if new invites have been redeemed if (user.noteInvites) { - const newInvitees = await models.$queryRaw(` + const newInvitees = await models.$queryRawUnsafe(` SELECT "Invite".id FROM users JOIN "Invite" on users."inviteId" = "Invite".id WHERE "Invite"."userId" = $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.$queryRaw(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`, + models.$queryRawUnsafe(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`, `@${user.name}'s bio`, bio, Number(me.id))) await createMentions(item, models) } @@ -645,7 +645,7 @@ export default { // forever return (user.stackedMsats && msatsToSats(user.stackedMsats)) || 0 } else if (when === 'day') { - const [{ stacked }] = await models.$queryRaw(` + const [{ stacked }] = await models.$queryRawUnsafe(` SELECT sum(amount) as stacked FROM ((SELECT coalesce(sum("ItemAct".msats),0) as amount @@ -673,8 +673,8 @@ export default { return user.spent } - const { sum: { msats } } = await models.itemAct.aggregate({ - sum: { + const { _sum: { msats } } = await models.itemAct.aggregate({ + _sum: { msats: true }, where: { diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index d72c9ccf..c6b531c6 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -145,7 +145,7 @@ export default { } } - let history = await models.$queryRaw(` + let history = await models.$queryRawUnsafe(` ${queries.join(' UNION ALL ')} ORDER BY "createdAt" DESC OFFSET $3 @@ -285,7 +285,7 @@ export default { if (fact.type !== 'spent' && fact.type !== 'stacked') { return null } - const [item] = await models.$queryRaw(` + const [item] = await models.$queryRawUnsafe(` ${SELECT} FROM "Item" WHERE id = $1`, Number(fact.factId)) diff --git a/api/webPush/index.js b/api/webPush/index.js index ca395491..c8a3cacb 100644 --- a/api/webPush/index.js +++ b/api/webPush/index.js @@ -33,7 +33,7 @@ const createUserFilter = (tag) => { } const createItemUrl = async ({ id }) => { - const [rootItem] = await models.$queryRaw( + const [rootItem] = await models.$queryRawUnsafe( 'SELECT subpath(path, -LEAST(nlevel(path), $1), 1)::text AS id FROM "Item" WHERE id = $2', COMMENT_DEPTH_LIMIT + 1, Number(id) ) diff --git a/package-lock.json b/package-lock.json index ada535be..1af3ffe7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@graphql-tools/schema": "^10.0.0", "@noble/curves": "^1.1.0", "@opensearch-project/opensearch": "^2.3.1", - "@prisma/client": "^2.30.3", + "@prisma/client": "^3.15.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": "^2.30.3", + "prisma": "^3.15.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": "2.30.3", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.30.3.tgz", - "integrity": "sha512-Ey2miZ+Hne12We3rA8XrlPoAF0iuKEhw5IK2nropaelSt0Ju3b2qSz9Qt50a/1Mx3+7yRSu/iSXt8y9TUMl/Yw==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz", + "integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + "@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" }, "engines": { - "node": ">=12.2" + "node": ">=12.6" }, "peerDependencies": { "prisma": "*" @@ -3002,15 +3002,15 @@ } }, "node_modules/@prisma/engines": { - "version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz", - "integrity": "sha512-WPnA/IUrxDihrRhdP6+8KAVSwsc0zsh8ioPYsLJjOhzVhwpRbuFH2tJDRIAbc+qFh+BbTIZbeyBYt8fpNXaYQQ==", + "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==", "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz", - "integrity": "sha512-/iDRgaoSQC77WN2oDsOM8dn61fykm6tnZUAClY+6p+XJbOEgZ9gy4CKuKTBgrjSGDVjtQ/S2KGcYd3Ring8xaw==" + "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==" }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", @@ -15025,19 +15025,19 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "2.30.3", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-2.30.3.tgz", - "integrity": "sha512-48qYba2BIyUmXuosBZs0g3kYGrxKvo4VkSHYOuLlDdDirmKyvoY2hCYMUYHSx3f++8ovfgs+MX5KmNlP+iAZrQ==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz", + "integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + "@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" }, "bin": { "prisma": "build/index.js", "prisma2": "build/index.js" }, "engines": { - "node": ">=12.2" + "node": ">=12.6" } }, "node_modules/prismjs": { @@ -22131,22 +22131,22 @@ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@prisma/client": { - "version": "2.30.3", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.30.3.tgz", - "integrity": "sha512-Ey2miZ+Hne12We3rA8XrlPoAF0iuKEhw5IK2nropaelSt0Ju3b2qSz9Qt50a/1Mx3+7yRSu/iSXt8y9TUMl/Yw==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.15.2.tgz", + "integrity": "sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==", "requires": { - "@prisma/engines-version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + "@prisma/engines-version": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" } }, "@prisma/engines": { - "version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz", - "integrity": "sha512-WPnA/IUrxDihrRhdP6+8KAVSwsc0zsh8ioPYsLJjOhzVhwpRbuFH2tJDRIAbc+qFh+BbTIZbeyBYt8fpNXaYQQ==" + "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==" }, "@prisma/engines-version": { - "version": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz", - "integrity": "sha512-/iDRgaoSQC77WN2oDsOM8dn61fykm6tnZUAClY+6p+XJbOEgZ9gy4CKuKTBgrjSGDVjtQ/S2KGcYd3Ring8xaw==" + "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==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -30717,11 +30717,11 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "prisma": { - "version": "2.30.3", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-2.30.3.tgz", - "integrity": "sha512-48qYba2BIyUmXuosBZs0g3kYGrxKvo4VkSHYOuLlDdDirmKyvoY2hCYMUYHSx3f++8ovfgs+MX5KmNlP+iAZrQ==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.15.2.tgz", + "integrity": "sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==", "requires": { - "@prisma/engines": "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + "@prisma/engines": "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" } }, "prismjs": { diff --git a/package.json b/package.json index 64ed4cad..8c29d39e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@graphql-tools/schema": "^10.0.0", "@noble/curves": "^1.1.0", "@opensearch-project/opensearch": "^2.3.1", - "@prisma/client": "^2.30.3", + "@prisma/client": "^3.15.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": "^2.30.3", + "prisma": "^3.15.2", "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-avatar-editor": "^13.0.0", diff --git a/pages/api/daily.js b/pages/api/daily.js index 0d3936cb..c9cd776c 100644 --- a/pages/api/daily.js +++ b/pages/api/daily.js @@ -5,7 +5,7 @@ export default async (_, res) => { // this should probably be made more generic // eg if the title changes this will break // ... but this will need to change when we have more subs anyway - const [{ id }] = await models.$queryRaw(` + const [{ id }] = await models.$queryRawUnsafe(` SELECT id FROM "Item" WHERE "pinId" IS NOT NULL diff --git a/pages/invites/[id].js b/pages/invites/[id].js index 35ee753d..5891f624 100644 --- a/pages/invites/[id].js +++ b/pages/invites/[id].js @@ -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.$queryRaw('SELECT invite_drain($1, $2)', session.user.id, id)) + models.$queryRawUnsafe('SELECT invite_drain($1, $2)', session.user.id, id)) } catch (e) { console.log(e) } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 832d2b5a..edadac4b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,169 +1,503 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema +generator client { + provider = "prisma-client-js" +} datasource db { provider = "postgresql" url = env("DATABASE_URL") } -generator client { - provider = "prisma-client-js" -} - model Snl { id Int @id @default(autoincrement()) live Boolean @default(false) } model User { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - name String? @unique @db.Citext - email String? @unique - emailVerified DateTime? @map(name: "email_verified") - image String? - items Item[] @relation("UserItems") - fwdItems Item[] @relation("FwdItem") - mentions Mention[] - messages Message[] - actions ItemAct[] - invoices Invoice[] - withdrawls Withdrawl[] - invites Invite[] @relation(name: "Invites") - invite Invite? @relation(fields: [inviteId], references: [id]) - inviteId String? - bio Item? @relation(fields: [bioId], references: [id]) - bioId Int? - msats BigInt @default(0) - stackedMsats BigInt @default(0) - freeComments Int @default(5) - freePosts Int @default(2) - checkedNotesAt DateTime? - fiatCurrency String @default("USD") - pubkey String? @unique - slashtagId String? @unique - trust Float @default(0) - upvoteTrust Float @default(0) - lastSeenAt DateTime? - lastCheckedJobs DateTime? - photoId Int? - photo Upload? @relation(fields: [photoId], references: [id]) - subs String[] - - // streak - streak Int? - - // walkthrough - upvotePopover Boolean @default(false) - tipPopover Boolean @default(false) - - // nostr - nostrPubkey String? - nostrRelays UserNostrRelay[] - - // referrals - referrer User? @relation("referrals", fields: [referrerId], references: [id]) - referrerId Int? - referrees User[] @relation("referrals") - - // tip settings - tipDefault Int @default(100) - turboTipping Boolean @default(false) - - // notification settings - noteItemSats Boolean @default(true) - noteEarning Boolean @default(true) - noteAllDescendants Boolean @default(true) - noteMentions Boolean @default(true) - noteDeposits Boolean @default(true) - noteInvites Boolean @default(true) - noteJobIndicator Boolean @default(true) - noteCowboyHat Boolean @default(true) - - // privacy settings - hideInvoiceDesc Boolean @default(false) - hideFromTopUsers Boolean @default(false) - hideCowboyHat Boolean @default(false) - - // content settings - wildWestMode Boolean @default(false) - greeterMode Boolean @default(false) - - Earn Earn[] - Upload Upload[] @relation(name: "Uploads") - PollVote PollVote[] + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + name String? @unique(map: "users.name_unique") @db.Citext + email String? @unique(map: "users.email_unique") + emailVerified DateTime? @map("email_verified") + image String? + msats BigInt @default(0) + freeComments Int @default(5) + freePosts Int @default(2) + checkedNotesAt DateTime? + pubkey String? @unique(map: "users.pubkey_unique") + tipDefault Int @default(100) + bioId Int? + inviteId String? + tipPopover Boolean @default(false) + upvotePopover Boolean @default(false) + trust Float @default(0) + lastSeenAt DateTime? + stackedMsats BigInt @default(0) + noteAllDescendants Boolean @default(true) + noteDeposits Boolean @default(true) + noteEarning Boolean @default(true) + noteInvites Boolean @default(true) + noteItemSats Boolean @default(true) + noteMentions Boolean @default(true) + lastCheckedJobs DateTime? + noteJobIndicator Boolean @default(true) + photoId Int? + upvoteTrust Float @default(0) + hideInvoiceDesc Boolean @default(false) + wildWestMode Boolean @default(false) + greeterMode Boolean @default(false) + fiatCurrency String @default("USD") + hideFromTopUsers Boolean @default(false) + turboTipping Boolean @default(false) + referrerId Int? + nostrPubkey String? + slashtagId String? @unique(map: "users.slashtagId_unique") + noteCowboyHat Boolean @default(true) + 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[] + invites Invite[] @relation("Invites") + invoices Invoice[] + fwdItems Item[] @relation("FwdItem") + items Item[] @relation("UserItems") + actions ItemAct[] + mentions Mention[] + messages Message[] + PollVote PollVote[] + PushSubscriptions PushSubscription[] ReferralAct ReferralAct[] Streak Streak[] - Bookmarks Bookmark[] Subscriptions Subscription[] ThreadSubscriptions ThreadSubscription[] - PushSubscriptions PushSubscription[] + Upload Upload[] @relation("Uploads") + nostrRelays UserNostrRelay[] + withdrawls Withdrawl[] + referrees User[] @relation("referrals") - @@index([createdAt]) - @@index([inviteId]) - @@map(name: "users") + @@index([createdAt], map: "users.created_at_index") + @@index([inviteId], map: "users.inviteId_index") + @@map("users") } model Streak { id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") startedAt DateTime @db.Date endedAt DateTime? @db.Date userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@unique([startedAt, userId]) - @@index([userId]) + @@unique([startedAt, userId], map: "Streak.startedAt_userId_unique") + @@index([userId], map: "Streak.userId_index") } model NostrRelay { addr String @id - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") users UserNostrRelay[] } model UserNostrRelay { - User User @relation(fields: [userId], references: [id]) userId Int - NostrRelay NostrRelay @relation(fields: [nostrRelayAddr], references: [addr]) nostrRelayAddr String - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + NostrRelay NostrRelay @relation(fields: [nostrRelayAddr], references: [addr], onDelete: Cascade) + User User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([userId, nostrRelayAddr]) } model Donation { id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") sats Int userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Upload { id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") type String size Int width Int? height Int? - item Item? @relation(fields: [itemId], references: [id]) - itemId Int? @unique - user User @relation(name: "Uploads", fields: [userId], references: [id]) + itemId Int? @unique(map: "Upload.itemId_unique") userId Int + item Item? @relation(fields: [itemId], references: [id]) + user User @relation("Uploads", fields: [userId], references: [id], onDelete: Cascade) + User User[] - User User[] + @@index([createdAt], map: "Upload.created_at_index") + @@index([itemId], map: "Upload.itemId_index") + @@index([userId], map: "Upload.userId_index") +} - @@index([createdAt]) - @@index([itemId]) - @@index([userId]) +model Earn { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + msats BigInt + userId Int + rank Int? + type EarnType? + typeId Int? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([createdAt], map: "Earn.created_at_index") + @@index([createdAt, userId], map: "Earn.created_at_userId_index") + @@index([userId], map: "Earn.userId_index") +} + +model LnAuth { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + k1 String @unique(map: "LnAuth.k1_unique") + pubkey String? +} + +model LnWith { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + k1 String @unique(map: "LnWith.k1_unique") + userId Int + withdrawalId Int? +} + +model Invite { + id String @id @default(cuid()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + userId Int + gift Int? + limit Int? + revoked Boolean @default(false) + user User @relation("Invites", fields: [userId], references: [id], onDelete: Cascade) + invitees User[] + + @@index([createdAt], map: "Invite.created_at_index") + @@index([userId], map: "Invite.userId_index") +} + +model Message { + id Int @id @default(autoincrement()) + text String + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model Item { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + title String? + text String? + url String? + userId Int + parentId Int? + path Unsupported("ltree")? + pinId Int? + latitude Float? + location String? + longitude Float? + maxBid Int? + maxSalary Int? + minSalary Int? + remote Boolean? + subName String? @db.Citext + statusUpdatedAt DateTime? + status Status @default(ACTIVE) + company String? + fwdUserId Int? + weightedVotes Float @default(0) + boost Int @default(0) + uploadId Int? + pollCost Int? + paidImgLink Boolean @default(false) + commentMsats BigInt @default(0) + lastCommentAt DateTime? + ncomments Int @default(0) + msats BigInt @default(0) + weightedDownVotes Float @default(0) + bio Boolean @default(false) + freebie Boolean @default(false) + deletedAt DateTime? + otsFile Bytes? + otsHash String? + bounty Int? + rootId Int? + bountyPaidTo Int[] + upvotes Int @default(0) + weightedComments Float @default(0) + fwdUser User? @relation("FwdItem", fields: [fwdUserId], references: [id]) + parent Item? @relation("ParentChildren", fields: [parentId], references: [id]) + pin Pin? @relation(fields: [pinId], references: [id]) + root Item? @relation("RootDescendant", fields: [rootId], references: [id]) + 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[] + PollVote PollVote[] + ThreadSubscription ThreadSubscription[] + upload Upload? + User User[] + + @@index([bio], map: "Item.bio_index") + @@index([createdAt], map: "Item.created_at_index") + @@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([pinId], map: "Item.pinId_index") + @@index([rootId], map: "Item.rootId_index") + @@index([statusUpdatedAt], map: "Item.statusUpdatedAt_index") + @@index([status], map: "Item.status_index") + @@index([subName], map: "Item.subName_index") + @@index([userId], map: "Item.userId_index") + @@index([weightedDownVotes], map: "Item.weightedDownVotes_index") + @@index([weightedVotes], map: "Item.weightedVotes_index") +} + +model PollOption { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + itemId Int + option String + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + PollVote PollVote[] + + @@index([itemId], map: "PollOption.itemId_index") +} + +model PollVote { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + userId Int + itemId Int + pollOptionId Int + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + pollOption PollOption @relation(fields: [pollOptionId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([itemId, userId], map: "PollVote.itemId_userId_unique") + @@index([pollOptionId], map: "PollVote.pollOptionId_index") + @@index([userId], map: "PollVote.userId_index") +} + +model Sub { + name String @id @db.Citext + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + postTypes PostType[] + rankingType RankingType + baseCost Int @default(1) + desc String? + Item Item[] + Subscription Subscription[] +} + +model Subscription { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + subName String @db.Citext + userId Int + sub Sub @relation(fields: [subName], references: [name], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model Pin { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + cron String? + timezone String? + position Int + Item Item[] +} + +model ReferralAct { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + referrerId Int + itemActId Int + msats BigInt + itemAct ItemAct @relation(fields: [itemActId], references: [id], onDelete: Cascade) + referrer User @relation(fields: [referrerId], references: [id], onDelete: Cascade) +} + +model ItemAct { + id Int @id(map: "Vote_pkey") @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + msats BigInt + act ItemActType + itemId Int + userId Int + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + ReferralAct ReferralAct[] + + @@index([act], map: "ItemAct.act_index") + @@index([createdAt], map: "ItemAct.created_at_index") + @@index([createdAt, itemId, act], map: "ItemAct.created_at_itemId_act_index") + @@index([itemId, createdAt, act], map: "ItemAct.itemId_created_at_act_index") + @@index([itemId], map: "ItemAct.itemId_index") + @@index([itemId, userId, act], map: "ItemAct.itemId_userId_act_index") + @@index([userId, createdAt, act], map: "ItemAct.userId_created_at_act_index") + @@index([userId], map: "ItemAct.userId_index") + @@index([itemId], map: "Vote.itemId_index") + @@index([userId], map: "Vote.userId_index") +} + +model Mention { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + itemId Int + userId Int + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([itemId, userId], map: "Mention.itemId_userId_unique") + @@index([createdAt], map: "Mention.created_at_index") + @@index([itemId], map: "Mention.itemId_index") + @@index([userId], map: "Mention.userId_index") +} + +model Invoice { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + userId Int + hash String @unique(map: "Invoice.hash_unique") + bolt11 String + expiresAt DateTime + confirmedAt DateTime? + cancelled Boolean @default(false) + msatsRequested BigInt + msatsReceived BigInt? + desc String? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([createdAt], map: "Invoice.created_at_index") + @@index([userId], map: "Invoice.userId_index") +} + +model Withdrawl { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + userId Int + hash String + bolt11 String + msatsPaying BigInt + msatsPaid BigInt? + msatsFeePaying BigInt + msatsFeePaid BigInt? + status WithdrawlStatus? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([createdAt], map: "Withdrawl.created_at_index") + @@index([userId], map: "Withdrawl.userId_index") +} + +model Account { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + compoundId String @unique(map: "accounts.compound_id_unique") @map("compound_id") + userId Int @map("user_id") + providerType String @map("provider_type") + providerId String @map("provider_id") + providerAccountId String @map("provider_account_id") + refreshToken String? @map("refresh_token") + accessToken String? @map("access_token") + accessTokenExpires DateTime? @map("access_token_expires") + + @@index([providerAccountId], map: "accounts.provider_account_id_index") + @@index([providerId], map: "accounts.provider_id_index") + @@index([userId], map: "accounts.user_id_index") + @@map("accounts") +} + +model Session { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + userId Int @map("user_id") + expires DateTime + sessionToken String @unique(map: "sessions.session_token_unique") @map("session_token") + accessToken String @unique(map: "sessions.access_token_unique") @map("access_token") + + @@map("sessions") +} + +model VerificationRequest { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + identifier String + token String @unique(map: "verification_requests.token_unique") + expires DateTime + + @@map("verification_requests") +} + +model Bookmark { + userId Int + itemId Int + createdAt DateTime @default(now()) @map("created_at") + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@id([userId, itemId]) + @@index([createdAt], map: "Bookmark.created_at_index") +} + +model ThreadSubscription { + userId Int + itemId Int + createdAt DateTime @default(now()) @map("created_at") + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@id([userId, itemId]) + @@index([createdAt], map: "ThreadSubscription.created_at_index") +} + +model PushSubscription { + id Int @id @default(autoincrement()) + userId Int + endpoint String + p256dh String + auth String + createdAt DateTime @default(now()) @map("created_at") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId], map: "PushSubscription.userId_index") } enum EarnType { @@ -173,191 +507,12 @@ enum EarnType { TIP_POST } -model Earn { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - - msats BigInt - user User @relation(fields: [userId], references: [id]) - userId Int - - type EarnType? - typeId Int? - rank Int? - - @@index([createdAt]) - @@index([userId]) - @@index([createdAt, userId]) -} - -model LnAuth { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - k1 String @unique - pubkey String? -} - -model LnWith { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - k1 String @unique - userId Int - withdrawalId Int? -} - -model Invite { - id String @id @default(cuid()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - user User @relation(name: "Invites", fields: [userId], references: [id]) - userId Int - gift Int? - limit Int? - revoked Boolean @default(false) - invitees User[] - - @@index([userId]) - @@index([createdAt]) -} - -model Message { - id Int @id @default(autoincrement()) - text String - user User @relation(fields: [userId], references: [id]) - userId Int -} - enum Status { ACTIVE STOPPED NOSATS } -model Item { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - deletedAt DateTime? - title String? - text String? - url String? - user User @relation("UserItems", fields: [userId], references: [id]) - userId Int - fwdUser User? @relation(name: "FwdItem", fields: [fwdUserId], references: [id]) - fwdUserId Int? - parent Item? @relation("ParentChildren", fields: [parentId], references: [id]) - parentId Int? - children Item[] @relation("ParentChildren") - root Item? @relation("RootDescendant", fields: [rootId], references: [id]) - rootId Int? - descendants Item[] @relation("RootDescendant") - actions ItemAct[] - mentions Mention[] - path Unsupported("LTREE")? - pin Pin? @relation(fields: [pinId], references: [id]) - pinId Int? - uploadId Int? - upload Upload? - paidImgLink Boolean @default(false) - - // open timestamps - otsHash String? - otsFile Bytes? - - // bounties - bounty Int? - bountyPaidTo Int[] - - // is free post or bio - freebie Boolean @default(false) - bio Boolean @default(false) - - // denormalized self stats - weightedVotes Float @default(0) - weightedDownVotes Float @default(0) - msats BigInt @default(0) - boost Int @default(0) - upvotes Int @default(0) - - // denormalized comment stats - ncomments Int @default(0) - commentMsats BigInt @default(0) - lastCommentAt DateTime? - weightedComments Float @default(0) - - // if sub is null, this is the main sub - sub Sub? @relation(fields: [subName], references: [name]) - subName String? @db.Citext - - // fields exclusively for job post types right now - minSalary Int? - maxSalary Int? - maxBid Int? - status Status @default(ACTIVE) - statusUpdatedAt DateTime? - location String? - company String? - latitude Float? - longitude Float? - remote Boolean? - - // fields for polls - pollCost Int? - - User User[] - PollOption PollOption[] - PollVote PollVote[] - Bookmark Bookmark[] - ThreadSubscription ThreadSubscription[] - - @@index([weightedVotes]) - @@index([weightedDownVotes]) - @@index([bio]) - @@index([freebie]) - @@index([createdAt]) - @@index([userId]) - @@index([rootId]) - @@index([parentId]) - @@index([status]) - @@index([maxBid]) - @@index([statusUpdatedAt]) - @@index([subName]) - @@index([pinId]) - @@index([path]) -} - -model PollOption { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - itemId Int - item Item @relation(fields: [itemId], references: [id]) - option String - - PollVote PollVote[] - - @@index([itemId]) -} - -model PollVote { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - userId Int - user User @relation(fields: [userId], references: [id]) - itemId Int - item Item @relation(fields: [itemId], references: [id]) - pollOptionId Int - pollOption PollOption @relation(fields: [pollOptionId], references: [id]) - - @@unique([itemId, userId]) - @@index([userId]) - @@index([pollOptionId]) -} - enum PostType { LINK DISCUSSION @@ -372,51 +527,6 @@ enum RankingType { AUCTION } -model Sub { - name String @id @db.Citext - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - postTypes PostType[] - rankingType RankingType - baseCost Int @default(1) - desc String? - - Item Item[] - Subscription Subscription[] -} - -model Subscription { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - subName String @db.Citext - sub Sub @relation(fields: [subName], references: [name]) - user User @relation(fields: [userId], references: [id]) - userId Int -} - -// the active pin is the latest one when it's a recurring cron -model Pin { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - cron String? - timezone String? - position Int - Item Item[] -} - -model ReferralAct { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at") - referrerId Int - referrer User @relation(fields: [referrerId], references: [id]) - itemActId Int - itemAct ItemAct @relation(fields: [itemActId], references: [id]) - msats BigInt -} - enum ItemActType { VOTE BOOST @@ -427,168 +537,11 @@ enum ItemActType { FEE } -model ItemAct { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - msats BigInt - act ItemActType - item Item @relation(fields: [itemId], references: [id]) - itemId Int - user User @relation(fields: [userId], references: [id]) - userId Int - ReferralAct ReferralAct[] - - @@index([itemId]) - @@index([userId]) - @@index([act]) - @@index([createdAt]) - // for getting a user's spending on an item, eg meSats - @@index([itemId, userId, act]) - // for getting a user's spending over time, eg amount spent in a week - @@index([userId, createdAt, act]) - // for checking if an item has been tipped recently, where the item number is small - @@index([itemId, createdAt, act]) - // for checking if an item has been tipped recently, where the recent acts are small - @@index([createdAt, itemId, act]) -} - -model Mention { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - item Item @relation(fields: [itemId], references: [id]) - itemId Int - user User @relation(fields: [userId], references: [id]) - userId Int - - @@unique([itemId, userId]) - @@index([createdAt]) - @@index([itemId]) - @@index([userId]) -} - -model Invoice { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - user User @relation(fields: [userId], references: [id]) - userId Int - - hash String @unique - bolt11 String - desc String? - expiresAt DateTime - confirmedAt DateTime? - msatsRequested BigInt - msatsReceived BigInt? - cancelled Boolean @default(false) - - @@index([createdAt]) - @@index([userId]) -} - enum WithdrawlStatus { - CONFIRMED INSUFFICIENT_BALANCE INVALID_PAYMENT PATHFINDING_TIMEOUT ROUTE_NOT_FOUND + CONFIRMED UNKNOWN_FAILURE } - -model Withdrawl { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - user User @relation(fields: [userId], references: [id]) - userId Int - - hash String - bolt11 String - msatsPaying BigInt - msatsPaid BigInt? - msatsFeePaying BigInt - msatsFeePaid BigInt? - - status WithdrawlStatus? - - @@index([createdAt]) - @@index([userId]) -} - -model Account { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - compoundId String @unique @map(name: "compound_id") - userId Int @map(name: "user_id") - providerType String @map(name: "provider_type") - providerId String @map(name: "provider_id") - providerAccountId String @map(name: "provider_account_id") - refreshToken String? @map(name: "refresh_token") - accessToken String? @map(name: "access_token") - accessTokenExpires DateTime? @map(name: "access_token_expires") - - @@index([providerAccountId]) - @@index([providerId]) - @@index([userId]) - @@map(name: "accounts") -} - -model Session { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - userId Int @map(name: "user_id") - expires DateTime - sessionToken String @unique @map(name: "session_token") - accessToken String @unique @map(name: "access_token") - - @@map(name: "sessions") -} - -model VerificationRequest { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @updatedAt @map(name: "updated_at") - identifier String - token String @unique - expires DateTime - - @@map(name: "verification_requests") -} - -model Bookmark { - user User @relation(fields: [userId], references: [id]) - userId Int - item Item @relation(fields: [itemId], references: [id]) - itemId Int - createdAt DateTime @default(now()) @map(name: "created_at") - - @@id([userId, itemId]) - @@index([createdAt]) -} - -model ThreadSubscription { - user User @relation(fields: [userId], references: [id]) - userId Int - item Item @relation(fields: [itemId], references: [id]) - itemId Int - createdAt DateTime @default(now()) @map(name: "created_at") - - @@id([userId, itemId]) - @@index([createdAt]) -} - -model PushSubscription { - id Int @id @default(autoincrement()) - user User @relation(fields: [userId], references: [id]) - userId Int - endpoint String - p256dh String - auth String - createdAt DateTime @default(now()) @map(name: "created_at") - - @@index([userId]) -} diff --git a/worker/earn.js b/worker/earn.js index 3b195bfc..cf78afc7 100644 --- a/worker/earn.js +++ b/worker/earn.js @@ -42,7 +42,7 @@ function earn ({ models }) { } // get earners { userId, id, type, rank, proportion } - const earners = await models.$queryRaw(` + const earners = await models.$queryRawUnsafe(` WITH item_ratios AS ( SELECT *, CASE WHEN "parentId" IS NULL THEN 'POST' ELSE 'COMMENT' END as type, diff --git a/worker/streak.js b/worker/streak.js index f7cad58b..0f4e2ae0 100644 --- a/worker/streak.js +++ b/worker/streak.js @@ -7,7 +7,7 @@ function computeStreaks ({ models }) { // get all eligible users in the last day // if the user doesn't have an active streak, add one // if they have an active streak but didn't maintain it, end it - await models.$executeRaw( + await models.$executeRawUnsafe( `WITH day_streaks (id) AS ( SELECT "userId" FROM diff --git a/worker/trust.js b/worker/trust.js index be36ed43..38b372af 100644 --- a/worker/trust.js +++ b/worker/trust.js @@ -165,7 +165,7 @@ async function storeTrust (models, nodeTrust) { // update the trust of each user in graph await models.$transaction([ models.$executeRaw`UPDATE users SET trust = 0`, - models.$executeRaw( + models.$executeRawUnsafe( `UPDATE users SET trust = g.trust FROM (values ${values}) g(id, trust) diff --git a/worker/views.js b/worker/views.js index 6cc9719c..57504d56 100644 --- a/worker/views.js +++ b/worker/views.js @@ -6,7 +6,7 @@ function views ({ models }) { for (const view of ['reg_growth_days', 'spender_growth_days', 'item_growth_days', 'spending_growth_days', 'stackers_growth_days', 'stacking_growth_days', 'user_stats_days']) { - await models.$queryRaw(`REFRESH MATERIALIZED VIEW CONCURRENTLY ${view}`) + await models.$queryRawUnsafe(`REFRESH MATERIALIZED VIEW CONCURRENTLY ${view}`) } console.log('done refreshing stats views') @@ -19,7 +19,7 @@ function rankViews ({ models }) { console.log('refreshing rank views') for (const view of ['sat_rank_wwm_view', 'sat_rank_tender_view']) { - await models.$queryRaw(`REFRESH MATERIALIZED VIEW CONCURRENTLY ${view}`) + await models.$queryRawUnsafe(`REFRESH MATERIALIZED VIEW CONCURRENTLY ${view}`) } console.log('done refreshing rank views')