Some small refactoring and bug fixing
This commit is contained in:
parent
fa5ef0e282
commit
983d828f92
7 changed files with 70 additions and 103 deletions
|
@ -1,4 +1,4 @@
|
|||
const ACTOR = require("./actor")
|
||||
const ACTOR = require("./actor").default
|
||||
|
||||
module.exports = function(eleventyConfig) {
|
||||
// I'm .gitignoring my content for now, so 11ty should not ignore that
|
||||
|
|
27
actor.ts
27
actor.ts
|
@ -43,7 +43,7 @@ const attachment:{ type:"PropertyValue", name:string, value:string }[] = [
|
|||
}
|
||||
]
|
||||
|
||||
export default {
|
||||
const ACTOR = {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
|
@ -88,4 +88,27 @@ export default {
|
|||
attachment,
|
||||
icon,
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
export const handle = `${ACTOR.preferredUsername}@${HOSTNAME}`
|
||||
|
||||
export const webfinger = {
|
||||
subject: `acct:${ACTOR.preferredUsername}@${HOSTNAME}`,
|
||||
aliases: [ACTOR.id],
|
||||
links: [
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": ACTOR.url
|
||||
},
|
||||
{
|
||||
rel: "self",
|
||||
type: "application/activity+json",
|
||||
href: ACTOR.id,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default ACTOR
|
|
@ -1,8 +1,9 @@
|
|||
import * as db from "./db"
|
||||
import { reqIsActivityPub, verify } from "./request"
|
||||
import { verify } from "./request"
|
||||
import outbox from "./outbox"
|
||||
import inbox from "./inbox"
|
||||
import ACTOR from "../actor"
|
||||
import { activityPubTypes } from "./env"
|
||||
|
||||
export default (req: Request): Response | Promise<Response> | undefined => {
|
||||
const url = new URL(req.url)
|
||||
|
@ -22,6 +23,10 @@ export default (req: Request): Response | Promise<Response> | undefined => {
|
|||
return undefined
|
||||
}
|
||||
|
||||
export function reqIsActivityPub(req:Request) {
|
||||
const contentType = req.headers.get("Accept")
|
||||
return activityPubTypes.some(t => contentType?.includes(t))
|
||||
}
|
||||
|
||||
export function idsFromValue(value:any):string[] {
|
||||
if (!value) return []
|
||||
|
|
14
src/admin.ts
14
src/admin.ts
|
@ -1,8 +1,8 @@
|
|||
import { idsFromValue } from "./activitypub"
|
||||
import * as db from "./db"
|
||||
import { ADMIN_PASSWORD, ADMIN_USERNAME } from "./env"
|
||||
import { ADMIN_PASSWORD, ADMIN_USERNAME, activityPubTypes } from "./env"
|
||||
import outbox from "./outbox"
|
||||
import { activityMimeTypes, fetchObject } from "./request"
|
||||
import { fetchObject } from "./request"
|
||||
import ACTOR from "../actor"
|
||||
|
||||
export default (req: Request): Response | Promise<Response> | undefined => {
|
||||
|
@ -94,15 +94,19 @@ const follow = async (req:Request, handle:string):Promise<Response> => {
|
|||
catch {
|
||||
// this is not a valid url. Probably a someone@domain.tld format
|
||||
const [_, host] = handle.split('@')
|
||||
const res = await fetch(`https://${host}/.well-known/webfinger/?resource=acct:${handle}`)
|
||||
if(!host) return new Response('account not url or name@domain.tld', { status: 400 })
|
||||
|
||||
const res = await fetch(`https://${host}/.well-known/webfinger/?resource=acct:${handle}`, { headers: { 'accept': 'application/jrd+json'}})
|
||||
const webfinger = await res.json()
|
||||
if(!webfinger.links) return new Response("", { status: 404 })
|
||||
|
||||
const links:any[] = webfinger.links
|
||||
const actorLink = links.find(l => l.rel === "self" && (activityMimeTypes.includes(l.type)))
|
||||
const actorLink = links.find(l => l.rel === "self" && (activityPubTypes.includes(l.type)))
|
||||
if(!actorLink) return new Response("", { status: 404 })
|
||||
|
||||
url = actorLink.href
|
||||
}
|
||||
console.log(`Follow ${url}`)
|
||||
console.log(`Following ${url}`)
|
||||
|
||||
// send the follow request to the supplied actor
|
||||
return await outbox({
|
||||
|
|
59
src/env.ts
59
src/env.ts
|
@ -45,57 +45,8 @@ export const DEFAULT_DOCUMENTS = process.env.DEFAULT_DOCUMENTS || [
|
|||
'default.htm'
|
||||
]
|
||||
|
||||
// export const ACTOR_OBJ = {
|
||||
// "@context": [
|
||||
// "https://www.w3.org/ns/activitystreams",
|
||||
// "https://w3id.org/security/v1",
|
||||
// ],
|
||||
// id: ACTOR,
|
||||
// type: "Person",
|
||||
// following: `${ACTOR}/following`,
|
||||
// followers: `${ACTOR}/followers`,
|
||||
// inbox: `${ACTOR}/inbox`,
|
||||
// outbox: `${ACTOR}/outbox`,
|
||||
// //featured: `${ACTOR}/featured`,
|
||||
// //featuredTags: `${ACTOR}/tags`,
|
||||
// preferredUsername: ACCOUNT,
|
||||
// name: REAL_NAME,
|
||||
// summary: SUMMARY,
|
||||
// url: ACTOR,
|
||||
// manuallyApprovesFollowers: false,
|
||||
// discoverable: true,
|
||||
// published: "2023-09-14T00:00:00Z",
|
||||
// //devices: `${ACTOR}/devices`
|
||||
// alsoKnownAs: ALSO_KNOWN_AS,
|
||||
// publicKey: {
|
||||
// id: `${ACTOR}#main-key`,
|
||||
// owner: ACTOR,
|
||||
// publicKeyPem: PUBLIC_KEY,
|
||||
// },
|
||||
// // attachment: [
|
||||
// // {
|
||||
// // "type": "PropertyValue",
|
||||
// // "name": "Pronouns",
|
||||
// // "value": "he/him"
|
||||
// // },
|
||||
// // {
|
||||
// // "type": "PropertyValue",
|
||||
// // "name": "Website",
|
||||
// // "value": "<a href=\"https://death.id.au\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"><span class=\"invisible\">https://</span>death.id.au</a>"
|
||||
// // }
|
||||
// // ],
|
||||
// // tag: [],
|
||||
// // endpoints: {
|
||||
// // sharedInbox: N/A
|
||||
// // },
|
||||
// icon: {
|
||||
// type: "Image",
|
||||
// mediaType: "image/svg+xml",
|
||||
// url: BASE_URL + "/assets/img/avatar-tt.svg"
|
||||
// },
|
||||
// image: {
|
||||
// type: "Image",
|
||||
// mediaType: "image/jpeg",
|
||||
// url: BASE_URL + "/assets/img/banner-1500x500.jpg"
|
||||
// }
|
||||
// }
|
||||
export const activityPubTypes = [
|
||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||
'application/activity+json'
|
||||
]
|
||||
export const contentTypeHeader = { 'Content-Type': activityPubTypes[0]}
|
54
src/index.ts
54
src/index.ts
|
@ -1,11 +1,11 @@
|
|||
import { DEFAULT_DOCUMENTS, HOSTNAME, STATIC_PATH } from "./env"
|
||||
import { DEFAULT_DOCUMENTS, STATIC_PATH } from "./env"
|
||||
import admin from './admin'
|
||||
import activitypub from "./activitypub"
|
||||
import { fetchObject } from "./request"
|
||||
import path from "path"
|
||||
import { BunFile } from "bun"
|
||||
import { rebuild } from "./db"
|
||||
import ACTOR from "../actor"
|
||||
import { handle, webfinger } from "../actor"
|
||||
|
||||
rebuild()
|
||||
|
||||
|
@ -14,13 +14,30 @@ const server = Bun.serve({
|
|||
fetch(req: Request): Response | Promise<Response> {
|
||||
const url = new URL(req.url)
|
||||
|
||||
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`)
|
||||
// log the incoming request info
|
||||
console.info(`${new Date().toISOString()} 📥 ${req.method} ${req.url}`)
|
||||
|
||||
if(req.method === "GET" && url.pathname === "/.well-known/webfinger") {
|
||||
return webfinger(req, url.searchParams.get("resource"))
|
||||
// CORS route (for now, any domain has access)
|
||||
if(req.method === "OPTIONS") {
|
||||
const headers:any = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE',
|
||||
'Access-Control-Max-Age': '86400',
|
||||
}
|
||||
let h
|
||||
if (h = req.headers.get('Access-Control-Request-Headers'))
|
||||
headers['Access-Control-Allow-Headers'] = 'Accept, Content-Type, Authorization, Signature, Digest, Date, Host'
|
||||
return new Response('', { status: 204, headers })
|
||||
}
|
||||
else if(req.method === "GET" && url.pathname === "/fetch") {
|
||||
const object_url = url.searchParams.get('url')
|
||||
// Webfinger route
|
||||
else if(req.method === "GET" && url.pathname === "/.well-known/webfinger") {
|
||||
// make sure the resource matches the current handle. If it doesn't, 404
|
||||
if(url.searchParams.get("resource") !== `acct:${handle}`) return new Response("", { status: 404 })
|
||||
// return the webfinger
|
||||
return Response.json(webfinger, { headers: { 'content-type': 'application/jrd+json' }})
|
||||
}
|
||||
else if(req.method === "GET" && url.pathname.startsWith("/fetch/")) {
|
||||
const object_url = url.pathname.substring(7)
|
||||
if(!object_url) return new Response("No url supplied", { status: 400})
|
||||
|
||||
return fetchObject(object_url)
|
||||
|
@ -29,29 +46,6 @@ const server = Bun.serve({
|
|||
return admin(req) || activitypub(req) || staticFile(req)
|
||||
},
|
||||
});
|
||||
|
||||
const webfinger = async (req: Request, resource: string | null) => {
|
||||
|
||||
if(resource !== `acct:${ACTOR.preferredUsername}@${HOSTNAME}`) return new Response("", { status: 404 })
|
||||
|
||||
return Response.json({
|
||||
subject: `acct:${ACTOR.preferredUsername}@${HOSTNAME}`,
|
||||
aliases: [ACTOR.id],
|
||||
links: [
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": ACTOR.url
|
||||
},
|
||||
{
|
||||
rel: "self",
|
||||
type: "application/activity+json",
|
||||
href: ACTOR.id,
|
||||
},
|
||||
],
|
||||
}, { headers: { "content-type": "application/activity+json" }})
|
||||
}
|
||||
|
||||
const getDefaultDocument = async(base_path: string) => {
|
||||
for(const d of DEFAULT_DOCUMENTS){
|
||||
const filePath = path.join(base_path, d)
|
||||
|
|
|
@ -2,16 +2,6 @@ import forge from "node-forge" // import crypto from "node:crypto"
|
|||
import { PRIVATE_KEY } from "./env";
|
||||
import ACTOR from "../actor"
|
||||
|
||||
export const activityMimeTypes:string[] = ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']
|
||||
|
||||
export function reqIsActivityPub(req:Request) {
|
||||
const contentType = req.headers.get("Accept")
|
||||
const activityPub = contentType?.includes('application/activity+json')
|
||||
|| contentType?.includes('application/ld+json; profile="https://www.w3.org/ns/activitystreams"')
|
||||
|| contentType?.includes('application/ld+json')
|
||||
return activityPub
|
||||
}
|
||||
|
||||
// this function adds / modifies the appropriate headers for signing a request, then calls fetch
|
||||
export function signedFetch(url: string | URL | Request, init?: FetchRequestInit): Promise<Response>
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue