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
|
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";
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";
|