workout tips functions etc

This commit is contained in:
keyan 2021-09-08 16:15:06 -05:00
parent 59cfc7660b
commit 28d684da73
5 changed files with 162 additions and 105 deletions

View File

@ -15,25 +15,6 @@ export default {
}
return me.name?.toUpperCase() === name?.toUpperCase() || !(await models.user.findUnique({ where: { name } }))
},
recentlyStacked: async (parent, args, { models, me }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
}
const user = await models.user.findUnique({ where: { id: me.id } })
const [{ sum }] = await models.$queryRaw(`
SELECT sum("Vote".sats)
FROM "Vote"
JOIN "Item" on "Vote"."itemId" = "Item".id
WHERE "Vote"."userId" <> $1
AND ("Vote".created_at > $2 OR $2 IS NULL)
AND "Vote".boost = false
AND "Item"."userId" = $1`, user.id, user.checkedNotesAt)
await models.user.update({ where: { id: me.id }, data: { checkedNotesAt: new Date() } })
return sum || 0
}
},

View File

@ -6,7 +6,6 @@ export default gql`
user(name: String!): User
users: [User!]
nameAvailable(name: String!): Boolean!
recentlyStacked: Int!
}
extend type Mutation {

View File

@ -0,0 +1,135 @@
/*
Warnings:
- You are about to drop the `Vote` table. If the table is not empty, all the data it contains will be lost.
*/
-- CreateEnum
CREATE TYPE "ItemAct" AS ENUM ('VOTE', 'BOOST', 'TIP');
-- DropForeignKey
ALTER TABLE "Vote" DROP CONSTRAINT "Vote_itemId_fkey";
-- DropForeignKey
ALTER TABLE "Vote" DROP CONSTRAINT "Vote_userId_fkey";
ALTER TABLE "Vote" RENAME TO "ItemAct";
ALTER TABLE "ItemAct" RENAME COLUMN "boost" TO "act";
ALTER TABLE "ItemAct"
ALTER COLUMN "act" DROP DEFAULT,
ALTER COLUMN "act" SET DATA TYPE "ItemActType"
USING (
CASE
WHEN "act" THEN 'VOTE'
ELSE 'BOOST'
END
)::"ItemActType";
-- CreateIndex
CREATE INDEX "ItemAct.itemId_index" ON "ItemAct"("itemId");
-- CreateIndex
CREATE INDEX "ItemAct.userId_index" ON "ItemAct"("userId");
-- CreateIndex
CREATE INDEX "ItemAct.act_index" ON "ItemAct"("act");
-- AddForeignKey
ALTER TABLE "ItemAct" ADD FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ItemAct" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
CREATE OR REPLACE FUNCTION item_act(item_id INTEGER, user_id INTEGER, act "ItemActType", act_sats INTEGER)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
DECLARE
user_sats INTEGER;
BEGIN
PERFORM ASSERT_SERIALIZED();
SELECT (msats / 1000) INTO user_sats FROM users WHERE id = user_id;
IF act_sats > user_sats THEN
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
END IF;
UPDATE users SET msats = msats - (act_sats * 1000) WHERE id = user_id;
CASE act
when 'VOTE', 'BOOST' then
-- if we've already voted, then this is boost (doing this here prevents any potential
-- race)
IF EXISTS (SELECT 1 FROM "ItemAct" WHERE "itemId" = item_id AND "userId" = user_id) THEN
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
VALUES (act_sats, item_id, user_id, 'BOOST', now_utc(), now_utc());
-- this is a vote
ELSE
-- only 1 sat votes are allowed
IF act_sats > 1 THEN
RAISE EXCEPTION 'SN_EXCEEDS_ACT_SAT_LIMIT';
END IF;
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
VALUES (1, item_id, user_id, 'VOTE', now_utc(), now_utc());
-- give the item's user 1 sat
UPDATE users SET msats = msats + 1000 WHERE id = (SELECT "userId" FROM "Item" WHERE id = item_id);
END IF;
when 'TIP' then
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
VALUES (act_sats, item_id, user_id, 'TIP', now_utc(), now_utc());
-- give the item's user act_sats
UPDATE users SET msats = msats + (act_sats * 1000) WHERE id = (SELECT "userId" FROM "Item" WHERE id = item_id);
END case;
RETURN act_sats;
END;
$$;
-- if user has free comments or posts, use that
CREATE OR REPLACE FUNCTION create_item(title TEXT, url TEXT, text TEXT, parent_id INTEGER, user_id INTEGER)
RETURNS "Item"
LANGUAGE plpgsql
AS $$
DECLARE
user_sats INTEGER;
free_posts INTEGER;
free_comments INTEGER;
freebie BOOLEAN;
item "Item";
BEGIN
PERFORM ASSERT_SERIALIZED();
SELECT (msats / 1000), "freePosts", "freeComments"
INTO user_sats, free_posts, free_comments
FROM users WHERE id = user_id;
freebie := (parent_id IS NULL AND free_posts > 0) OR (parent_id IS NOT NULL AND free_comments > 0);
IF NOT freebie AND 1 > user_sats THEN
RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
END IF;
INSERT INTO "Item" (title, url, text, "userId", "parentId", created_at, updated_at)
VALUES (title, url, text, user_id, parent_id, now_utc(), now_utc()) RETURNING * INTO item;
IF freebie THEN
IF parent_id IS NULL THEN
UPDATE users SET "freePosts" = "freePosts" - 1 WHERE id = user_id;
ELSE
UPDATE users SET "freeComments" = "freeComments" - 1 WHERE id = user_id;
END IF;
ELSE
UPDATE users SET msats = msats - 1000 WHERE id = user_id;
INSERT INTO "ItemAct" (sats, "itemId", "userId", act, created_at, updated_at)
VALUES (1, item.id, user_id, 'VOTE', now_utc(), now_utc());
END IF;
RETURN item;
END;
$$;

View File

@ -1,65 +0,0 @@
WITH RECURSIVE base AS (
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
ARRAY[row_number() OVER (ORDER BY (x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 1.5) DESC NULLS LAST, "Item"."path")] AS sort_path
FROM "Item"
LEFT JOIN (SELECT i.id, SUM("Vote".sats) as sats
FROM "Item" i
JOIN "Vote" ON i.id = "Vote"."itemId"
WHERE i."parentId" IS NULL
GROUP BY i.id) x ON "Item".id = x.id
WHERE "parentId" IS NULL
UNION ALL
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
p.sort_path || row_number() OVER (ORDER BY (x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 1.5) DESC NULLS LAST, "Item"."path")
FROM base p
JOIN "Item" ON ltree2text(subpath("Item"."path", 0, -1)) = p."path"
LEFT JOIN (SELECT i.id, SUM("Vote".sats) as sats
FROM "Item" i
JOIN "Vote" ON i.id = "Vote"."itemId"
WHERE i."parentId" IS NULL
GROUP BY i.id) x ON "Item".id = x.id
)
select * from base order by sort_path;
WITH RECURSIVE base AS (
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
ARRAY[row_number() OVER (ORDER BY "Item".created_at, "Item"."path")] AS sort_path
FROM "Item"
WHERE "parentId" IS NULL
UNION ALL
SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS "path",
p.sort_path || row_number() OVER (ORDER BY "Item".created_at, "Item"."path")
FROM base p
JOIN "Item" ON ltree2text(subpath("Item"."path", 0, -1)) = p."path"
)
select * from base order by sort_path;
CREATE OR REPLACE FUNCTION vote(item_id INTEGER, username TEXT, vote_sats INTEGER)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
DECLARE
user_id INTEGER
user_sats INTEGER
BEGIN
SELECT sats, id INTO user_sats, user_id FROM "User" WHERE name = username;
IF vote_sats > user_sats THEN
RAISE EXCEPTION 'insufficient funds';
END IF;
UPDATE "User" SET sats = sats - vote_sats WHERE id = user_id;
IF EXISTS (SELECT 1 FROM "Vote" WHERE "itemId" = item_id AND "userId" = user_id) THEN
INSERT INTO "Vote" (sats, "itemId", "userId") VALUES (vote_sats, item_id, user_id);
UPDATE "User" SET sats = sats + vote_sats WHERE id = (SELECT "userId" FROM "Item" WHERE id = item_id);
ELSE
INSERT INTO "Vote" (sats, "itemId", "userId", boost) VALUES (vote_sats, item_id, user_id, true);
END IF;
RETURN sats
END;
$$;

View File

@ -11,24 +11,24 @@ generator client {
}
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")
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[]
mentions Mention[]
messages Message[]
votes Vote[]
itemActions ItemAction[]
invoices Invoice[]
withdrawls Withdrawl[]
msats Int @default(0)
freeComments Int @default(5)
freePosts Int @default(2)
msats Int @default(0)
freeComments Int @default(5)
freePosts Int @default(2)
checkedNotesAt DateTime?
pubkey String? @unique
pubkey String? @unique
@@map(name: "users")
}
@ -60,7 +60,7 @@ model Item {
parent Item? @relation("ParentChildren", fields: [parentId], references: [id])
parentId Int?
children Item[] @relation("ParentChildren")
votes Vote[]
actions ItemAction[]
mentions Mention[]
path Unsupported("LTREE")?
@ -68,19 +68,26 @@ model Item {
@@index([parentId])
}
model Vote {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
sats Int @default(1)
boost Boolean @default(false)
item Item @relation(fields: [itemId], references: [id])
enum ItemActionType {
VOTE
BOOST
TIP
}
model ItemAction {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
sats Int @default(1)
action ItemActionType @default(VOTE)
item Item @relation(fields: [itemId], references: [id])
itemId Int
user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id])
userId Int
@@index([itemId])
@@index([userId])
@@index([action])
}
model Mention {