mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-04 11:22:40 -05:00
Kysely fills keys missing from some rows of a multi-row .values() with literal null on SQLite (it cannot emit the DEFAULT keyword), which was the cause of the organizer registration crash. The kysely patch now rejects row arrays whose element type has optional or heterogeneous keys, with an error naming the offending column.
119 lines
3.0 KiB
TypeScript
119 lines
3.0 KiB
TypeScript
import { sub } from "date-fns";
|
|
import { db } from "~/db/sql";
|
|
import type { TablesInsertable } from "~/db/tables";
|
|
import type { NotificationSubscription } from "~/db/tables-json";
|
|
import { actorId } from "~/features/auth/core/user.server";
|
|
import { dateToDatabaseTimestamp } from "../../utils/dates";
|
|
import { NOTIFICATIONS } from "./notifications-contants";
|
|
import type { Notification } from "./notifications-types";
|
|
|
|
export function insert(
|
|
notification: Notification,
|
|
users: Array<Omit<TablesInsertable["NotificationUser"], "notificationId">>,
|
|
) {
|
|
return db.transaction().execute(async (trx) => {
|
|
const inserted = await trx
|
|
.insertInto("Notification")
|
|
.values({
|
|
type: notification.type,
|
|
pictureUrl: notification.pictureUrl,
|
|
meta: notification.meta ? JSON.stringify(notification.meta) : null,
|
|
})
|
|
.returning("id")
|
|
.executeTakeFirstOrThrow();
|
|
|
|
await trx
|
|
.insertInto("NotificationUser")
|
|
.values(
|
|
users.map(({ userId, seen }) => ({
|
|
userId,
|
|
notificationId: inserted.id,
|
|
seen: seen ?? 0,
|
|
})),
|
|
)
|
|
.execute();
|
|
|
|
return inserted;
|
|
});
|
|
}
|
|
|
|
export function findByUserId(
|
|
userId: number,
|
|
{ limit }: { limit?: number } = {},
|
|
) {
|
|
return db
|
|
.selectFrom("NotificationUser")
|
|
.innerJoin(
|
|
"Notification",
|
|
"Notification.id",
|
|
"NotificationUser.notificationId",
|
|
)
|
|
.select([
|
|
"Notification.id",
|
|
"Notification.createdAt",
|
|
"NotificationUser.seen",
|
|
"Notification.type",
|
|
"Notification.meta",
|
|
"Notification.pictureUrl",
|
|
])
|
|
.where("NotificationUser.userId", "=", userId)
|
|
.limit(limit ?? NOTIFICATIONS.MAX_SHOWN)
|
|
.orderBy("Notification.id", "desc")
|
|
.execute() as Promise<
|
|
Array<Notification & { id: number; createdAt: number; seen: number }>
|
|
>;
|
|
}
|
|
|
|
export function findAllByType<T extends Notification["type"]>(type: T) {
|
|
return db
|
|
.selectFrom("Notification")
|
|
.select(["type", "meta", "pictureUrl"])
|
|
.where("type", "=", type)
|
|
.execute() as Promise<Array<Extract<Notification, { type: T }>>>;
|
|
}
|
|
|
|
export function markOwnAsSeen(notificationIds: number[]) {
|
|
return db
|
|
.updateTable("NotificationUser")
|
|
.set("seen", 1)
|
|
.where("NotificationUser.notificationId", "in", notificationIds)
|
|
.where("NotificationUser.userId", "=", actorId())
|
|
.execute();
|
|
}
|
|
|
|
export function deleteOld() {
|
|
return db
|
|
.deleteFrom("Notification")
|
|
.where(
|
|
"createdAt",
|
|
"<",
|
|
dateToDatabaseTimestamp(sub(new Date(), { days: 14 })),
|
|
)
|
|
.executeTakeFirst();
|
|
}
|
|
|
|
export function insertOwnSubscription(subscription: NotificationSubscription) {
|
|
return db
|
|
.insertInto("NotificationUserSubscription")
|
|
.values({
|
|
userId: actorId(),
|
|
subscription: JSON.stringify(subscription),
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
export function findAllSubscriptionsByUserIds(userIds: number[]) {
|
|
return db
|
|
.selectFrom("NotificationUserSubscription")
|
|
.select(["id", "subscription"])
|
|
.where("userId", "in", userIds)
|
|
.execute();
|
|
}
|
|
|
|
export function deleteSubscriptionById(id: number) {
|
|
return db
|
|
.deleteFrom("NotificationUserSubscription")
|
|
.where("id", "=", id)
|
|
.execute();
|
|
}
|