micro_blog_repo_fetchers/old_node_src/pocketbase.ts

223 lines
5.8 KiB
TypeScript

import type { Logger } from "pino";
import PocketBase from "pocketbase";
export type MicroBlogPostImage = {
id: string;
collectionId: string;
image: string;
alt?: string;
remoteURL: string;
};
export type MicroBlogPostTag = {
id: string;
tag: string;
};
export type MicroBlogPostSource =
| "blue_sky"
| "mastodon"
| "pleroma"
| "pixelfed"
| "nostr";
export type MicroBlogPost = {
source: MicroBlogPostSource;
fullPost: any;
remoteId: string;
authorId: string;
id: string;
posted: string;
expand: {
images?: MicroBlogPostImage[];
tags?: {
id: string;
}[];
};
};
export class MicroBlogBackend {
private pb: PocketBase;
private clientSetTime?: Date;
constructor(private logger: Logger) {
this.pb = new PocketBase(process.env.POCKET_BASE_HOST);
}
private async login() {
const pw = process.env.POCKET_BASE_PW;
const userName = process.env.POCKET_BASE_USER;
if (!pw) {
this.logger.error("POCKET_BASE_PW env var not set");
throw new Error("POCKET_BASE_PW env var not set");
}
if (!userName) {
this.logger.error("POCKET_BASE_USER env var not set");
throw new Error("POCKET_BASE_USER env var not set");
}
this.logger.info({ userName }, "Logging in to pocketbase");
await this.pb.collection("users").authWithPassword(userName, pw);
this.clientSetTime = new Date();
}
private async checkLogin() {
if (!this.clientSetTime) {
await this.login();
return;
}
const now = new Date();
const diff = now.getTime() - this.clientSetTime.getTime();
const day = 86_400_000;
if (diff > day) {
await this.login();
return;
}
}
public async getLatestPostBySource(
postSource: MicroBlogPostSource,
): Promise<MicroBlogPost | undefined> {
await this.checkLogin();
try {
const post = await this.pb
.collection<MicroBlogPost>("micro_blog_posts")
.getFirstListItem(`source = '${postSource}'`, { sort: "-posted" });
return post;
} catch (error: any) {
if (error.status === 404) {
return undefined;
}
throw error;
}
}
public async getLatestPostRemoteIDBySource(postSource: MicroBlogPostSource) {
await this.checkLogin();
const post = await this.getLatestPostBySource(postSource);
return post?.remoteId;
}
async getTag(tag: string): Promise<MicroBlogPostTag | undefined> {
await this.checkLogin();
try {
const remoteTag = await this.pb
.collection<MicroBlogPostTag>("micro_blog_tags")
.getFirstListItem(`tag = '${tag}'`);
return remoteTag;
} catch (e: any) {
if (e.status === 404) {
return undefined;
}
throw e;
}
}
async getImageByRemoteURL(
remoteURL: string,
): Promise<MicroBlogPostImage | undefined> {
await this.checkLogin();
try {
const remoteImage = await this.pb
.collection<MicroBlogPostImage>("micro_blog_images")
.getFirstListItem(`remoteURL = '${remoteURL}'`);
return remoteImage;
} catch (e: any) {
if (e.status === 404) {
return undefined;
}
throw e;
}
}
private async checkForPost(
remoteId: string,
): Promise<MicroBlogPost | undefined> {
await this.checkLogin();
try {
return await this.pb
.collection<MicroBlogPost>("micro_blog_posts")
.getFirstListItem(`remoteId = '${remoteId}'`);
} catch (e: any) {
if (e.status === 404) {
return undefined;
}
throw e;
}
}
public async savePost(
post: Omit<MicroBlogPost, "id" | "expand">,
): Promise<MicroBlogPost> {
await this.checkLogin();
const existingPost = await this.checkForPost(post.remoteId);
if (!existingPost) {
return await this.pb
.collection<MicroBlogPost>("micro_blog_posts")
.create(post);
}
this.logger.info({ existingPost }, "Found existing post");
return existingPost;
}
public async setTag(rawTag: string, postId: string) {
await this.checkLogin();
let tag = await this.getTag(rawTag);
if (!tag) {
tag = await this.pb
.collection<MicroBlogPostTag>("micro_blog_tags")
.create({ tag: rawTag });
}
if (!tag) {
throw new Error("Failed to create tag");
}
await this.pb.collection("micro_blog_posts").update(postId, {
"tags+": tag.id,
});
}
public async saveAndSetImage(
imageToSave: Omit<MicroBlogPostImage, "id" | "image" | "collectionId">,
postId: string,
) {
await this.checkLogin();
let image = await this.getImageByRemoteURL(imageToSave.remoteURL);
if (!image) {
const imageResponse = await fetch(imageToSave.remoteURL);
if (!imageResponse.ok) {
throw new Error("Failed to download image");
}
const imageBlob = await imageResponse.blob();
const imageFile = new File([imageBlob], "image.jpg", {
type: imageBlob.type,
});
const data = {
...imageToSave,
image: imageFile,
};
image = await this.pb
.collection<MicroBlogPostTag>("micro_blog_images")
.create(data);
this.logger.info({ image }, "Created image");
}
if (!image) {
throw new Error("Failed to create image");
}
const res = await this.pb.collection("micro_blog_posts").update(postId, {
"images+": image.id,
});
this.logger.info({ res }, "Updated post with image");
}
public async getPosts(page: number, limit = 20) {
await this.checkLogin();
const resultList = await this.pb
.collection<MicroBlogPost>("micro_blog_posts")
.getList(page, limit, {
sort: "-posted",
expand: "images,tags",
filter: `(source = "blue_sky" || source = "pleroma")`,
// filter: 'source = "pleroma"',
});
return resultList;
}
}