2024-11-04 10:43:30 +01:00
|
|
|
import { Notes, Notifications, Users } from "@versia/kit/tables";
|
2025-04-10 19:15:31 +02:00
|
|
|
import { gt, type SQL } from "drizzle-orm";
|
2025-02-15 02:47:29 +01:00
|
|
|
import { config } from "~/config.ts";
|
2024-11-04 15:20:53 +01:00
|
|
|
import { Note } from "./note.ts";
|
|
|
|
|
import { Notification } from "./notification.ts";
|
|
|
|
|
import { User } from "./user.ts";
|
2024-04-17 06:09:21 +02:00
|
|
|
|
|
|
|
|
enum TimelineType {
|
2024-06-13 04:26:43 +02:00
|
|
|
Note = "Note",
|
|
|
|
|
User = "User",
|
2024-11-04 10:43:30 +01:00
|
|
|
Notification = "Notification",
|
2024-04-17 06:09:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 10:43:30 +01:00
|
|
|
export class Timeline<Type extends Note | User | Notification> {
|
2024-11-01 21:20:12 +01:00
|
|
|
public constructor(private type: TimelineType) {}
|
2024-04-17 06:09:21 +02:00
|
|
|
|
2024-11-01 21:20:12 +01:00
|
|
|
public static getNoteTimeline(
|
2024-04-17 06:09:21 +02:00
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
2025-02-01 16:32:18 +01:00
|
|
|
url: URL,
|
2024-05-09 01:19:53 +02:00
|
|
|
userId?: string,
|
2024-11-02 00:43:33 +01:00
|
|
|
): Promise<{ link: string; objects: Note[] }> {
|
2024-06-13 04:26:43 +02:00
|
|
|
return new Timeline<Note>(TimelineType.Note).fetchTimeline(
|
2024-04-25 05:40:27 +02:00
|
|
|
sql,
|
|
|
|
|
limit,
|
|
|
|
|
url,
|
2024-05-09 01:19:53 +02:00
|
|
|
userId,
|
2024-04-25 05:40:27 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 21:20:12 +01:00
|
|
|
public static getUserTimeline(
|
2024-04-25 05:40:27 +02:00
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
2025-02-01 16:32:18 +01:00
|
|
|
url: URL,
|
2024-11-02 00:43:33 +01:00
|
|
|
): Promise<{ link: string; objects: User[] }> {
|
2024-06-13 04:26:43 +02:00
|
|
|
return new Timeline<User>(TimelineType.User).fetchTimeline(
|
2024-04-25 05:40:27 +02:00
|
|
|
sql,
|
|
|
|
|
limit,
|
|
|
|
|
url,
|
|
|
|
|
);
|
2024-04-17 06:09:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 10:43:30 +01:00
|
|
|
public static getNotificationTimeline(
|
|
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
2025-02-01 16:32:18 +01:00
|
|
|
url: URL,
|
2024-11-04 10:43:30 +01:00
|
|
|
userId?: string,
|
|
|
|
|
): Promise<{ link: string; objects: Notification[] }> {
|
|
|
|
|
return new Timeline<Notification>(
|
|
|
|
|
TimelineType.Notification,
|
|
|
|
|
).fetchTimeline(sql, limit, url, userId);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 04:26:43 +02:00
|
|
|
private async fetchObjects(
|
|
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
|
|
|
|
userId?: string,
|
|
|
|
|
): Promise<Type[]> {
|
|
|
|
|
switch (this.type) {
|
|
|
|
|
case TimelineType.Note:
|
|
|
|
|
return (await Note.manyFromSql(
|
|
|
|
|
sql,
|
|
|
|
|
undefined,
|
|
|
|
|
limit,
|
|
|
|
|
undefined,
|
|
|
|
|
userId,
|
|
|
|
|
)) as Type[];
|
|
|
|
|
case TimelineType.User:
|
|
|
|
|
return (await User.manyFromSql(
|
|
|
|
|
sql,
|
|
|
|
|
undefined,
|
|
|
|
|
limit,
|
|
|
|
|
)) as Type[];
|
2024-11-04 10:43:30 +01:00
|
|
|
case TimelineType.Notification:
|
|
|
|
|
return (await Notification.manyFromSql(
|
|
|
|
|
sql,
|
|
|
|
|
undefined,
|
|
|
|
|
limit,
|
|
|
|
|
undefined,
|
|
|
|
|
undefined,
|
|
|
|
|
userId,
|
|
|
|
|
)) as Type[];
|
2024-06-13 04:26:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async fetchLinkHeader(
|
|
|
|
|
objects: Type[],
|
2025-02-01 16:32:18 +01:00
|
|
|
url: URL,
|
2024-06-13 04:26:43 +02:00
|
|
|
limit: number,
|
|
|
|
|
): Promise<string> {
|
2024-06-13 10:52:03 +02:00
|
|
|
const linkHeader: string[] = [];
|
2025-02-01 16:32:18 +01:00
|
|
|
const urlWithoutQuery = new URL(url.pathname, config.http.base_url);
|
2024-06-13 04:26:43 +02:00
|
|
|
|
|
|
|
|
if (objects.length > 0) {
|
|
|
|
|
switch (this.type) {
|
|
|
|
|
case TimelineType.Note:
|
|
|
|
|
linkHeader.push(
|
2024-10-03 13:51:19 +02:00
|
|
|
...(await Timeline.fetchNoteLinkHeader(
|
2024-06-13 04:26:43 +02:00
|
|
|
objects as Note[],
|
|
|
|
|
urlWithoutQuery,
|
|
|
|
|
limit,
|
|
|
|
|
)),
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case TimelineType.User:
|
|
|
|
|
linkHeader.push(
|
2024-10-03 13:51:19 +02:00
|
|
|
...(await Timeline.fetchUserLinkHeader(
|
2024-06-13 04:26:43 +02:00
|
|
|
objects as User[],
|
|
|
|
|
urlWithoutQuery,
|
|
|
|
|
limit,
|
|
|
|
|
)),
|
|
|
|
|
);
|
|
|
|
|
break;
|
2024-11-04 10:43:30 +01:00
|
|
|
case TimelineType.Notification:
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
...(await Timeline.fetchNotificationLinkHeader(
|
|
|
|
|
objects as Notification[],
|
|
|
|
|
urlWithoutQuery,
|
|
|
|
|
limit,
|
|
|
|
|
)),
|
|
|
|
|
);
|
2024-06-13 04:26:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader.join(", ");
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 13:51:19 +02:00
|
|
|
private static async fetchNoteLinkHeader(
|
2024-06-13 04:26:43 +02:00
|
|
|
notes: Note[],
|
2025-02-01 16:32:18 +01:00
|
|
|
urlWithoutQuery: URL,
|
2024-06-13 04:26:43 +02:00
|
|
|
limit: number,
|
|
|
|
|
): Promise<string[]> {
|
2024-06-13 10:52:03 +02:00
|
|
|
const linkHeader: string[] = [];
|
2024-06-13 04:26:43 +02:00
|
|
|
|
|
|
|
|
const objectBefore = await Note.fromSql(gt(Notes.id, notes[0].data.id));
|
|
|
|
|
if (objectBefore) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&min_id=${notes[0].data.id}>; rel="prev"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (notes.length >= (limit ?? 20)) {
|
|
|
|
|
const objectAfter = await Note.fromSql(
|
2025-04-10 19:56:42 +02:00
|
|
|
gt(Notes.id, notes.at(-1)?.data.id ?? ""),
|
2024-06-13 04:26:43 +02:00
|
|
|
);
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
2025-04-10 19:56:42 +02:00
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notes.at(-1)?.data.id}>; rel="next"`,
|
2024-06-13 04:26:43 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 13:51:19 +02:00
|
|
|
private static async fetchUserLinkHeader(
|
2024-06-13 04:26:43 +02:00
|
|
|
users: User[],
|
2025-02-01 16:32:18 +01:00
|
|
|
urlWithoutQuery: URL,
|
2024-06-13 04:26:43 +02:00
|
|
|
limit: number,
|
|
|
|
|
): Promise<string[]> {
|
2024-06-13 10:52:03 +02:00
|
|
|
const linkHeader: string[] = [];
|
2024-06-13 04:26:43 +02:00
|
|
|
|
|
|
|
|
const objectBefore = await User.fromSql(gt(Users.id, users[0].id));
|
|
|
|
|
if (objectBefore) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&min_id=${users[0].id}>; rel="prev"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (users.length >= (limit ?? 20)) {
|
|
|
|
|
const objectAfter = await User.fromSql(
|
2025-04-10 19:56:42 +02:00
|
|
|
gt(Users.id, users.at(-1)?.id ?? ""),
|
2024-06-13 04:26:43 +02:00
|
|
|
);
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
2025-04-10 19:56:42 +02:00
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${users.at(-1)?.id}>; rel="next"`,
|
2024-06-13 04:26:43 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 10:43:30 +01:00
|
|
|
private static async fetchNotificationLinkHeader(
|
|
|
|
|
notifications: Notification[],
|
2025-02-01 16:32:18 +01:00
|
|
|
urlWithoutQuery: URL,
|
2024-11-04 10:43:30 +01:00
|
|
|
limit: number,
|
|
|
|
|
): Promise<string[]> {
|
|
|
|
|
const linkHeader: string[] = [];
|
|
|
|
|
|
|
|
|
|
const objectBefore = await Notification.fromSql(
|
|
|
|
|
gt(Notifications.id, notifications[0].data.id),
|
|
|
|
|
);
|
|
|
|
|
if (objectBefore) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&min_id=${notifications[0].data.id}>; rel="prev"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (notifications.length >= (limit ?? 20)) {
|
|
|
|
|
const objectAfter = await Notification.fromSql(
|
2025-04-10 19:56:42 +02:00
|
|
|
gt(Notifications.id, notifications.at(-1)?.data.id ?? ""),
|
2024-11-04 10:43:30 +01:00
|
|
|
);
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
2025-04-10 19:56:42 +02:00
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notifications.at(-1)?.data.id}>; rel="next"`,
|
2024-11-04 10:43:30 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 04:26:43 +02:00
|
|
|
private async fetchTimeline(
|
|
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
2025-02-01 16:32:18 +01:00
|
|
|
url: URL,
|
2024-06-13 04:26:43 +02:00
|
|
|
userId?: string,
|
|
|
|
|
): Promise<{ link: string; objects: Type[] }> {
|
|
|
|
|
const objects = await this.fetchObjects(sql, limit, userId);
|
|
|
|
|
const link = await this.fetchLinkHeader(objects, url, limit);
|
|
|
|
|
|
|
|
|
|
switch (this.type) {
|
|
|
|
|
case TimelineType.Note:
|
|
|
|
|
return {
|
|
|
|
|
link,
|
2024-10-03 13:41:58 +02:00
|
|
|
objects,
|
2024-06-13 04:26:43 +02:00
|
|
|
};
|
|
|
|
|
case TimelineType.User:
|
|
|
|
|
return {
|
|
|
|
|
link,
|
2024-10-03 13:41:58 +02:00
|
|
|
objects,
|
2024-06-13 04:26:43 +02:00
|
|
|
};
|
2024-11-04 10:43:30 +01:00
|
|
|
case TimelineType.Notification:
|
2024-04-25 05:40:27 +02:00
|
|
|
return {
|
2024-11-04 10:43:30 +01:00
|
|
|
link,
|
|
|
|
|
objects,
|
2024-04-25 05:40:27 +02:00
|
|
|
};
|
|
|
|
|
}
|
2024-11-04 10:43:30 +01:00
|
|
|
}
|
2024-04-17 06:09:21 +02:00
|
|
|
}
|