style: format
parent
fe109f7fdb
commit
bf09166c51
|
@ -1,77 +1,98 @@
|
|||
declare module '@peertube/http-signature' {
|
||||
import type { IncomingMessage, ClientRequest } from 'node:http';
|
||||
declare module "@peertube/http-signature" {
|
||||
import type { IncomingMessage, ClientRequest } from "node:http";
|
||||
|
||||
interface ISignature {
|
||||
keyId: string;
|
||||
algorithm: string;
|
||||
headers: string[];
|
||||
signature: string;
|
||||
}
|
||||
interface ISignature {
|
||||
keyId: string;
|
||||
algorithm: string;
|
||||
headers: string[];
|
||||
signature: string;
|
||||
}
|
||||
|
||||
interface IOptions {
|
||||
headers?: string[];
|
||||
algorithm?: string;
|
||||
strict?: boolean;
|
||||
authorizationHeaderName?: string;
|
||||
}
|
||||
interface IOptions {
|
||||
headers?: string[];
|
||||
algorithm?: string;
|
||||
strict?: boolean;
|
||||
authorizationHeaderName?: string;
|
||||
}
|
||||
|
||||
interface IParseRequestOptions extends IOptions {
|
||||
clockSkew?: number;
|
||||
}
|
||||
interface IParseRequestOptions extends IOptions {
|
||||
clockSkew?: number;
|
||||
}
|
||||
|
||||
interface IParsedSignature {
|
||||
scheme: string;
|
||||
params: ISignature;
|
||||
signingString: string;
|
||||
algorithm: string;
|
||||
keyId: string;
|
||||
}
|
||||
interface IParsedSignature {
|
||||
scheme: string;
|
||||
params: ISignature;
|
||||
signingString: string;
|
||||
algorithm: string;
|
||||
keyId: string;
|
||||
}
|
||||
|
||||
type RequestSignerConstructorOptions =
|
||||
IRequestSignerConstructorOptionsFromProperties |
|
||||
IRequestSignerConstructorOptionsFromFunction;
|
||||
type RequestSignerConstructorOptions =
|
||||
| IRequestSignerConstructorOptionsFromProperties
|
||||
| IRequestSignerConstructorOptionsFromFunction;
|
||||
|
||||
interface IRequestSignerConstructorOptionsFromProperties {
|
||||
keyId: string;
|
||||
key: string | Buffer;
|
||||
algorithm?: string;
|
||||
}
|
||||
interface IRequestSignerConstructorOptionsFromProperties {
|
||||
keyId: string;
|
||||
key: string | Buffer;
|
||||
algorithm?: string;
|
||||
}
|
||||
|
||||
interface IRequestSignerConstructorOptionsFromFunction {
|
||||
sign?: (data: string, cb: (err: any, sig: ISignature) => void) => void;
|
||||
}
|
||||
interface IRequestSignerConstructorOptionsFromFunction {
|
||||
sign?: (data: string, cb: (err: any, sig: ISignature) => void) => void;
|
||||
}
|
||||
|
||||
class RequestSigner {
|
||||
constructor(options: RequestSignerConstructorOptions);
|
||||
class RequestSigner {
|
||||
constructor(options: RequestSignerConstructorOptions);
|
||||
|
||||
public writeHeader(header: string, value: string): string;
|
||||
public writeHeader(header: string, value: string): string;
|
||||
|
||||
public writeDateHeader(): string;
|
||||
public writeDateHeader(): string;
|
||||
|
||||
public writeTarget(method: string, path: string): void;
|
||||
public writeTarget(method: string, path: string): void;
|
||||
|
||||
public sign(cb: (err: any, authz: string) => void): void;
|
||||
}
|
||||
public sign(cb: (err: any, authz: string) => void): void;
|
||||
}
|
||||
|
||||
interface ISignRequestOptions extends IOptions {
|
||||
keyId: string;
|
||||
key: string;
|
||||
httpVersion?: string;
|
||||
}
|
||||
interface ISignRequestOptions extends IOptions {
|
||||
keyId: string;
|
||||
key: string;
|
||||
httpVersion?: string;
|
||||
}
|
||||
|
||||
export function parse(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
|
||||
export function parseRequest(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
|
||||
export function parse(
|
||||
request: IncomingMessage,
|
||||
options?: IParseRequestOptions
|
||||
): IParsedSignature;
|
||||
export function parseRequest(
|
||||
request: IncomingMessage,
|
||||
options?: IParseRequestOptions
|
||||
): IParsedSignature;
|
||||
|
||||
export function sign(request: ClientRequest, options: ISignRequestOptions): boolean;
|
||||
export function signRequest(request: ClientRequest, options: ISignRequestOptions): boolean;
|
||||
export function createSigner(): RequestSigner;
|
||||
export function isSigner(obj: any): obj is RequestSigner;
|
||||
export function sign(
|
||||
request: ClientRequest,
|
||||
options: ISignRequestOptions
|
||||
): boolean;
|
||||
export function signRequest(
|
||||
request: ClientRequest,
|
||||
options: ISignRequestOptions
|
||||
): boolean;
|
||||
export function createSigner(): RequestSigner;
|
||||
export function isSigner(obj: any): obj is RequestSigner;
|
||||
|
||||
export function sshKeyToPEM(key: string): string;
|
||||
export function sshKeyFingerprint(key: string): string;
|
||||
export function pemToRsaSSHKey(pem: string, comment: string): string;
|
||||
export function sshKeyToPEM(key: string): string;
|
||||
export function sshKeyFingerprint(key: string): string;
|
||||
export function pemToRsaSSHKey(pem: string, comment: string): string;
|
||||
|
||||
export function verify(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
|
||||
export function verifySignature(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
|
||||
export function verifyHMAC(parsedSignature: IParsedSignature, secret: string): boolean;
|
||||
}
|
||||
export function verify(
|
||||
parsedSignature: IParsedSignature,
|
||||
pubkey: string | Buffer
|
||||
): boolean;
|
||||
export function verifySignature(
|
||||
parsedSignature: IParsedSignature,
|
||||
pubkey: string | Buffer
|
||||
): boolean;
|
||||
export function verifyHMAC(
|
||||
parsedSignature: IParsedSignature,
|
||||
secret: string
|
||||
): boolean;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { env } from "process"
|
||||
import { env } from "process";
|
||||
|
||||
export const ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||
export const USER_AGENT = `xtex-home/1.0@${env['VERCEL_GIT_COMMIT_SHA']} (${env['NEXT_PUBLIC_VERCEL_URL']})`
|
||||
export const ACCEPT_HEADER =
|
||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
export const USER_AGENT = `xtex-home/1.0@${env["VERCEL_GIT_COMMIT_SHA"]} (${env["NEXT_PUBLIC_VERCEL_URL"]})`;
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import { signRequest } from "@peertube/http-signature";
|
||||
import { BaseEntity } from "activitypub-core-types/lib/activitypub/Core/Entity"
|
||||
import { BaseEntity } from "activitypub-core-types/lib/activitypub/Core/Entity";
|
||||
import { ClientRequest } from "http";
|
||||
import { env } from "process";
|
||||
import { ACCEPT_HEADER, USER_AGENT } from "./consts"
|
||||
import { ACCEPT_HEADER, USER_AGENT } from "./consts";
|
||||
|
||||
export async function deliveryAPActivity(url: URL, doc: BaseEntity) {
|
||||
console.log(`deliverying AP document ${url}`)
|
||||
console.log(`deliverying AP document ${url}`);
|
||||
|
||||
const request = {
|
||||
url: url.toString(),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Date': new Date().toUTCString(),
|
||||
'Host': url.hostname,
|
||||
},
|
||||
}
|
||||
const signature = signRequest(request as unknown as ClientRequest, {
|
||||
key: env['XTEX_HOME_AP_PRIV_KEY']!!,
|
||||
keyId: 'XTEX-HOME-AP-INSTANCE-ACTOR',
|
||||
})
|
||||
console.log(signature)
|
||||
const request = {
|
||||
url: url.toString(),
|
||||
method: "POST",
|
||||
headers: {
|
||||
Date: new Date().toUTCString(),
|
||||
Host: url.hostname,
|
||||
},
|
||||
};
|
||||
const signature = signRequest(request as unknown as ClientRequest, {
|
||||
key: env["XTEX_HOME_AP_PRIV_KEY"]!!,
|
||||
keyId: "XTEX-HOME-AP-INSTANCE-ACTOR",
|
||||
});
|
||||
console.log(signature);
|
||||
|
||||
const result = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(doc),
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': ACCEPT_HEADER,
|
||||
'Content-Type': 'application/activity+json',
|
||||
}
|
||||
})
|
||||
const result = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(doc),
|
||||
headers: {
|
||||
"User-Agent": USER_AGENT,
|
||||
Accept: ACCEPT_HEADER,
|
||||
"Content-Type": "application/activity+json",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`delivered AP doc ${url}: ${result.status}`)
|
||||
console.log(`delivered AP doc ${url}: ${result.status}`);
|
||||
}
|
||||
|
|
|
@ -1,35 +1,40 @@
|
|||
import { CoreObject, Entity, EntityReference, Link } from "activitypub-core-types/lib/activitypub";
|
||||
import {
|
||||
CoreObject,
|
||||
Entity,
|
||||
EntityReference,
|
||||
Link,
|
||||
} from "activitypub-core-types/lib/activitypub";
|
||||
import { BaseEntity } from "activitypub-core-types/lib/activitypub/Core/Entity";
|
||||
import { URL } from "url";
|
||||
import { ACCEPT_HEADER, USER_AGENT } from "./consts";
|
||||
|
||||
export async function resolveApEntity(ref: EntityReference): Promise<Entity> {
|
||||
if (typeof ref == 'string') {
|
||||
return await getApDocument(ref) as Entity
|
||||
} else if (typeof (ref as Link).href == 'string') {
|
||||
return resolveApEntity((ref as Link).href!!)
|
||||
} else if (typeof (ref as CoreObject).type == 'string') {
|
||||
return ref as CoreObject
|
||||
} else {
|
||||
throw `${ref} cannot be resolved as a AP entity`
|
||||
}
|
||||
if (typeof ref == "string") {
|
||||
return (await getApDocument(ref)) as Entity;
|
||||
} else if (typeof (ref as Link).href == "string") {
|
||||
return resolveApEntity((ref as Link).href!!);
|
||||
} else if (typeof (ref as CoreObject).type == "string") {
|
||||
return ref as CoreObject;
|
||||
} else {
|
||||
throw `${ref} cannot be resolved as a AP entity`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getApDocument(url: URL): Promise<BaseEntity> {
|
||||
console.log(`resolving AP document ${url}, UA: ${USER_AGENT}`)
|
||||
const result = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': ACCEPT_HEADER,
|
||||
}
|
||||
})
|
||||
console.log(`resolved AP doc ${url}: ${result.status}`)
|
||||
const json = await result.text()
|
||||
try {
|
||||
return JSON.parse(json) as BaseEntity
|
||||
} catch (e) {
|
||||
console.error(json)
|
||||
throw e
|
||||
}
|
||||
console.log(`resolving AP document ${url}, UA: ${USER_AGENT}`);
|
||||
const result = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"User-Agent": USER_AGENT,
|
||||
Accept: ACCEPT_HEADER,
|
||||
},
|
||||
});
|
||||
console.log(`resolved AP doc ${url}: ${result.status}`);
|
||||
const json = await result.text();
|
||||
try {
|
||||
return JSON.parse(json) as BaseEntity;
|
||||
} catch (e) {
|
||||
console.error(json);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,71 +6,95 @@ const nextConfig = {
|
|||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/blog/:path*',
|
||||
destination: 'https://blog.xtexx.ml/:path*',
|
||||
source: "/blog/:path*",
|
||||
destination: "https://blog.xtexx.ml/:path*",
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
];
|
||||
},
|
||||
async headers() {//application/ld+json; profile="https://www.w3.org/ns/activitystreams"
|
||||
async headers() {
|
||||
const nodeinfo = {
|
||||
key: 'Content-Type',
|
||||
value: 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"',
|
||||
}
|
||||
key: "Content-Type",
|
||||
value:
|
||||
'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"',
|
||||
};
|
||||
const cors = [
|
||||
{
|
||||
key: "Access-Control-Allow-Origin",
|
||||
value: "*",
|
||||
},
|
||||
{
|
||||
key: "Access-Control-Expose-Headers",
|
||||
value: "*",
|
||||
},
|
||||
{
|
||||
key: "Access-Control-Max-Age",
|
||||
value: "86400",
|
||||
},
|
||||
{
|
||||
key: "Access-Control-Allow-Methods",
|
||||
value: "*",
|
||||
},
|
||||
{
|
||||
key: "Access-Control-Allow-Headers",
|
||||
value: "*",
|
||||
},
|
||||
];
|
||||
return [
|
||||
{
|
||||
source: '/.well-known/nodeinfo',
|
||||
source: "/.well-known/nodeinfo",
|
||||
headers: [nodeinfo],
|
||||
},
|
||||
{
|
||||
source: '/nodeinfo.json',
|
||||
source: "/nodeinfo.json",
|
||||
headers: [nodeinfo],
|
||||
},
|
||||
{
|
||||
source: '/.well-known/host-meta',
|
||||
source: "/.well-known/host-meta",
|
||||
headers: [
|
||||
{
|
||||
key: 'Content-Type',
|
||||
value: 'application/xrd+xml',
|
||||
key: "Content-Type",
|
||||
value: "application/xrd+xml",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/.well-known/matrix/:path*',
|
||||
source: "/.well-known/matrix/:path*",
|
||||
headers: [
|
||||
{
|
||||
key: 'Content-Type',
|
||||
value: 'application/json; charset=utf-8',
|
||||
key: "Content-Type",
|
||||
value: "application/json; charset=utf-8",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/ap/:path*',
|
||||
source: "/ap/:path*",
|
||||
headers: [
|
||||
{
|
||||
key: 'Content-Type',
|
||||
value: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8',
|
||||
key: "Content-Type",
|
||||
value:
|
||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/.well-known/host-meta.json',
|
||||
destination: '/api/host-meta.json',
|
||||
source: "/.well-known/host-meta.json",
|
||||
destination: "/api/host-meta.json",
|
||||
},
|
||||
{
|
||||
source: '/.well-known/webfinger',
|
||||
destination: '/api/webfinger',
|
||||
source: "/.well-known/webfinger",
|
||||
destination: "/api/webfinger",
|
||||
},
|
||||
{
|
||||
source: '/ap/api/:path*',
|
||||
destination: '/api/ap/:path*',
|
||||
source: "/ap/api/:path*",
|
||||
destination: "/api/ap/:path*",
|
||||
},
|
||||
]
|
||||
];
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = nextConfig
|
||||
module.exports = nextConfig;
|
||||
|
|
|
@ -1,39 +1,60 @@
|
|||
import * as httpSignature from '@peertube/http-signature'
|
||||
import { Activity, ActivityTypes, Actor, Reject } from 'activitypub-core-types/lib/activitypub'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { deliveryAPActivity } from '../../../ap/delivery'
|
||||
import { resolveApEntity } from '../../../ap/resolver'
|
||||
import * as httpSignature from "@peertube/http-signature";
|
||||
import {
|
||||
Activity,
|
||||
ActivityTypes,
|
||||
Actor,
|
||||
Reject,
|
||||
} from "activitypub-core-types/lib/activitypub";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { deliveryAPActivity } from "../../../ap/delivery";
|
||||
import { resolveApEntity } from "../../../ap/resolver";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const signature = req.headers['signature']
|
||||
const activity = req.body as Activity
|
||||
if (signature == null) {
|
||||
return res.status(400).send('no signature')
|
||||
const signature = req.headers["signature"];
|
||||
const activity = req.body as Activity;
|
||||
if (signature == null) {
|
||||
return res.status(400).send("no signature");
|
||||
}
|
||||
if (activity.actor instanceof Array || !activity.actor) {
|
||||
return res.status(400).send("actor is more than one AS entity ref");
|
||||
}
|
||||
const actor = (await resolveApEntity(activity.actor)) as Actor;
|
||||
if (
|
||||
!httpSignature.verifySignature(
|
||||
httpSignature.parseRequest(req),
|
||||
actor.publicKey!!.publicKeyPem
|
||||
)
|
||||
) {
|
||||
console.error(
|
||||
`signature check failed, actor: ${actor}, provided: ${signature}`
|
||||
);
|
||||
return res
|
||||
.status(400)
|
||||
.send(`signature check failed, expected: ${actor.publicKey}`);
|
||||
}
|
||||
if (activity.type == ActivityTypes.FOLLOW) {
|
||||
// follow request
|
||||
console.log(`sending follow Reject to ${actor.inbox}`);
|
||||
if (actor.inbox! instanceof URL) {
|
||||
return res.status(400).send("inbox is not a standalone doc");
|
||||
}
|
||||
if (activity.actor instanceof Array || !activity.actor) {
|
||||
return res.status(400).send('actor is more than one AS entity ref')
|
||||
}
|
||||
const actor = (await resolveApEntity(activity.actor) as Actor)
|
||||
if (!httpSignature.verifySignature(httpSignature.parseRequest(req), actor.publicKey!!.publicKeyPem)) {
|
||||
console.error(`signature check failed, actor: ${actor}, provided: ${signature}`)
|
||||
return res.status(400).send(`signature check failed, expected: ${actor.publicKey}`)
|
||||
}
|
||||
if (activity.type == ActivityTypes.FOLLOW) {
|
||||
// follow request
|
||||
console.log(`sending follow Reject to ${actor.inbox}`)
|
||||
if (actor.inbox! instanceof URL) {
|
||||
return res.status(400).send('inbox is not a standalone doc')
|
||||
}
|
||||
await deliveryAPActivity(actor.inbox as unknown as URL, {
|
||||
type: ActivityTypes.REJECT,
|
||||
id: new URL(`https://xtexx.ml/ap/reject_follows/${encodeURI(actor.id!!.toString())}`),
|
||||
actor: new URL('https://xtexx.ml/ap/actor.json'),
|
||||
object: activity.id,
|
||||
target: actor.id,
|
||||
} as Reject)
|
||||
}
|
||||
res.status(200).end()
|
||||
await deliveryAPActivity(
|
||||
actor.inbox as unknown as URL,
|
||||
{
|
||||
type: ActivityTypes.REJECT,
|
||||
id: new URL(
|
||||
`https://xtexx.ml/ap/reject_follows/${encodeURI(
|
||||
actor.id!!.toString()
|
||||
)}`
|
||||
),
|
||||
actor: new URL("https://xtexx.ml/ap/actor.json"),
|
||||
object: activity.id,
|
||||
target: actor.id,
|
||||
} as Reject
|
||||
);
|
||||
}
|
||||
res.status(200).end();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
console.log(req.body)
|
||||
console.log(JSON.stringify(req.body))
|
||||
res.status(200).end()
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
console.log(req.body);
|
||||
console.log(JSON.stringify(req.body));
|
||||
res.status(200).end();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { Link } from 'activitypub-core-types/lib/activitypub'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import site_lrs from '../../data/site_lrs.json'
|
||||
import { Link } from "activitypub-core-types/lib/activitypub";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import site_lrs from "../../data/site_lrs.json";
|
||||
|
||||
type Data = {
|
||||
links: Link[],
|
||||
}
|
||||
links: Link[];
|
||||
};
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({
|
||||
links: site_lrs as Link[],
|
||||
})
|
||||
res.status(200).json({
|
||||
links: site_lrs as Link[],
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,74 +1,75 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import site_lrs from '../../data/site_lrs.json'
|
||||
import webfingerData from '../../data/webfinger.json'
|
||||
import { collect as collectEcho } from './whoami'
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import site_lrs from "../../data/site_lrs.json";
|
||||
import webfingerData from "../../data/webfinger.json";
|
||||
import { collect as collectEcho } from "./whoami";
|
||||
|
||||
type Data = {
|
||||
subject: string,
|
||||
aliases: string[] | undefined,
|
||||
links: any[],
|
||||
}
|
||||
subject: string;
|
||||
aliases: string[] | undefined;
|
||||
links: any[];
|
||||
};
|
||||
|
||||
export function lookupData(username: string): {
|
||||
aliases: string[],
|
||||
links: any[]
|
||||
} | undefined {
|
||||
let data = (webfingerData as any[string])[username]
|
||||
if (data != undefined && data.aliasTo != undefined)
|
||||
return lookupData(data.aliasTo)
|
||||
return data
|
||||
export function lookupData(username: string):
|
||||
| {
|
||||
aliases: string[];
|
||||
links: any[];
|
||||
}
|
||||
| undefined {
|
||||
let data = (webfingerData as any[string])[username];
|
||||
if (data != undefined && data.aliasTo != undefined)
|
||||
return lookupData(data.aliasTo);
|
||||
return data;
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
let uri: string = req.query['resource'] as string
|
||||
if (uri == undefined) {
|
||||
res.status(400).end('"resource" query param is not provided')
|
||||
return
|
||||
let uri: string = req.query["resource"] as string;
|
||||
if (uri == undefined) {
|
||||
res.status(400).end('"resource" query param is not provided');
|
||||
return;
|
||||
}
|
||||
if (!uri.startsWith("acct:")) {
|
||||
res.status(404).end("Only acct urls are allowed");
|
||||
return;
|
||||
}
|
||||
if (uri.indexOf("@") == -1) {
|
||||
res.status(404).end("Username is not provided");
|
||||
return;
|
||||
}
|
||||
let username = uri.substring(5, uri.indexOf("@")).toLowerCase();
|
||||
if (username.startsWith("//")) username = username.substring(2);
|
||||
let aliases: string[] = [];
|
||||
let links: any[] = [];
|
||||
switch (username) {
|
||||
case "this": {
|
||||
links = site_lrs;
|
||||
break;
|
||||
}
|
||||
if (!uri.startsWith('acct:')) {
|
||||
res.status(404).end('Only acct urls are allowed')
|
||||
return
|
||||
case "echo": {
|
||||
links = [
|
||||
{
|
||||
rel: "contents",
|
||||
href: JSON.stringify(collectEcho(req)),
|
||||
},
|
||||
];
|
||||
break;
|
||||
}
|
||||
if (uri.indexOf('@') == -1) {
|
||||
res.status(404).end('Username is not provided')
|
||||
return
|
||||
default: {
|
||||
let result = lookupData(username);
|
||||
if (result == undefined) {
|
||||
res.status(404).end(`User "${username}" not found`);
|
||||
return;
|
||||
} else {
|
||||
aliases = result.aliases;
|
||||
links = result.links;
|
||||
}
|
||||
}
|
||||
let username = uri.substring(5, uri.indexOf('@')).toLowerCase()
|
||||
if (username.startsWith('//'))
|
||||
username = username.substring(2)
|
||||
let aliases: string[] = []
|
||||
let links: any[] = []
|
||||
switch (username) {
|
||||
case 'this': {
|
||||
links = site_lrs
|
||||
break
|
||||
}
|
||||
case 'echo': {
|
||||
links = [
|
||||
{
|
||||
rel: 'contents',
|
||||
href: JSON.stringify(collectEcho(req)),
|
||||
}
|
||||
]
|
||||
break
|
||||
}
|
||||
default: {
|
||||
let result = lookupData(username)
|
||||
if (result == undefined) {
|
||||
res.status(404).end(`User "${username}" not found`)
|
||||
return
|
||||
} else {
|
||||
aliases = result.aliases
|
||||
links = result.links
|
||||
}
|
||||
}
|
||||
}
|
||||
res.status(200).json({
|
||||
subject: uri,
|
||||
aliases: (aliases != undefined && aliases.length == 0) ? undefined : aliases,
|
||||
links,
|
||||
})
|
||||
}
|
||||
res.status(200).json({
|
||||
subject: uri,
|
||||
aliases: aliases != undefined && aliases.length == 0 ? undefined : aliases,
|
||||
links,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { IncomingHttpHeaders } from 'node:http'
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import type { IncomingHttpHeaders } from "node:http";
|
||||
|
||||
type Data = {
|
||||
httpVersion: string,
|
||||
headers: IncomingHttpHeaders,
|
||||
address: string,
|
||||
port: number,
|
||||
ipv6: boolean,
|
||||
method: string,
|
||||
userAgent: string | undefined,
|
||||
}
|
||||
httpVersion: string;
|
||||
headers: IncomingHttpHeaders;
|
||||
address: string;
|
||||
port: number;
|
||||
ipv6: boolean;
|
||||
method: string;
|
||||
userAgent: string | undefined;
|
||||
};
|
||||
|
||||
export function collect(req: NextApiRequest): Data {
|
||||
return {
|
||||
|
@ -17,15 +17,15 @@ export function collect(req: NextApiRequest): Data {
|
|||
headers: req.headers,
|
||||
address: req.socket.remoteAddress!,
|
||||
port: req.socket.remotePort!,
|
||||
ipv6: req.socket.remoteFamily == 'IPv6',
|
||||
ipv6: req.socket.remoteFamily == "IPv6",
|
||||
method: req.method!,
|
||||
userAgent: req.headers["user-agent"],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json(collect(req))
|
||||
res.status(200).json(collect(req));
|
||||
}
|
||||
|
|
Reference in New Issue