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) {
|
module.exports = function(eleventyConfig) {
|
||||||
// I'm .gitignoring my content for now, so 11ty should not ignore that
|
// 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": [
|
"@context": [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"https://w3id.org/security/v1",
|
"https://w3id.org/security/v1",
|
||||||
|
@ -88,4 +88,27 @@ export default {
|
||||||
attachment,
|
attachment,
|
||||||
icon,
|
icon,
|
||||||
image
|
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 * as db from "./db"
|
||||||
import { reqIsActivityPub, verify } from "./request"
|
import { verify } from "./request"
|
||||||
import outbox from "./outbox"
|
import outbox from "./outbox"
|
||||||
import inbox from "./inbox"
|
import inbox from "./inbox"
|
||||||
import ACTOR from "../actor"
|
import ACTOR from "../actor"
|
||||||
|
import { activityPubTypes } from "./env"
|
||||||
|
|
||||||
export default (req: Request): Response | Promise<Response> | undefined => {
|
export default (req: Request): Response | Promise<Response> | undefined => {
|
||||||
const url = new URL(req.url)
|
const url = new URL(req.url)
|
||||||
|
@ -22,6 +23,10 @@ export default (req: Request): Response | Promise<Response> | undefined => {
|
||||||
return 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[] {
|
export function idsFromValue(value:any):string[] {
|
||||||
if (!value) return []
|
if (!value) return []
|
||||||
|
|
14
src/admin.ts
14
src/admin.ts
|
@ -1,8 +1,8 @@
|
||||||
import { idsFromValue } from "./activitypub"
|
import { idsFromValue } from "./activitypub"
|
||||||
import * as db from "./db"
|
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 outbox from "./outbox"
|
||||||
import { activityMimeTypes, fetchObject } from "./request"
|
import { fetchObject } from "./request"
|
||||||
import ACTOR from "../actor"
|
import ACTOR from "../actor"
|
||||||
|
|
||||||
export default (req: Request): Response | Promise<Response> | undefined => {
|
export default (req: Request): Response | Promise<Response> | undefined => {
|
||||||
|
@ -94,15 +94,19 @@ const follow = async (req:Request, handle:string):Promise<Response> => {
|
||||||
catch {
|
catch {
|
||||||
// this is not a valid url. Probably a someone@domain.tld format
|
// this is not a valid url. Probably a someone@domain.tld format
|
||||||
const [_, host] = handle.split('@')
|
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()
|
const webfinger = await res.json()
|
||||||
if(!webfinger.links) return new Response("", { status: 404 })
|
if(!webfinger.links) return new Response("", { status: 404 })
|
||||||
|
|
||||||
const links:any[] = webfinger.links
|
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 })
|
if(!actorLink) return new Response("", { status: 404 })
|
||||||
|
|
||||||
url = actorLink.href
|
url = actorLink.href
|
||||||
}
|
}
|
||||||
console.log(`Follow ${url}`)
|
console.log(`Following ${url}`)
|
||||||
|
|
||||||
// send the follow request to the supplied actor
|
// send the follow request to the supplied actor
|
||||||
return await outbox({
|
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'
|
'default.htm'
|
||||||
]
|
]
|
||||||
|
|
||||||
// export const ACTOR_OBJ = {
|
export const activityPubTypes = [
|
||||||
// "@context": [
|
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
// "https://www.w3.org/ns/activitystreams",
|
'application/activity+json'
|
||||||
// "https://w3id.org/security/v1",
|
]
|
||||||
// ],
|
export const contentTypeHeader = { 'Content-Type': activityPubTypes[0]}
|
||||||
// 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"
|
|
||||||
// }
|
|
||||||
// }
|
|
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 admin from './admin'
|
||||||
import activitypub from "./activitypub"
|
import activitypub from "./activitypub"
|
||||||
import { fetchObject } from "./request"
|
import { fetchObject } from "./request"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { BunFile } from "bun"
|
import { BunFile } from "bun"
|
||||||
import { rebuild } from "./db"
|
import { rebuild } from "./db"
|
||||||
import ACTOR from "../actor"
|
import { handle, webfinger } from "../actor"
|
||||||
|
|
||||||
rebuild()
|
rebuild()
|
||||||
|
|
||||||
|
@ -14,13 +14,30 @@ const server = Bun.serve({
|
||||||
fetch(req: Request): Response | Promise<Response> {
|
fetch(req: Request): Response | Promise<Response> {
|
||||||
const url = new URL(req.url)
|
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") {
|
// CORS route (for now, any domain has access)
|
||||||
return webfinger(req, url.searchParams.get("resource"))
|
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") {
|
// Webfinger route
|
||||||
const object_url = url.searchParams.get('url')
|
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})
|
if(!object_url) return new Response("No url supplied", { status: 400})
|
||||||
|
|
||||||
return fetchObject(object_url)
|
return fetchObject(object_url)
|
||||||
|
@ -29,29 +46,6 @@ const server = Bun.serve({
|
||||||
return admin(req) || activitypub(req) || staticFile(req)
|
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) => {
|
const getDefaultDocument = async(base_path: string) => {
|
||||||
for(const d of DEFAULT_DOCUMENTS){
|
for(const d of DEFAULT_DOCUMENTS){
|
||||||
const filePath = path.join(base_path, d)
|
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 { PRIVATE_KEY } from "./env";
|
||||||
import ACTOR from "../actor"
|
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
|
// 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>
|
export function signedFetch(url: string | URL | Request, init?: FetchRequestInit): Promise<Response>
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue