2024-11-01 21:05:54 +01:00
|
|
|
import { Notes, Users } from "@versia/kit/tables";
|
2024-04-17 06:09:21 +02:00
|
|
|
import { type SQL, gt } from "drizzle-orm";
|
2024-05-29 02:59:49 +02:00
|
|
|
import { config } from "~/packages/config-manager";
|
2024-10-04 15:22:48 +02:00
|
|
|
import { Note } from "./note.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-04-17 06:09:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-13 04:26:43 +02:00
|
|
|
export class Timeline<Type extends Note | User> {
|
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,
|
|
|
|
|
url: string,
|
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,
|
|
|
|
|
url: string,
|
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-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[];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async fetchLinkHeader(
|
|
|
|
|
objects: Type[],
|
|
|
|
|
url: string,
|
|
|
|
|
limit: number,
|
|
|
|
|
): Promise<string> {
|
2024-06-13 10:52:03 +02:00
|
|
|
const linkHeader: string[] = [];
|
2024-06-13 04:26:43 +02:00
|
|
|
const urlWithoutQuery = new URL(
|
|
|
|
|
new URL(url).pathname,
|
|
|
|
|
config.http.base_url,
|
|
|
|
|
).toString();
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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[],
|
|
|
|
|
urlWithoutQuery: string,
|
|
|
|
|
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(
|
|
|
|
|
gt(Notes.id, notes[notes.length - 1].data.id),
|
|
|
|
|
);
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notes[notes.length - 1].data.id}>; rel="next"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 13:51:19 +02:00
|
|
|
private static async fetchUserLinkHeader(
|
2024-06-13 04:26:43 +02:00
|
|
|
users: User[],
|
|
|
|
|
urlWithoutQuery: string,
|
|
|
|
|
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(
|
|
|
|
|
gt(Users.id, users[users.length - 1].id),
|
|
|
|
|
);
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${users[users.length - 1].id}>; rel="next"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return linkHeader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async fetchTimeline(
|
|
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
|
|
|
|
url: string,
|
|
|
|
|
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
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* private async fetchTimeline<T>(
|
2024-04-17 06:09:21 +02:00
|
|
|
sql: SQL<unknown> | undefined,
|
|
|
|
|
limit: number,
|
|
|
|
|
url: string,
|
2024-05-09 01:19:53 +02:00
|
|
|
userId?: string,
|
2024-04-17 06:09:21 +02:00
|
|
|
) {
|
2024-04-25 05:40:27 +02:00
|
|
|
const notes: Note[] = [];
|
|
|
|
|
const users: User[] = [];
|
2024-04-17 06:09:21 +02:00
|
|
|
|
|
|
|
|
switch (this.type) {
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.Note:
|
2024-05-09 01:19:53 +02:00
|
|
|
notes.push(
|
|
|
|
|
...(await Note.manyFromSql(
|
|
|
|
|
sql,
|
|
|
|
|
undefined,
|
|
|
|
|
limit,
|
|
|
|
|
undefined,
|
|
|
|
|
userId,
|
|
|
|
|
)),
|
|
|
|
|
);
|
2024-04-25 05:40:27 +02:00
|
|
|
break;
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.User:
|
2024-04-25 05:40:27 +02:00
|
|
|
users.push(...(await User.manyFromSql(sql, undefined, limit)));
|
2024-04-17 06:09:21 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const linkHeader = [];
|
|
|
|
|
const urlWithoutQuery = new URL(
|
|
|
|
|
new URL(url).pathname,
|
|
|
|
|
config.http.base_url,
|
|
|
|
|
).toString();
|
|
|
|
|
|
2024-04-25 05:40:27 +02:00
|
|
|
if (notes.length > 0) {
|
2024-04-17 06:09:21 +02:00
|
|
|
switch (this.type) {
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.Note: {
|
2024-04-17 06:09:21 +02:00
|
|
|
const objectBefore = await Note.fromSql(
|
2024-06-13 02:45:07 +02:00
|
|
|
gt(Notes.id, notes[0].data.id),
|
2024-04-17 06:09:21 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (objectBefore) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${limit ?? 20}&min_id=${
|
2024-06-13 02:45:07 +02:00
|
|
|
notes[0].data.id
|
2024-04-17 06:09:21 +02:00
|
|
|
}>; rel="prev"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-25 05:40:27 +02:00
|
|
|
if (notes.length >= (limit ?? 20)) {
|
2024-04-17 06:09:21 +02:00
|
|
|
const objectAfter = await Note.fromSql(
|
2024-06-13 02:45:07 +02:00
|
|
|
gt(Notes.id, notes[notes.length - 1].data.id),
|
2024-04-17 06:09:21 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${
|
|
|
|
|
limit ?? 20
|
|
|
|
|
}&max_id=${
|
2024-06-13 02:45:07 +02:00
|
|
|
notes[notes.length - 1].data.id
|
2024-04-25 05:40:27 +02:00
|
|
|
}>; rel="next"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.User: {
|
2024-04-25 05:40:27 +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(
|
|
|
|
|
gt(Users.id, users[users.length - 1].id),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (objectAfter) {
|
|
|
|
|
linkHeader.push(
|
|
|
|
|
`<${urlWithoutQuery}?limit=${
|
|
|
|
|
limit ?? 20
|
|
|
|
|
}&max_id=${
|
|
|
|
|
users[users.length - 1].id
|
2024-04-17 06:09:21 +02:00
|
|
|
}>; rel="next"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-25 05:40:27 +02:00
|
|
|
switch (this.type) {
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.Note:
|
2024-04-25 05:40:27 +02:00
|
|
|
return {
|
|
|
|
|
link: linkHeader.join(", "),
|
|
|
|
|
objects: notes as T[],
|
|
|
|
|
};
|
2024-06-13 04:26:43 +02:00
|
|
|
case TimelineType.User:
|
2024-04-25 05:40:27 +02:00
|
|
|
return {
|
|
|
|
|
link: linkHeader.join(", "),
|
|
|
|
|
objects: users as T[],
|
|
|
|
|
};
|
|
|
|
|
}
|
2024-06-13 04:26:43 +02:00
|
|
|
} */
|
2024-04-17 06:09:21 +02:00
|
|
|
}
|