Merge branch 'develop' of https://github.com/syuilo/misskey into develop
This commit is contained in:
		
							
								
								
									
										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}` | ||||||
| 		}, { | 	}; | ||||||
|  | 	const subscribe = { | ||||||
| 		rel: 'http://ostatus.org/schema/1.0/subscribe', | 		rel: 'http://ostatus.org/schema/1.0/subscribe', | ||||||
| 		template: `${config.url}/authorize-follow?acct={uri}` | 		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'); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo