Needed a little modification here and there to make it work with the activity pub data structure, but it's looking pretty good!
228 lines
No EOL
8.7 KiB
TypeScript
228 lines
No EOL
8.7 KiB
TypeScript
import { ACCOUNT, ACTIVITY_INBOX_PATH, ACTIVITY_OUTBOX_PATH, ACTOR, CONTENT_PATH, DATA_PATH, POSTS_PATH, PUBLIC_KEY, STATIC_PATH } from "./env";
|
|
import path from "path"
|
|
import { readdir } from "fs/promises"
|
|
import { unlinkSync } from "node:fs"
|
|
import { fetchObject } from "./request";
|
|
import { idsFromValue } from "./activitypub";
|
|
const matter = require('gray-matter')
|
|
const Eleventy = require("@11ty/eleventy")
|
|
|
|
// rebuild the 11ty static pages
|
|
export async function rebuild() {
|
|
console.info(`Building 11ty from ${CONTENT_PATH}, to ${STATIC_PATH}`)
|
|
await new Eleventy(CONTENT_PATH, STATIC_PATH, { configPath: '.eleventy.js' }).write()
|
|
}
|
|
|
|
export async function createInboxActivity(activity:any, object_id:any) {
|
|
const activityFile = Bun.file(path.join(ACTIVITY_INBOX_PATH, `${object_id}.activity.json`))
|
|
await Bun.write(activityFile, JSON.stringify(activity))
|
|
}
|
|
|
|
export async function createOutboxActivity(activity:any, object_id:any) {
|
|
const activityFile = Bun.file(path.join(ACTIVITY_OUTBOX_PATH, `${object_id}.activity.json`))
|
|
await Bun.write(activityFile, JSON.stringify(activity))
|
|
}
|
|
|
|
export async function getInboxActivity(id:string) {
|
|
const file = Bun.file(path.join(ACTIVITY_INBOX_PATH, `${id}.activity.json`))
|
|
return await file.json()
|
|
}
|
|
|
|
export async function getOutboxActivity(id:string) {
|
|
const file = Bun.file(path.join(ACTIVITY_OUTBOX_PATH, `${id}.activity.json`))
|
|
return await file.json()
|
|
}
|
|
|
|
export async function listInboxActivities() {
|
|
return await Promise.all(
|
|
(await readdir(ACTIVITY_INBOX_PATH)).filter(v => v.endsWith('.activity.json'))
|
|
.map(async filename => await Bun.file(path.join(ACTIVITY_INBOX_PATH, filename)).json())
|
|
)
|
|
}
|
|
|
|
export async function listOutboxActivities() {
|
|
return await Promise.all(
|
|
(await readdir(ACTIVITY_OUTBOX_PATH)).filter(v => v.endsWith('.activity.json'))
|
|
.map(async filename => await Bun.file(path.join(ACTIVITY_OUTBOX_PATH, filename)).json())
|
|
)
|
|
}
|
|
|
|
export async function createPost(post_object:any, object_id:string) {
|
|
const file = Bun.file(path.join(POSTS_PATH, `${object_id}.md`))
|
|
let {type, object, inReplyTo} = post_object
|
|
if(inReplyTo && typeof inReplyTo === 'string') inReplyTo = await fetchObject(ACTOR, inReplyTo)
|
|
|
|
if(object){
|
|
let { content, published, id, attributedTo } = object
|
|
if(content as string) content = '> ' + content.replace('\n', '\n> ') + '\n'
|
|
else content = ""
|
|
|
|
content += post_object.content || ""
|
|
//TODO: add appropriate content for different types (e.g. like, etc)
|
|
const data:any = { id, published, attributedTo, type }
|
|
if(inReplyTo) data.inReplyTo = idsFromValue(inReplyTo).at(0)
|
|
await Bun.write(file, matter.stringify(content, data))
|
|
}
|
|
else {
|
|
const { content, published, id, attributedTo } = post_object
|
|
|
|
let reply_content = ""
|
|
if(!object && inReplyTo) {
|
|
reply_content = inReplyTo.content
|
|
if(reply_content as string) reply_content = '> ' + reply_content.replace('\n', '\n> ') + '\n'
|
|
else reply_content = ""
|
|
}
|
|
|
|
const data:any = { id, published, attributedTo, type }
|
|
if(inReplyTo) data.inReplyTo = idsFromValue(inReplyTo).at(0)
|
|
await Bun.write(file, matter.stringify((reply_content || "") + (content || ""), data))
|
|
}
|
|
rebuild()
|
|
}
|
|
|
|
export async function getPost(id:string) {
|
|
const file = Bun.file(path.join(POSTS_PATH, `${id}.md`))
|
|
const { data, content } = matter(await file.text())
|
|
return {
|
|
...data,
|
|
content: content.trim(),
|
|
local_id: id
|
|
}
|
|
}
|
|
|
|
export async function getPostByURL(url_id:string) {
|
|
if(!url_id || !url_id.startsWith(ACTOR + '/posts/')) return null
|
|
const match = url_id.match(/\/([0-9a-f]+)\/?$/)
|
|
const local_id = match ? match[1] : url_id
|
|
return await getPost(local_id)
|
|
}
|
|
|
|
export async function deletePost(id:string) {
|
|
unlinkSync(path.join(POSTS_PATH, id + '.md'))
|
|
rebuild()
|
|
}
|
|
|
|
export async function listPosts() {
|
|
return await Promise.all((await readdir(POSTS_PATH)).filter(v => v.endsWith('.md')).map(async filename => await getPost(filename.slice(0, -3))))
|
|
}
|
|
|
|
export async function createFollowing(handle:string, id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `following.json`))
|
|
const following_list = await file.json() as Array<any>
|
|
if(!following_list.find(v => v.id === id || v.handle === handle)) following_list.push({id, handle, createdAt: new Date().toISOString()})
|
|
await Bun.write(file, JSON.stringify(following_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function deleteFollowing(handle:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `following.json`))
|
|
const following_list = await file.json() as Array<any>
|
|
await Bun.write(file, JSON.stringify(following_list.filter(v => v.handle !== handle)))
|
|
rebuild()
|
|
}
|
|
|
|
export async function getFollowing(handle:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `following.json`))
|
|
const following_list = await file.json() as Array<any>
|
|
return following_list.find(v => v.handle === handle)
|
|
}
|
|
|
|
export async function listFollowing(onlyAccepted = true) {
|
|
const file = Bun.file(path.join(DATA_PATH, `following.json`))
|
|
return ((await file.json()) as Array<any>).filter(f => !onlyAccepted || f.accepted)
|
|
}
|
|
|
|
export async function acceptFollowing(handle:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `following.json`))
|
|
const following_list = await file.json() as Array<any>
|
|
const following = following_list.find(v => v.handle === handle)
|
|
if(following) following.accepted = new Date().toISOString()
|
|
await Bun.write(file, JSON.stringify(following_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function createFollower(actor:string, id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `followers.json`))
|
|
const followers_list = await file.json() as Array<any>
|
|
if(!followers_list.find(v => v.id === id || v.actor === actor)) followers_list.push({id, actor, createdAt: new Date().toISOString()})
|
|
await Bun.write(file, JSON.stringify(followers_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function deleteFollower(actor:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `followers.json`))
|
|
const followers_list = await file.json() as Array<any>
|
|
await Bun.write(file, JSON.stringify(followers_list.filter(v => v.actor !== actor)))
|
|
rebuild()
|
|
}
|
|
|
|
export async function getFollower(actor:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `followers.json`))
|
|
const followers_list = await file.json() as Array<any>
|
|
return followers_list.find(v => v.actor === actor)
|
|
}
|
|
|
|
export async function listFollowers() {
|
|
const file = Bun.file(path.join(DATA_PATH, `followers.json`))
|
|
return await file.json() as Array<any>
|
|
}
|
|
|
|
export async function createLiked(object_id:string, id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `liked.json`))
|
|
const liked_list = await file.json() as Array<any>
|
|
if(!liked_list.find(v => v.object_id === object_id)) liked_list.push({id, object_id, createdAt: new Date().toISOString()})
|
|
await Bun.write(file, JSON.stringify(liked_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function deleteLiked(object_id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `liked.json`))
|
|
const liked_list = await file.json() as Array<any>
|
|
await Bun.write(file, JSON.stringify(liked_list.filter(v => v.object_id !== object_id)))
|
|
rebuild()
|
|
}
|
|
|
|
export async function listLiked() {
|
|
const file = Bun.file(path.join(DATA_PATH, `liked.json`))
|
|
return await file.json() as Array<any>
|
|
}
|
|
|
|
export async function createDisliked(object_id:string, id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `disliked.json`))
|
|
const disliked_list = await file.json() as Array<any>
|
|
if(!disliked_list.find(v => v.object_id === object_id)) disliked_list.push({id, object_id, createdAt: new Date().toISOString()})
|
|
await Bun.write(file, JSON.stringify(disliked_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function deleteDisliked(object_id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `disliked.json`))
|
|
const disliked_list = await file.json() as Array<any>
|
|
await Bun.write(file, JSON.stringify(disliked_list.filter(v => v.object_id !== object_id)))
|
|
rebuild()
|
|
}
|
|
|
|
export async function listDisliked() {
|
|
const file = Bun.file(path.join(DATA_PATH, `disliked.json`))
|
|
return await file.json() as Array<any>
|
|
}
|
|
|
|
export async function createShared(object_id:string, id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `shared.json`))
|
|
const shared_list = await file.json() as Array<any>
|
|
if(!shared_list.find(v => v.object_id === object_id)) shared_list.push({id, object_id, createdAt: new Date().toISOString()})
|
|
await Bun.write(file, JSON.stringify(shared_list))
|
|
rebuild()
|
|
}
|
|
|
|
export async function deleteShared(object_id:string) {
|
|
const file = Bun.file(path.join(DATA_PATH, `shared.json`))
|
|
const shared_list = await file.json() as Array<any>
|
|
await Bun.write(file, JSON.stringify(shared_list.filter(v => v.object_id !== object_id)))
|
|
rebuild()
|
|
}
|
|
|
|
export async function listShared() {
|
|
const file = Bun.file(path.join(DATA_PATH, `shared.json`))
|
|
return await file.json() as Array<any>
|
|
} |