mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-26 20:07:33 -07:00
* Create xml.ts * Update well-known.ts * Update well-known.ts * Fix typo * Update well-known.ts * Update well-known.ts
This commit is contained in:
parent
e20bbd1add
commit
382def22d7
2 changed files with 89 additions and 22 deletions
41
src/prelude/xml.ts
Normal file
41
src/prelude/xml.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
'\'': '''
|
||||||
|
};
|
||||||
|
|
||||||
|
const beginingOfCDATA = '<![CDATA[';
|
||||||
|
const endOfCDATA = ']]>';
|
||||||
|
|
||||||
|
export function escapeValue(x: string): string {
|
||||||
|
let insideOfCDATA = false;
|
||||||
|
let builder = '';
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < x.length;
|
||||||
|
) {
|
||||||
|
if (insideOfCDATA) {
|
||||||
|
if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
|
||||||
|
insideOfCDATA = true;
|
||||||
|
i += beginingOfCDATA.length;
|
||||||
|
} else {
|
||||||
|
builder += x[i++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
|
||||||
|
insideOfCDATA = false;
|
||||||
|
i += endOfCDATA.length;
|
||||||
|
} else {
|
||||||
|
const b = x[i++];
|
||||||
|
builder += map[b] || b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function escapeAttribute(x: string): string {
|
||||||
|
return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
|
||||||
|
}
|
|
@ -6,27 +6,37 @@ import parseAcct from '../misc/acct/parse';
|
||||||
import User from '../models/user';
|
import User from '../models/user';
|
||||||
import Acct from '../misc/acct/type';
|
import Acct from '../misc/acct/type';
|
||||||
import { links } from './nodeinfo';
|
import { links } from './nodeinfo';
|
||||||
|
import { escapeAttribute, escapeValue } from '../prelude/xml';
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
|
const XRD = (...x: { element: string, value?: string, attributes?: Record<string, string> }[]) =>
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">${x.map(({ element, value, attributes }) =>
|
||||||
|
`<${
|
||||||
|
Object.entries(typeof attributes === 'object' && attributes || {}).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element)
|
||||||
|
}${
|
||||||
|
typeof value === 'string' ? `>${escapeValue(value)}</${element}` : '/'
|
||||||
|
}>`).reduce((a, c) => a + c, '')}</XRD>`;
|
||||||
|
|
||||||
const webFingerPath = '/.well-known/webfinger';
|
const webFingerPath = '/.well-known/webfinger';
|
||||||
|
const jrd = 'application/jrd+json';
|
||||||
|
const xrd = 'application/xrd+xml';
|
||||||
|
|
||||||
router.get('/.well-known/host-meta', async ctx => {
|
router.get('/.well-known/host-meta', async ctx => {
|
||||||
ctx.set('Content-Type', 'application/xrd+xml');
|
ctx.set('Content-Type', xrd);
|
||||||
ctx.body = `<?xml version="1.0" encoding="UTF-8"?>
|
ctx.body = XRD({ element: 'Link', attributes: {
|
||||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
type: xrd,
|
||||||
<Link rel="lrdd" type="application/xrd+xml" template="${config.url}${webFingerPath}?resource={uri}"/>
|
template: `${config.url}${webFingerPath}?resource={uri}`
|
||||||
</XRD>
|
}});
|
||||||
`;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/.well-known/host-meta.json', async ctx => {
|
router.get('/.well-known/host-meta.json', async ctx => {
|
||||||
ctx.set('Content-Type', 'application/jrd+json');
|
ctx.set('Content-Type', jrd);
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
links: [{
|
links: [{
|
||||||
rel: 'lrdd',
|
rel: 'lrdd',
|
||||||
type: 'application/xrd+xml',
|
type: jrd,
|
||||||
template: `${config.url}${webFingerPath}?resource={uri}`
|
template: `${config.url}${webFingerPath}?resource={uri}`
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
@ -75,22 +85,38 @@ router.get(webFingerPath, async ctx => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = {
|
const subject = `acct:${user.username}@${config.host}`;
|
||||||
subject: `acct:${user.username}@${config.host}`,
|
const self = {
|
||||||
links: [{
|
rel: 'self',
|
||||||
rel: 'self',
|
type: 'application/activity+json',
|
||||||
type: 'application/activity+json',
|
href: `${config.url}/users/${user._id}`
|
||||||
href: `${config.url}/users/${user._id}`
|
};
|
||||||
}, {
|
const profilePage = {
|
||||||
rel: 'http://webfinger.net/rel/profile-page',
|
rel: 'http://webfinger.net/rel/profile-page',
|
||||||
type: 'text/html',
|
type: 'text/html',
|
||||||
href: `${config.url}/@${user.username}`
|
href: `${config.url}/@${user.username}`
|
||||||
}, {
|
};
|
||||||
rel: 'http://ostatus.org/schema/1.0/subscribe',
|
const subscribe = {
|
||||||
template: `${config.url}/authorize-follow?acct={uri}`
|
rel: 'http://ostatus.org/schema/1.0/subscribe',
|
||||||
}]
|
template: `${config.url}/authorize-follow?acct={uri}`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ctx.accepts(jrd, xrd) === xrd) {
|
||||||
|
ctx.body = XRD(
|
||||||
|
{ element: 'Subject', value: subject },
|
||||||
|
{ element: 'Link', attributes: self },
|
||||||
|
{ element: 'Link', attributes: profilePage },
|
||||||
|
{ element: 'Link', attributes: subscribe });
|
||||||
|
ctx.type = xrd;
|
||||||
|
} else {
|
||||||
|
ctx.body = {
|
||||||
|
subject,
|
||||||
|
links: [self, profilePage, subscribe]
|
||||||
|
};
|
||||||
|
ctx.type = jrd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.vary('Accept');
|
||||||
ctx.set('Cache-Control', 'public, max-age=180');
|
ctx.set('Cache-Control', 'public, max-age=180');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue