summaryrefslogtreecommitdiff
path: root/src/lib/collection/helpers.ts
blob: f65e7c0990e084703edbb33ffadbc175c93fddc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import type { CollectionEntry } from "astro:content";
import {
  Blog,
  Entity,
  type Entry,
  type MicroEntry,
  type OriginalEntry,
  type TranslationEntry,
} from "./schemas.ts";
import { getEntries, type z } from "astro:content";
import { defined, get, identity } from "../../utils/anonymous.ts";
import { createKeyFromArmor } from "../pgp/create.ts";
import { getUserIDsFromKey } from "../pgp/user.ts";
import type { UserIDPacket } from "openpgp";
import { getCollection } from "astro:content";
import { getEntry } from "astro:content";

export function getLastUpdate({ data }: CollectionEntry<"blog">): Date {
  return data.dateUpdated ?? data.dateCreated;
}
export const sortLastCreated = (
  { data: a }: CollectionEntry<"blog">,
  { data: b }: CollectionEntry<"blog">,
): number => b.dateCreated - a.dateCreated;
export const sortFirstCreated = (
  a: CollectionEntry<"blog">,
  b: CollectionEntry<"blog">,
): number => sortLastCreated(b, a);
export const sortLastUpdated = (
  { data: a }: CollectionEntry<"blog">,
  { data: b }: CollectionEntry<"blog">,
): number =>
  (b.dateUpdated ?? b.dateCreated) - (a.dateUpdated ?? a.dateCreated);
export const sortFirstUpdated = (
  a: CollectionEntry<"blog">,
  b: CollectionEntry<"blog">,
): number => sortLastUpdated(b, a);

export async function getSigners(
  { data }: CollectionEntry<"blog">,
): Promise<{
  id: string;
  entity: CollectionEntry<"entity">;
  role: z.infer<typeof Blog>["signers"][number]["role"] | undefined;
}[]> {
  const post = Blog.parse(data);
  return await getEntries(post.signers.map(get("entity"))).then((x) =>
    x.map((x) => ({
      id: x.id,
      entity: x,
      role: post.signers?.find((y) => y.entity.id === x.id)?.role,
    })).filter(({ role }) => defined(role))
  );
}

export async function getFirstAuthorEmail(
  blog: CollectionEntry<"blog">,
): Promise<string | undefined> {
  const signers = await getSigners(blog);
  const emails = await Promise.all(
    signers.filter(({ role }) => role === "author").map(async ({ entity }) => {
      const { publickey } = Entity.parse(entity.data);
      const key = await createKeyFromArmor(publickey.armor);
      const users = getUserIDsFromKey(undefined, key);
      return users.map(get("email")).filter(Boolean)?.[0];
    }),
  );
  return emails.filter(defined)?.[0];
}

export async function getFirstUserID(
  blog: CollectionEntry<"blog">,
): Promise<
  (Partial<UserIDPacket> & { entity: string; website: string | undefined })
> {
  const signers = await getSigners(blog);
  const userIDs = await Promise.all(
    signers.filter(({ role }) => role === "author").map(
      async ({ id, entity }) => {
        const { publickey, websites } = Entity.parse(entity.data);
        const website = websites?.[0];
        const key = await createKeyFromArmor(publickey.armor);
        const users = getUserIDsFromKey(undefined, key);
        return users.map((user) => {
          return { ...user, entity: id, website };
        })?.[0];
      },
    ),
  );
  return userIDs.filter(defined)?.[0];
}

export async function fromPosts<T extends Entry, U>(
  filter: (entry: CollectionEntry<"blog">) => entry is T,
  predicate: (entries: T[]) => U = identity as (entries: T[]) => U,
): Promise<U> {
  const entries = await getCollection<"blog", T>("blog", filter);
  return predicate(entries);
}
export const isEntry = (
  _entry: CollectionEntry<"blog">,
): _entry is Entry => true;
export const isOriginal = (
  entry: CollectionEntry<"blog">,
): entry is OriginalEntry => entry.data.kind === "original";
export const isTranslation = (
  entry: CollectionEntry<"blog">,
): entry is TranslationEntry => entry.data.kind === "translation";
export const isMicro = (
  entry: CollectionEntry<"blog">,
): entry is MicroEntry => entry.data.kind === "micro";

export async function getTranslationOriginal(
  translation: TranslationEntry,
): Promise<OriginalEntry | undefined> {
  if (!isTranslation(translation)) {
    throw new Error();
  }
  return await getEntry(translation.data.translationOf);
}