mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 13:34:12 +00:00 
			
		
		
		
	improve mastodon note conversion and use access checks in more places (resolves #509)
This commit is contained in:
		
							parent
							
								
									3550ce27d5
								
							
						
					
					
						commit
						1e43162ba7
					
				
					 8 changed files with 222 additions and 236 deletions
				
			
		|  | @ -19,12 +19,13 @@ import { ApiSearchMastodonRoute } from '@/server/api/mastodon/endpoints/search.j | ||||||
| import { ApiFilterMastodonRoute } from '@/server/api/mastodon/endpoints/filter.js'; | import { ApiFilterMastodonRoute } from '@/server/api/mastodon/endpoints/filter.js'; | ||||||
| import { ApiNotifyMastodonRoute } from '@/server/api/mastodon/endpoints/notifications.js'; | import { ApiNotifyMastodonRoute } from '@/server/api/mastodon/endpoints/notifications.js'; | ||||||
| import { AuthenticateService } from '@/server/api/AuthenticateService.js'; | import { AuthenticateService } from '@/server/api/AuthenticateService.js'; | ||||||
|  | import { MiLocalUser } from '@/models/User.js'; | ||||||
| import { AuthMastodonRoute } from './endpoints/auth.js'; | import { AuthMastodonRoute } from './endpoints/auth.js'; | ||||||
| import { toBoolean } from './timelineArgs.js'; | import { toBoolean } from './timelineArgs.js'; | ||||||
| import { convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList, MastoConverters } from './converters.js'; | import { convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList, MastoConverters } from './converters.js'; | ||||||
| import { getInstance } from './endpoints/meta.js'; | import { getInstance } from './endpoints/meta.js'; | ||||||
| import { ApiAuthMastodon, ApiAccountMastodon, ApiFilterMastodon, ApiNotifyMastodon, ApiSearchMastodon, ApiTimelineMastodon, ApiStatusMastodon } from './endpoints.js'; | import { ApiAuthMastodon, ApiAccountMastodon, ApiFilterMastodon, ApiNotifyMastodon, ApiSearchMastodon, ApiTimelineMastodon, ApiStatusMastodon } from './endpoints.js'; | ||||||
| import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; | import type { FastifyInstance, FastifyPluginOptions, FastifyRequest } from 'fastify'; | ||||||
| 
 | 
 | ||||||
| export function getAccessToken(authorization: string | undefined): string | null { | export function getAccessToken(authorization: string | undefined): string | null { | ||||||
| 	const accessTokenArr = authorization?.split(' ') ?? [null]; | 	const accessTokenArr = authorization?.split(' ') ?? [null]; | ||||||
|  | @ -50,11 +51,29 @@ export class MastodonApiServerService { | ||||||
| 		@Inject(DI.config) | 		@Inject(DI.config) | ||||||
| 		private readonly config: Config, | 		private readonly config: Config, | ||||||
| 		private readonly driveService: DriveService, | 		private readonly driveService: DriveService, | ||||||
| 		private readonly mastoConverter: MastoConverters, | 		private readonly mastoConverters: MastoConverters, | ||||||
| 		private readonly logger: MastodonLogger, | 		private readonly logger: MastodonLogger, | ||||||
| 		private readonly authenticateService: AuthenticateService, | 		private readonly authenticateService: AuthenticateService, | ||||||
| 	) { } | 	) { } | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
|  | 	public async getAuthClient(request: FastifyRequest): Promise<{ client: MegalodonInterface, me: MiLocalUser | null }> { | ||||||
|  | 		const accessToken = getAccessToken(request.headers.authorization); | ||||||
|  | 		const [me] = await this.authenticateService.authenticate(accessToken); | ||||||
|  | 
 | ||||||
|  | 		const baseUrl = `${request.protocol}://${request.host}`; | ||||||
|  | 		const client = megalodon('misskey', baseUrl, accessToken); | ||||||
|  | 
 | ||||||
|  | 		return { client, me }; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@bindThis | ||||||
|  | 	public async getAuthOnly(request: FastifyRequest): Promise<MiLocalUser | null> { | ||||||
|  | 		const accessToken = getAccessToken(request.headers.authorization); | ||||||
|  | 		const [me] = await this.authenticateService.authenticate(accessToken); | ||||||
|  | 		return me; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) { | 	public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) { | ||||||
| 		const upload = multer({ | 		const upload = multer({ | ||||||
|  | @ -118,7 +137,7 @@ export class MastodonApiServerService { | ||||||
| 					}, | 					}, | ||||||
| 					order: { id: 'ASC' }, | 					order: { id: 'ASC' }, | ||||||
| 				}); | 				}); | ||||||
| 				const contact = admin == null ? null : await this.mastoConverter.convertAccount((await client.getAccount(admin.id)).data); | 				const contact = admin == null ? null : await this.mastoConverters.convertAccount((await client.getAccount(admin.id)).data); | ||||||
| 				reply.send(await getInstance(data.data, contact as Entity.Account, this.config, this.serverSettings)); | 				reply.send(await getInstance(data.data, contact as Entity.Account, this.config, this.serverSettings)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -275,12 +294,9 @@ export class MastodonApiServerService { | ||||||
| 
 | 
 | ||||||
| 		//#region Accounts
 | 		//#region Accounts
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute>('/v1/accounts/verify_credentials', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute>('/v1/accounts/verify_credentials', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
 |  | ||||||
| 			// displayed without being logged in
 |  | ||||||
| 			try { | 			try { | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.verifyCredentials()); | 				reply.send(await account.verifyCredentials()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -378,7 +394,7 @@ export class MastodonApiServerService { | ||||||
| 					} : undefined, | 					} : undefined, | ||||||
| 				}; | 				}; | ||||||
| 				const data = await client.updateCredentials(options); | 				const data = await client.updateCredentials(options); | ||||||
| 				reply.send(await this.mastoConverter.convertAccount(data.data)); | 				reply.send(await this.mastoConverters.convertAccount(data.data)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('PATCH /v1/accounts/update_credentials', data); | 				this.logger.error('PATCH /v1/accounts/update_credentials', data); | ||||||
|  | @ -395,7 +411,7 @@ export class MastodonApiServerService { | ||||||
| 				const data = await client.search(_request.query.acct, { type: 'accounts' }); | 				const data = await client.search(_request.query.acct, { type: 'accounts' }); | ||||||
| 				const profile = await this.userProfilesRepository.findOneBy({ userId: data.data.accounts[0].id }); | 				const profile = await this.userProfilesRepository.findOneBy({ userId: data.data.accounts[0].id }); | ||||||
| 				data.data.accounts[0].fields = profile?.fields.map(f => ({ ...f, verified_at: null })) ?? []; | 				data.data.accounts[0].fields = profile?.fields.map(f => ({ ...f, verified_at: null })) ?? []; | ||||||
| 				reply.send(await this.mastoConverter.convertAccount(data.data.accounts[0])); | 				reply.send(await this.mastoConverters.convertAccount(data.data.accounts[0])); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('GET /v1/accounts/lookup', data); | 				this.logger.error('GET /v1/accounts/lookup', data); | ||||||
|  | @ -404,15 +420,13 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute & { Querystring: { id?: string | string[], 'id[]'?: string | string[] }}>('/v1/accounts/relationships', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute & { Querystring: { id?: string | string[], 'id[]'?: string | string[] }}>('/v1/accounts/relationships', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isn't displayed without being logged in
 |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.getAuthClient(_request); | ||||||
| 				let ids = _request.query['id[]'] ?? _request.query['id'] ?? []; | 				let ids = _request.query['id[]'] ?? _request.query['id'] ?? []; | ||||||
| 				if (typeof ids === 'string') { | 				if (typeof ids === 'string') { | ||||||
| 					ids = [ids]; | 					ids = [ids]; | ||||||
| 				} | 				} | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getRelationships(ids)); | 				reply.send(await account.getRelationships(ids)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -428,7 +442,7 @@ export class MastodonApiServerService { | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const data = await client.getAccount(_request.params.id); | 				const data = await client.getAccount(_request.params.id); | ||||||
| 				const account = await this.mastoConverter.convertAccount(data.data); | 				const account = await this.mastoConverters.convertAccount(data.data); | ||||||
| 				reply.send(account); | 				reply.send(account); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -438,12 +452,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/statuses', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/statuses', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getStatuses()); | 				reply.send(await account.getStatuses()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -468,12 +480,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/followers', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/followers', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getFollowers()); | 				reply.send(await account.getFollowers()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -483,12 +493,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/following', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/following', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getFollowing()); | 				reply.send(await account.getFollowing()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -513,12 +521,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/follow', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/follow', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.addFollow()); | 				reply.send(await account.addFollow()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -528,12 +534,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unfollow', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unfollow', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.rmFollow()); | 				reply.send(await account.rmFollow()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -543,12 +547,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/block', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/block', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.addBlock()); | 				reply.send(await account.addBlock()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -558,12 +560,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unblock', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unblock', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.rmBlock()); | 				reply.send(await account.rmBlock()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -573,12 +573,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/mute', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/mute', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.addMute()); | 				reply.send(await account.addMute()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -588,12 +586,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unmute', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/accounts/:id/unmute', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.rmMute()); | 				reply.send(await account.rmMute()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -617,11 +613,9 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute>('/v1/bookmarks', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute>('/v1/bookmarks', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getBookmarks()); | 				reply.send(await account.getBookmarks()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -631,11 +625,9 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute>('/v1/favourites', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute>('/v1/favourites', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getFavourites()); | 				reply.send(await account.getFavourites()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -645,11 +637,9 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute>('/v1/mutes', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute>('/v1/mutes', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getMutes()); | 				reply.send(await account.getMutes()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -659,11 +649,9 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiAccountMastodonRoute>('/v1/blocks', async (_request, reply) => { | 		fastify.get<ApiAccountMastodonRoute>('/v1/blocks', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.getBlocks()); | 				reply.send(await account.getBlocks()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -679,7 +667,7 @@ export class MastodonApiServerService { | ||||||
| 			try { | 			try { | ||||||
| 				const limit = _request.query.limit ? parseInt(_request.query.limit) : 20; | 				const limit = _request.query.limit ? parseInt(_request.query.limit) : 20; | ||||||
| 				const data = await client.getFollowRequests(limit); | 				const data = await client.getFollowRequests(limit); | ||||||
| 				reply.send(await Promise.all(data.data.map(async (account) => await this.mastoConverter.convertAccount(account as Entity.Account)))); | 				reply.send(await Promise.all(data.data.map(async (account) => await this.mastoConverters.convertAccount(account as Entity.Account)))); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('GET /v1/follow_requests', data); | 				this.logger.error('GET /v1/follow_requests', data); | ||||||
|  | @ -688,12 +676,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/follow_requests/:id/authorize', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/follow_requests/:id/authorize', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.acceptFollow()); | 				reply.send(await account.acceptFollow()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -703,12 +689,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/follow_requests/:id/reject', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiAccountMastodonRoute & { Params: { id?: string } }>('/v1/follow_requests/:id/reject', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const account = new ApiAccountMastodon(_request, client, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const account = new ApiAccountMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await account.rejectFollow()); | 				reply.send(await account.rejectFollow()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -721,10 +705,9 @@ export class MastodonApiServerService { | ||||||
| 		//#region Search
 | 		//#region Search
 | ||||||
| 		fastify.get<ApiSearchMastodonRoute>('/v1/search', async (_request, reply) => { | 		fastify.get<ApiSearchMastodonRoute>('/v1/search', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; | 			const BASE_URL = `${_request.protocol}://${_request.host}`; | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const search = new ApiSearchMastodon(_request, client, me, BASE_URL, this.mastoConverters); | ||||||
| 				reply.send(await search.SearchV1()); | 				reply.send(await search.SearchV1()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -735,10 +718,9 @@ export class MastodonApiServerService { | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiSearchMastodonRoute>('/v2/search', async (_request, reply) => { | 		fastify.get<ApiSearchMastodonRoute>('/v2/search', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; | 			const BASE_URL = `${_request.protocol}://${_request.host}`; | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const search = new ApiSearchMastodon(_request, client, me, BASE_URL, this.mastoConverters); | ||||||
| 				reply.send(await search.SearchV2()); | 				reply.send(await search.SearchV2()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -749,10 +731,9 @@ export class MastodonApiServerService { | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiSearchMastodonRoute>('/v1/trends/statuses', async (_request, reply) => { | 		fastify.get<ApiSearchMastodonRoute>('/v1/trends/statuses', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; | 			const BASE_URL = `${_request.protocol}://${_request.host}`; | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const search = new ApiSearchMastodon(_request, client, me, BASE_URL, this.mastoConverters); | ||||||
| 				reply.send(await search.getStatusTrends()); | 				reply.send(await search.getStatusTrends()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -763,10 +744,9 @@ export class MastodonApiServerService { | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiSearchMastodonRoute>('/v2/suggestions', async (_request, reply) => { | 		fastify.get<ApiSearchMastodonRoute>('/v2/suggestions', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; | 			const BASE_URL = `${_request.protocol}://${_request.host}`; | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const search = new ApiSearchMastodon(_request, client, me, BASE_URL, this.mastoConverters); | ||||||
| 				reply.send(await search.getSuggestions()); | 				reply.send(await search.getSuggestions()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -778,11 +758,9 @@ export class MastodonApiServerService { | ||||||
| 
 | 
 | ||||||
| 		//#region Notifications
 | 		//#region Notifications
 | ||||||
| 		fastify.get<ApiNotifyMastodonRoute>('/v1/notifications', async (_request, reply) => { | 		fastify.get<ApiNotifyMastodonRoute>('/v1/notifications', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const notify = new ApiNotifyMastodon(_request, client); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const notify = new ApiNotifyMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await notify.getNotifications()); | 				reply.send(await notify.getNotifications()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -792,12 +770,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.get<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id', async (_request, reply) => { | 		fastify.get<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const notify = new ApiNotifyMastodon(_request, client); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const notify = new ApiNotifyMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await notify.getNotification()); | 				reply.send(await notify.getNotification()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -807,12 +783,10 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const notify = new ApiNotifyMastodon(_request, client); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const notify = new ApiNotifyMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await notify.rmNotification()); | 				reply.send(await notify.rmNotification()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -822,11 +796,9 @@ export class MastodonApiServerService { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		fastify.post<ApiNotifyMastodonRoute>('/v1/notifications/clear', { preHandler: upload.single('none') }, async (_request, reply) => { | 		fastify.post<ApiNotifyMastodonRoute>('/v1/notifications/clear', { preHandler: upload.single('none') }, async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				const notify = new ApiNotifyMastodon(_request, client); | 				const { client, me } = await this.getAuthClient(_request); | ||||||
|  | 				const notify = new ApiNotifyMastodon(_request, client, me, this.mastoConverters); | ||||||
| 				reply.send(await notify.rmNotifications()); | 				reply.send(await notify.rmNotifications()); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -899,7 +871,7 @@ export class MastodonApiServerService { | ||||||
| 		//#endregion
 | 		//#endregion
 | ||||||
| 
 | 
 | ||||||
| 		//#region Timelines
 | 		//#region Timelines
 | ||||||
| 		const TLEndpoint = new ApiTimelineMastodon(fastify, this.mastoConverter, this.logger); | 		const TLEndpoint = new ApiTimelineMastodon(fastify, this.mastoConverters, this.logger, this); | ||||||
| 
 | 
 | ||||||
| 		// GET Endpoints
 | 		// GET Endpoints
 | ||||||
| 		TLEndpoint.getTL(); | 		TLEndpoint.getTL(); | ||||||
|  | @ -924,7 +896,7 @@ export class MastodonApiServerService { | ||||||
| 		//#endregion
 | 		//#endregion
 | ||||||
| 
 | 
 | ||||||
| 		//#region Status
 | 		//#region Status
 | ||||||
| 		const NoteEndpoint = new ApiStatusMastodon(fastify, this.mastoConverter, this.logger, this.authenticateService); | 		const NoteEndpoint = new ApiStatusMastodon(fastify, this.mastoConverters, this.logger, this.authenticateService, this); | ||||||
| 
 | 
 | ||||||
| 		// GET Endpoints
 | 		// GET Endpoints
 | ||||||
| 		NoteEndpoint.getStatus(); | 		NoteEndpoint.getStatus(); | ||||||
|  |  | ||||||
|  | @ -4,10 +4,12 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
|  | import { IsNull } from 'typeorm'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { QueryService } from '@/core/QueryService.js'; | import { QueryService } from '@/core/QueryService.js'; | ||||||
| import type { MiNote, NotesRepository } from '@/models/_.js'; | import type { MiNote, NotesRepository } from '@/models/_.js'; | ||||||
| import type { MiLocalUser } from '@/models/User.js'; | import type { MiLocalUser } from '@/models/User.js'; | ||||||
|  | import { ApiError } from '../error.js'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Utility service for accessing data with Mastodon semantics |  * Utility service for accessing data with Mastodon semantics | ||||||
|  | @ -22,6 +24,28 @@ export class MastodonDataService { | ||||||
| 		private readonly queryService: QueryService, | 		private readonly queryService: QueryService, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Fetches a note in the context of the current user, and throws an exception if not found. | ||||||
|  | 	 */ | ||||||
|  | 	public async requireNote(noteId: string, me?: MiLocalUser | null): Promise<MiNote> { | ||||||
|  | 		const note = await this.getNote(noteId, me); | ||||||
|  | 
 | ||||||
|  | 		if (!note) { | ||||||
|  | 			throw new ApiError({ | ||||||
|  | 				message: 'No such note.', | ||||||
|  | 				code: 'NO_SUCH_NOTE', | ||||||
|  | 				id: '24fcbfc6-2e37-42b6-8388-c29b3861a08d', | ||||||
|  | 				kind: 'client', | ||||||
|  | 				httpStatusCode: 404, | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return note; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Fetches a note in the context of the current user. | ||||||
|  | 	 */ | ||||||
| 	public async getNote(noteId: string, me?: MiLocalUser | null): Promise<MiNote | null> { | 	public async getNote(noteId: string, me?: MiLocalUser | null): Promise<MiNote | null> { | ||||||
| 		// Root query: note + required dependencies
 | 		// Root query: note + required dependencies
 | ||||||
| 		const query = this.notesRepository | 		const query = this.notesRepository | ||||||
|  | @ -37,4 +61,24 @@ export class MastodonDataService { | ||||||
| 
 | 
 | ||||||
| 		return await query.getOne(); | 		return await query.getOne(); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Checks where the current user has made a reblog / boost / pure renote of a given target note. | ||||||
|  | 	 */ | ||||||
|  | 	public async hasReblog(noteId: string, me: MiLocalUser | null | undefined): Promise<boolean> { | ||||||
|  | 		if (!me) return false; | ||||||
|  | 
 | ||||||
|  | 		return await this.notesRepository.existsBy({ | ||||||
|  | 			// Reblog of the target note by me
 | ||||||
|  | 			userId: me.id, | ||||||
|  | 			renoteId: noteId, | ||||||
|  | 
 | ||||||
|  | 			// That is pure (not a quote)
 | ||||||
|  | 			text: IsNull(), | ||||||
|  | 			cw: IsNull(), | ||||||
|  | 			replyId: IsNull(), | ||||||
|  | 			hasPoll: false, | ||||||
|  | 			fileIds: '{}', | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -215,15 +215,16 @@ export class MastoConverters { | ||||||
| 		return await Promise.all(history); | 		return await Promise.all(history); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private async convertReblog(status: Entity.Status | null): Promise<MastodonEntity.Status | null> { | 	private async convertReblog(status: Entity.Status | null, me?: MiLocalUser | null): Promise<MastodonEntity.Status | null> { | ||||||
| 		if (!status) return null; | 		if (!status) return null; | ||||||
| 		return await this.convertStatus(status); | 		return await this.convertStatus(status, me); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public async convertStatus(status: Entity.Status): Promise<MastodonEntity.Status> { | 	public async convertStatus(status: Entity.Status, me?: MiLocalUser | null): Promise<MastodonEntity.Status> { | ||||||
| 		const convertedAccount = this.convertAccount(status.account); | 		const convertedAccount = this.convertAccount(status.account); | ||||||
| 		const note = await this.getterService.getNote(status.id); | 		const note = await this.mastodonDataService.requireNote(status.id, me); | ||||||
| 		const noteUser = await this.getUser(status.account.id); | 		const noteUser = await this.getUser(status.account.id); | ||||||
|  | 		const mentionedRemoteUsers = JSON.parse(note.mentionedRemoteUsers); | ||||||
| 
 | 
 | ||||||
| 		const emojis = await this.customEmojiService.populateEmojis(note.emojis, noteUser.host ? noteUser.host : this.config.host); | 		const emojis = await this.customEmojiService.populateEmojis(note.emojis, noteUser.host ? noteUser.host : this.config.host); | ||||||
| 		const emoji: Entity.Emoji[] = []; | 		const emoji: Entity.Emoji[] = []; | ||||||
|  | @ -240,7 +241,7 @@ export class MastoConverters { | ||||||
| 
 | 
 | ||||||
| 		const mentions = Promise.all(note.mentions.map(p => | 		const mentions = Promise.all(note.mentions.map(p => | ||||||
| 			this.getUser(p) | 			this.getUser(p) | ||||||
| 				.then(u => this.encode(u, JSON.parse(note.mentionedRemoteUsers))) | 				.then(u => this.encode(u, mentionedRemoteUsers)) | ||||||
| 				.catch(() => null))) | 				.catch(() => null))) | ||||||
| 			.then(p => p.filter(m => m)) as Promise<Entity.Mention[]>; | 			.then(p => p.filter(m => m)) as Promise<Entity.Mention[]>; | ||||||
| 
 | 
 | ||||||
|  | @ -255,7 +256,7 @@ export class MastoConverters { | ||||||
| 		// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
 | 		// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
 | ||||||
| 		const isQuote = note.renoteId && (note.text || note.cw || note.fileIds.length > 0 || note.hasPoll || note.replyId); | 		const isQuote = note.renoteId && (note.text || note.cw || note.fileIds.length > 0 || note.hasPoll || note.replyId); | ||||||
| 
 | 
 | ||||||
| 		const renote: Promise<MiNote> | null = note.renoteId ? this.getterService.getNote(note.renoteId) : null; | 		const renote: Promise<MiNote> | null = note.renoteId ? this.mastodonDataService.requireNote(note.renoteId, me) : null; | ||||||
| 
 | 
 | ||||||
| 		const quoteUri = Promise.resolve(renote).then(renote => { | 		const quoteUri = Promise.resolve(renote).then(renote => { | ||||||
| 			if (!renote || !isQuote) return null; | 			if (!renote || !isQuote) return null; | ||||||
|  | @ -265,10 +266,12 @@ export class MastoConverters { | ||||||
| 		const text = note.text; | 		const text = note.text; | ||||||
| 		const content = text !== null | 		const content = text !== null | ||||||
| 			? quoteUri | 			? quoteUri | ||||||
| 				.then(quoteUri => this.mfmService.toMastoApiHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers), false, quoteUri)) | 				.then(quoteUri => this.mfmService.toMastoApiHtml(mfm.parse(text), mentionedRemoteUsers, false, quoteUri)) | ||||||
| 				.then(p => p ?? escapeMFM(text)) | 				.then(p => p ?? escapeMFM(text)) | ||||||
| 			: ''; | 			: ''; | ||||||
| 
 | 
 | ||||||
|  | 		const reblogged = await this.mastodonDataService.hasReblog(note.id, me); | ||||||
|  | 
 | ||||||
| 		// noinspection ES6MissingAwait
 | 		// noinspection ES6MissingAwait
 | ||||||
| 		return await awaitAll({ | 		return await awaitAll({ | ||||||
| 			id: note.id, | 			id: note.id, | ||||||
|  | @ -277,7 +280,7 @@ export class MastoConverters { | ||||||
| 			account: convertedAccount, | 			account: convertedAccount, | ||||||
| 			in_reply_to_id: note.replyId, | 			in_reply_to_id: note.replyId, | ||||||
| 			in_reply_to_account_id: note.replyUserId, | 			in_reply_to_account_id: note.replyUserId, | ||||||
| 			reblog: !isQuote ? await this.convertReblog(status.reblog) : null, | 			reblog: !isQuote ? await this.convertReblog(status.reblog, me) : null, | ||||||
| 			content: content, | 			content: content, | ||||||
| 			content_type: 'text/x.misskeymarkdown', | 			content_type: 'text/x.misskeymarkdown', | ||||||
| 			text: note.text, | 			text: note.text, | ||||||
|  | @ -286,7 +289,7 @@ export class MastoConverters { | ||||||
| 			replies_count: note.repliesCount, | 			replies_count: note.repliesCount, | ||||||
| 			reblogs_count: note.renoteCount, | 			reblogs_count: note.renoteCount, | ||||||
| 			favourites_count: status.favourites_count, | 			favourites_count: status.favourites_count, | ||||||
| 			reblogged: false, | 			reblogged, | ||||||
| 			favourited: status.favourited, | 			favourited: status.favourited, | ||||||
| 			muted: status.muted, | 			muted: status.muted, | ||||||
| 			sensitive: status.sensitive, | 			sensitive: status.sensitive, | ||||||
|  | @ -303,10 +306,29 @@ export class MastoConverters { | ||||||
| 			reactions: status.emoji_reactions, | 			reactions: status.emoji_reactions, | ||||||
| 			emoji_reactions: status.emoji_reactions, | 			emoji_reactions: status.emoji_reactions, | ||||||
| 			bookmarked: false, //FIXME
 | 			bookmarked: false, //FIXME
 | ||||||
| 			quote: isQuote ? await this.convertReblog(status.reblog) : null, | 			quote: isQuote ? await this.convertReblog(status.reblog, me) : null, | ||||||
| 			edited_at: note.updatedAt?.toISOString() ?? null, | 			edited_at: note.updatedAt?.toISOString() ?? null, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	public async convertConversation(conversation: Entity.Conversation, me?: MiLocalUser | null): Promise<MastodonEntity.Conversation> { | ||||||
|  | 		return { | ||||||
|  | 			id: conversation.id, | ||||||
|  | 			accounts: await Promise.all(conversation.accounts.map(a => this.convertAccount(a))), | ||||||
|  | 			last_status: conversation.last_status ? await this.convertStatus(conversation.last_status, me) : null, | ||||||
|  | 			unread: conversation.unread, | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public async convertNotification(notification: Entity.Notification, me?: MiLocalUser | null): Promise<MastodonEntity.Notification> { | ||||||
|  | 		return { | ||||||
|  | 			account: await this.convertAccount(notification.account), | ||||||
|  | 			created_at: notification.created_at, | ||||||
|  | 			id: notification.id, | ||||||
|  | 			status: notification.status ? await this.convertStatus(notification.status, me) : undefined, | ||||||
|  | 			type: notification.type, | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function simpleConvert<T>(data: T): T { | function simpleConvert<T>(data: T): T { | ||||||
|  | @ -333,12 +355,6 @@ export function convertFeaturedTag(tag: Entity.FeaturedTag) { | ||||||
| 	return simpleConvert(tag); | 	return simpleConvert(tag); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function convertNotification(notification: Entity.Notification) { |  | ||||||
| 	notification.account = convertAccount(notification.account); |  | ||||||
| 	if (notification.status) notification.status = convertStatus(notification.status); |  | ||||||
| 	return notification; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export function convertPoll(poll: Entity.Poll) { | export function convertPoll(poll: Entity.Poll) { | ||||||
| 	return simpleConvert(poll); | 	return simpleConvert(poll); | ||||||
| } | } | ||||||
|  | @ -372,27 +388,7 @@ export function convertRelationship(relationship: Partial<Entity.Relationship> & | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function convertStatus(status: Entity.Status) { |  | ||||||
| 	status.account = convertAccount(status.account); |  | ||||||
| 	status.media_attachments = status.media_attachments.map((attachment) => |  | ||||||
| 		convertAttachment(attachment), |  | ||||||
| 	); |  | ||||||
| 	if (status.poll) status.poll = convertPoll(status.poll); |  | ||||||
| 	if (status.reblog) status.reblog = convertStatus(status.reblog); |  | ||||||
| 
 |  | ||||||
| 	return status; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // noinspection JSUnusedGlobalSymbols
 | // noinspection JSUnusedGlobalSymbols
 | ||||||
| export function convertStatusSource(status: Entity.StatusSource) { | export function convertStatusSource(status: Entity.StatusSource) { | ||||||
| 	return simpleConvert(status); | 	return simpleConvert(status); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export function convertConversation(conversation: Entity.Conversation) { |  | ||||||
| 	conversation.accounts = conversation.accounts.map(convertAccount); |  | ||||||
| 	if (conversation.last_status) { |  | ||||||
| 		conversation.last_status = convertStatus(conversation.last_status); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return conversation; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||||
| import { parseTimelineArgs, TimelineArgs } from '@/server/api/mastodon/timelineArgs.js'; | import { parseTimelineArgs, TimelineArgs } from '@/server/api/mastodon/timelineArgs.js'; | ||||||
|  | import { MiLocalUser } from '@/models/User.js'; | ||||||
| import { MastoConverters, convertRelationship } from '../converters.js'; | import { MastoConverters, convertRelationship } from '../converters.js'; | ||||||
| import type { MegalodonInterface } from 'megalodon'; | import type { MegalodonInterface } from 'megalodon'; | ||||||
| import type { FastifyRequest } from 'fastify'; | import type { FastifyRequest } from 'fastify'; | ||||||
|  | @ -20,6 +21,7 @@ export class ApiAccountMastodon { | ||||||
| 	constructor( | 	constructor( | ||||||
| 		private readonly request: FastifyRequest<ApiAccountMastodonRoute>, | 		private readonly request: FastifyRequest<ApiAccountMastodonRoute>, | ||||||
| 		private readonly client: MegalodonInterface, | 		private readonly client: MegalodonInterface, | ||||||
|  | 		private readonly me: MiLocalUser | null, | ||||||
| 		private readonly mastoConverters: MastoConverters, | 		private readonly mastoConverters: MastoConverters, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
|  | @ -51,7 +53,7 @@ export class ApiAccountMastodon { | ||||||
| 	public async getStatuses() { | 	public async getStatuses() { | ||||||
| 		if (!this.request.params.id) throw new Error('Missing required parameter "id"'); | 		if (!this.request.params.id) throw new Error('Missing required parameter "id"'); | ||||||
| 		const data = await this.client.getAccountStatuses(this.request.params.id, parseTimelineArgs(this.request.query)); | 		const data = await this.client.getAccountStatuses(this.request.params.id, parseTimelineArgs(this.request.query)); | ||||||
| 		return await Promise.all(data.data.map(async (status) => await this.mastoConverters.convertStatus(status))); | 		return await Promise.all(data.data.map(async (status) => await this.mastoConverters.convertStatus(status, this.me))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public async getFollowers() { | 	public async getFollowers() { | ||||||
|  | @ -117,12 +119,12 @@ export class ApiAccountMastodon { | ||||||
| 
 | 
 | ||||||
| 	public async getBookmarks() { | 	public async getBookmarks() { | ||||||
| 		const data = await this.client.getBookmarks(parseTimelineArgs(this.request.query)); | 		const data = await this.client.getBookmarks(parseTimelineArgs(this.request.query)); | ||||||
| 		return Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status))); | 		return Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status, this.me))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public async getFavourites() { | 	public async getFavourites() { | ||||||
| 		const data = await this.client.getFavourites(parseTimelineArgs(this.request.query)); | 		const data = await this.client.getFavourites(parseTimelineArgs(this.request.query)); | ||||||
| 		return Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status))); | 		return Promise.all(data.data.map((status) => this.mastoConverters.convertStatus(status, this.me))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public async getMutes() { | 	public async getMutes() { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,8 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { parseTimelineArgs, TimelineArgs } from '@/server/api/mastodon/timelineArgs.js'; | import { parseTimelineArgs, TimelineArgs } from '@/server/api/mastodon/timelineArgs.js'; | ||||||
| import { convertNotification } from '../converters.js'; | import { MiLocalUser } from '@/models/User.js'; | ||||||
|  | import { MastoConverters } from '@/server/api/mastodon/converters.js'; | ||||||
| import type { MegalodonInterface } from 'megalodon'; | import type { MegalodonInterface } from 'megalodon'; | ||||||
| import type { FastifyRequest } from 'fastify'; | import type { FastifyRequest } from 'fastify'; | ||||||
| 
 | 
 | ||||||
|  | @ -19,23 +20,25 @@ export class ApiNotifyMastodon { | ||||||
| 	constructor( | 	constructor( | ||||||
| 		private readonly request: FastifyRequest<ApiNotifyMastodonRoute>, | 		private readonly request: FastifyRequest<ApiNotifyMastodonRoute>, | ||||||
| 		private readonly client: MegalodonInterface, | 		private readonly client: MegalodonInterface, | ||||||
|  | 		private readonly me: MiLocalUser | null, | ||||||
|  | 		private readonly mastoConverters: MastoConverters, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
| 	public async getNotifications() { | 	public async getNotifications() { | ||||||
| 		const data = await this.client.getNotifications(parseTimelineArgs(this.request.query)); | 		const data = await this.client.getNotifications(parseTimelineArgs(this.request.query)); | ||||||
| 		return data.data.map(n => { | 		return Promise.all(data.data.map(async n => { | ||||||
| 			const converted = convertNotification(n); | 			const converted = await this.mastoConverters.convertNotification(n, this.me); | ||||||
| 			if (converted.type === 'reaction') { | 			if (converted.type === 'reaction') { | ||||||
| 				converted.type = 'favourite'; | 				converted.type = 'favourite'; | ||||||
| 			} | 			} | ||||||
| 			return converted; | 			return converted; | ||||||
| 		}); | 		})); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public async getNotification() { | 	public async getNotification() { | ||||||
| 		if (!this.request.params.id) throw new Error('Missing required parameter "id"'); | 		if (!this.request.params.id) throw new Error('Missing required parameter "id"'); | ||||||
| 		const data = await this.client.getNotification(this.request.params.id); | 		const data = await this.client.getNotification(this.request.params.id); | ||||||
| 		const converted = convertNotification(data.data); | 		const converted = await this.mastoConverters.convertNotification(data.data, this.me); | ||||||
| 		if (converted.type === 'reaction') { | 		if (converted.type === 'reaction') { | ||||||
| 			converted.type = 'favourite'; | 			converted.type = 'favourite'; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
|  * SPDX-License-Identifier: AGPL-3.0-only |  * SPDX-License-Identifier: AGPL-3.0-only | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | import { MiLocalUser } from '@/models/User.js'; | ||||||
| import { MastoConverters } from '../converters.js'; | import { MastoConverters } from '../converters.js'; | ||||||
| import { parseTimelineArgs, TimelineArgs } from '../timelineArgs.js'; | import { parseTimelineArgs, TimelineArgs } from '../timelineArgs.js'; | ||||||
| import Account = Entity.Account; | import Account = Entity.Account; | ||||||
|  | @ -21,8 +22,9 @@ export class ApiSearchMastodon { | ||||||
| 	constructor( | 	constructor( | ||||||
| 		private readonly request: FastifyRequest<ApiSearchMastodonRoute>, | 		private readonly request: FastifyRequest<ApiSearchMastodonRoute>, | ||||||
| 		private readonly client: MegalodonInterface, | 		private readonly client: MegalodonInterface, | ||||||
|  | 		private readonly me: MiLocalUser | null, | ||||||
| 		private readonly BASE_URL: string, | 		private readonly BASE_URL: string, | ||||||
| 		private readonly mastoConverter: MastoConverters, | 		private readonly mastoConverters: MastoConverters, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
| 	public async SearchV1() { | 	public async SearchV1() { | ||||||
|  | @ -40,8 +42,8 @@ export class ApiSearchMastodon { | ||||||
| 		const stat = !type || type === 'statuses' ? await this.client.search(this.request.query.q, { type: 'statuses', ...query }) : null; | 		const stat = !type || type === 'statuses' ? await this.client.search(this.request.query.q, { type: 'statuses', ...query }) : null; | ||||||
| 		const tags = !type || type === 'hashtags' ? await this.client.search(this.request.query.q, { type: 'hashtags', ...query }) : null; | 		const tags = !type || type === 'hashtags' ? await this.client.search(this.request.query.q, { type: 'hashtags', ...query }) : null; | ||||||
| 		return { | 		return { | ||||||
| 			accounts: await Promise.all(acct?.data.accounts.map(async (account: Account) => await this.mastoConverter.convertAccount(account)) ?? []), | 			accounts: await Promise.all(acct?.data.accounts.map(async (account: Account) => await this.mastoConverters.convertAccount(account)) ?? []), | ||||||
| 			statuses: await Promise.all(stat?.data.statuses.map(async (status: Status) => await this.mastoConverter.convertStatus(status)) ?? []), | 			statuses: await Promise.all(stat?.data.statuses.map(async (status: Status) => await this.mastoConverters.convertStatus(status, this.me)) ?? []), | ||||||
| 			hashtags: tags?.data.hashtags ?? [], | 			hashtags: tags?.data.hashtags ?? [], | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
|  | @ -54,10 +56,12 @@ export class ApiSearchMastodon { | ||||||
| 					'Accept': 'application/json', | 					'Accept': 'application/json', | ||||||
| 					'Content-Type': 'application/json', | 					'Content-Type': 'application/json', | ||||||
| 				}, | 				}, | ||||||
| 				body: '{}', | 				body: JSON.stringify({ | ||||||
|  | 					i: this.request.headers.authorization?.replace('Bearer ', ''), | ||||||
|  | 				}), | ||||||
| 			}) | 			}) | ||||||
| 			.then(res => res.json() as Promise<Status[]>) | 			.then(res => res.json() as Promise<Status[]>) | ||||||
| 			.then(data => data.map(status => this.mastoConverter.convertStatus(status))); | 			.then(data => data.map(status => this.mastoConverters.convertStatus(status, this.me))); | ||||||
| 		return Promise.all(data); | 		return Promise.all(data); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -83,7 +87,7 @@ export class ApiSearchMastodon { | ||||||
| 				account: entry, | 				account: entry, | ||||||
| 			})))); | 			})))); | ||||||
| 		return Promise.all(data.map(async suggestion => { | 		return Promise.all(data.map(async suggestion => { | ||||||
| 			suggestion.account = await this.mastoConverter.convertAccount(suggestion.account); | 			suggestion.account = await this.mastoConverters.convertAccount(suggestion.account); | ||||||
| 			return suggestion; | 			return suggestion; | ||||||
| 		})); | 		})); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ import { getErrorData, MastodonLogger } from '@/server/api/mastodon/MastodonLogg | ||||||
| import { parseTimelineArgs, TimelineArgs, toBoolean, toInt } from '@/server/api/mastodon/timelineArgs.js'; | import { parseTimelineArgs, TimelineArgs, toBoolean, toInt } from '@/server/api/mastodon/timelineArgs.js'; | ||||||
| import { AuthenticateService } from '@/server/api/AuthenticateService.js'; | import { AuthenticateService } from '@/server/api/AuthenticateService.js'; | ||||||
| import { convertAttachment, convertPoll, MastoConverters } from '../converters.js'; | import { convertAttachment, convertPoll, MastoConverters } from '../converters.js'; | ||||||
| import { getAccessToken, getClient } from '../MastodonApiServerService.js'; | import { getAccessToken, getClient, MastodonApiServerService } from '../MastodonApiServerService.js'; | ||||||
| import type { Entity } from 'megalodon'; | import type { Entity } from 'megalodon'; | ||||||
| import type { FastifyInstance } from 'fastify'; | import type { FastifyInstance } from 'fastify'; | ||||||
| 
 | 
 | ||||||
|  | @ -24,17 +24,16 @@ export class ApiStatusMastodon { | ||||||
| 		private readonly mastoConverters: MastoConverters, | 		private readonly mastoConverters: MastoConverters, | ||||||
| 		private readonly logger: MastodonLogger, | 		private readonly logger: MastodonLogger, | ||||||
| 		private readonly authenticateService: AuthenticateService, | 		private readonly authenticateService: AuthenticateService, | ||||||
|  | 		private readonly mastodon: MastodonApiServerService, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
| 	public getStatus() { | 	public getStatus() { | ||||||
| 		this.fastify.get<{ Params: { id?: string } }>('/v1/statuses/:id', async (_request, reply) => { | 		this.fastify.get<{ Params: { id?: string } }>('/v1/statuses/:id', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const data = await client.getStatus(_request.params.id); | 				const data = await client.getStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`GET /v1/statuses/${_request.params.id}`, data); | 				this.logger.error(`GET /v1/statuses/${_request.params.id}`, data); | ||||||
|  | @ -62,14 +61,12 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public getContext() { | 	public getContext() { | ||||||
| 		this.fastify.get<{ Params: { id?: string }, Querystring: TimelineArgs }>('/v1/statuses/:id/context', async (_request, reply) => { | 		this.fastify.get<{ Params: { id?: string }, Querystring: TimelineArgs }>('/v1/statuses/:id/context', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const { data } = await client.getStatusContext(_request.params.id, parseTimelineArgs(_request.query)); | 				const { data } = await client.getStatusContext(_request.params.id, parseTimelineArgs(_request.query)); | ||||||
| 				const ancestors = await Promise.all(data.ancestors.map(async status => await this.mastoConverters.convertStatus(status))); | 				const ancestors = await Promise.all(data.ancestors.map(async status => await this.mastoConverters.convertStatus(status, me))); | ||||||
| 				const descendants = await Promise.all(data.descendants.map(async status => await this.mastoConverters.convertStatus(status))); | 				const descendants = await Promise.all(data.descendants.map(async status => await this.mastoConverters.convertStatus(status, me))); | ||||||
| 				reply.send({ ancestors, descendants }); | 				reply.send({ ancestors, descendants }); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
|  | @ -204,11 +201,9 @@ export class ApiStatusMastodon { | ||||||
| 				'media_ids[]'?: string[], | 				'media_ids[]'?: string[], | ||||||
| 			} | 			} | ||||||
| 		}>('/v1/statuses', async (_request, reply) => { | 		}>('/v1/statuses', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			let body = _request.body; | 			let body = _request.body; | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				if ((!body.poll && body['poll[options][]']) || (!body.media_ids && body['media_ids[]']) | 				if ((!body.poll && body['poll[options][]']) || (!body.media_ids && body['media_ids[]']) | ||||||
| 				) { | 				) { | ||||||
| 					body = normalizeQuery(body); | 					body = normalizeQuery(body); | ||||||
|  | @ -253,7 +248,7 @@ export class ApiStatusMastodon { | ||||||
| 				}; | 				}; | ||||||
| 
 | 
 | ||||||
| 				const data = await client.postStatus(text, options); | 				const data = await client.postStatus(text, options); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data as Entity.Status)); | 				reply.send(await this.mastoConverters.convertStatus(data.data as Entity.Status, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('POST /v1/statuses', data); | 				this.logger.error('POST /v1/statuses', data); | ||||||
|  | @ -278,10 +273,8 @@ export class ApiStatusMastodon { | ||||||
| 				}, | 				}, | ||||||
| 			} | 			} | ||||||
| 		}>('/v1/statuses/:id', async (_request, reply) => { | 		}>('/v1/statuses/:id', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const body = _request.body; | 				const body = _request.body; | ||||||
| 
 | 
 | ||||||
| 				if (!body.media_ids || !body.media_ids.length) { | 				if (!body.media_ids || !body.media_ids.length) { | ||||||
|  | @ -300,7 +293,7 @@ export class ApiStatusMastodon { | ||||||
| 				}; | 				}; | ||||||
| 
 | 
 | ||||||
| 				const data = await client.editStatus(_request.params.id, options); | 				const data = await client.editStatus(_request.params.id, options); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}`, data); | ||||||
|  | @ -311,13 +304,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public addFavourite() { | 	public addFavourite() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/favourite', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/favourite', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.createEmojiReaction(_request.params.id, '❤'); | 				const data = await client.createEmojiReaction(_request.params.id, '❤'); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/favorite`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/favorite`, data); | ||||||
|  | @ -328,13 +319,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public rmFavourite() { | 	public rmFavourite() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unfavourite', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unfavourite', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				const data = await client.deleteEmojiReaction(_request.params.id, '❤'); | 				const data = await client.deleteEmojiReaction(_request.params.id, '❤'); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`GET /v1/statuses/${_request.params.id}/unfavorite`, data); | 				this.logger.error(`GET /v1/statuses/${_request.params.id}/unfavorite`, data); | ||||||
|  | @ -345,13 +334,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public reblogStatus() { | 	public reblogStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/reblog', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/reblog', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.reblogStatus(_request.params.id); | 				const data = await client.reblogStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/reblog`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/reblog`, data); | ||||||
|  | @ -362,13 +349,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public unreblogStatus() { | 	public unreblogStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unreblog', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unreblog', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.unreblogStatus(_request.params.id); | 				const data = await client.unreblogStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unreblog`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unreblog`, data); | ||||||
|  | @ -379,13 +364,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public bookmarkStatus() { | 	public bookmarkStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/bookmark', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/bookmark', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.bookmarkStatus(_request.params.id); | 				const data = await client.bookmarkStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/bookmark`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/bookmark`, data); | ||||||
|  | @ -396,13 +379,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public unbookmarkStatus() { | 	public unbookmarkStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unbookmark', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unbookmark', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.unbookmarkStatus(_request.params.id); | 				const data = await client.unbookmarkStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unbookmark`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unbookmark`, data); | ||||||
|  | @ -413,13 +394,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public pinStatus() { | 	public pinStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/pin', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/pin', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.pinStatus(_request.params.id); | 				const data = await client.pinStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/pin`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/pin`, data); | ||||||
|  | @ -430,13 +409,11 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public unpinStatus() { | 	public unpinStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unpin', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string } }>('/v1/statuses/:id/unpin', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.unpinStatus(_request.params.id); | 				const data = await client.unpinStatus(_request.params.id); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unpin`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unpin`, data); | ||||||
|  | @ -447,14 +424,12 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public reactStatus() { | 	public reactStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string, name?: string } }>('/v1/statuses/:id/react/:name', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string, name?: string } }>('/v1/statuses/:id/react/:name', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				if (!_request.params.name) return reply.code(400).send({ error: 'Missing required parameter "name"' }); | 				if (!_request.params.name) return reply.code(400).send({ error: 'Missing required parameter "name"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.createEmojiReaction(_request.params.id, _request.params.name); | 				const data = await client.createEmojiReaction(_request.params.id, _request.params.name); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/react/${_request.params.name}`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/react/${_request.params.name}`, data); | ||||||
|  | @ -465,14 +440,12 @@ export class ApiStatusMastodon { | ||||||
| 
 | 
 | ||||||
| 	public unreactStatus() { | 	public unreactStatus() { | ||||||
| 		this.fastify.post<{ Params: { id?: string, name?: string } }>('/v1/statuses/:id/unreact/:name', async (_request, reply) => { | 		this.fastify.post<{ Params: { id?: string, name?: string } }>('/v1/statuses/:id/unreact/:name', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
| 				if (!_request.params.name) return reply.code(400).send({ error: 'Missing required parameter "name"' }); | 				if (!_request.params.name) return reply.code(400).send({ error: 'Missing required parameter "name"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.deleteEmojiReaction(_request.params.id, _request.params.name); | 				const data = await client.deleteEmojiReaction(_request.params.id, _request.params.name); | ||||||
| 				reply.send(await this.mastoConverters.convertStatus(data.data)); | 				reply.send(await this.mastoConverters.convertStatus(data.data, me)); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unreact/${_request.params.name}`, data); | 				this.logger.error(`POST /v1/statuses/${_request.params.id}/unreact/${_request.params.name}`, data); | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import { getErrorData, MastodonLogger } from '@/server/api/mastodon/MastodonLogger.js'; | import { getErrorData, MastodonLogger } from '@/server/api/mastodon/MastodonLogger.js'; | ||||||
| import { convertConversation, convertList, MastoConverters } from '../converters.js'; | import { convertList, MastoConverters } from '../converters.js'; | ||||||
| import { getClient } from '../MastodonApiServerService.js'; | import { getClient, MastodonApiServerService } from '../MastodonApiServerService.js'; | ||||||
| import { parseTimelineArgs, TimelineArgs, toBoolean } from '../timelineArgs.js'; | import { parseTimelineArgs, TimelineArgs, toBoolean } from '../timelineArgs.js'; | ||||||
| import type { Entity } from 'megalodon'; | import type { Entity } from 'megalodon'; | ||||||
| import type { FastifyInstance } from 'fastify'; | import type { FastifyInstance } from 'fastify'; | ||||||
|  | @ -15,18 +15,17 @@ export class ApiTimelineMastodon { | ||||||
| 		private readonly fastify: FastifyInstance, | 		private readonly fastify: FastifyInstance, | ||||||
| 		private readonly mastoConverters: MastoConverters, | 		private readonly mastoConverters: MastoConverters, | ||||||
| 		private readonly logger: MastodonLogger, | 		private readonly logger: MastodonLogger, | ||||||
|  | 		private readonly mastodon: MastodonApiServerService, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
| 	public getTL() { | 	public getTL() { | ||||||
| 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/timelines/public', async (_request, reply) => { | 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/timelines/public', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = toBoolean(_request.query.local) | 				const data = toBoolean(_request.query.local) | ||||||
| 					? await client.getLocalTimeline(parseTimelineArgs(_request.query)) | 					? await client.getLocalTimeline(parseTimelineArgs(_request.query)) | ||||||
| 					: await client.getPublicTimeline(parseTimelineArgs(_request.query)); | 					: await client.getPublicTimeline(parseTimelineArgs(_request.query)); | ||||||
| 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status)))); | 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status, me)))); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('GET /v1/timelines/public', data); | 				this.logger.error('GET /v1/timelines/public', data); | ||||||
|  | @ -37,12 +36,10 @@ export class ApiTimelineMastodon { | ||||||
| 
 | 
 | ||||||
| 	public getHomeTl() { | 	public getHomeTl() { | ||||||
| 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/timelines/home', async (_request, reply) => { | 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/timelines/home', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.getHomeTimeline(parseTimelineArgs(_request.query)); | 				const data = await client.getHomeTimeline(parseTimelineArgs(_request.query)); | ||||||
| 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status)))); | 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status, me)))); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('GET /v1/timelines/home', data); | 				this.logger.error('GET /v1/timelines/home', data); | ||||||
|  | @ -53,13 +50,11 @@ export class ApiTimelineMastodon { | ||||||
| 
 | 
 | ||||||
| 	public getTagTl() { | 	public getTagTl() { | ||||||
| 		this.fastify.get<{ Params: { hashtag?: string }, Querystring: TimelineArgs }>('/v1/timelines/tag/:hashtag', async (_request, reply) => { | 		this.fastify.get<{ Params: { hashtag?: string }, Querystring: TimelineArgs }>('/v1/timelines/tag/:hashtag', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.hashtag) return reply.code(400).send({ error: 'Missing required parameter "hashtag"' }); | 				if (!_request.params.hashtag) return reply.code(400).send({ error: 'Missing required parameter "hashtag"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.getTagTimeline(_request.params.hashtag, parseTimelineArgs(_request.query)); | 				const data = await client.getTagTimeline(_request.params.hashtag, parseTimelineArgs(_request.query)); | ||||||
| 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status)))); | 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status, me)))); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`GET /v1/timelines/tag/${_request.params.hashtag}`, data); | 				this.logger.error(`GET /v1/timelines/tag/${_request.params.hashtag}`, data); | ||||||
|  | @ -70,13 +65,11 @@ export class ApiTimelineMastodon { | ||||||
| 
 | 
 | ||||||
| 	public getListTL() { | 	public getListTL() { | ||||||
| 		this.fastify.get<{ Params: { id?: string }, Querystring: TimelineArgs }>('/v1/timelines/list/:id', async (_request, reply) => { | 		this.fastify.get<{ Params: { id?: string }, Querystring: TimelineArgs }>('/v1/timelines/list/:id', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
| 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | 				if (!_request.params.id) return reply.code(400).send({ error: 'Missing required parameter "id"' }); | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.getListTimeline(_request.params.id, parseTimelineArgs(_request.query)); | 				const data = await client.getListTimeline(_request.params.id, parseTimelineArgs(_request.query)); | ||||||
| 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status)))); | 				reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoConverters.convertStatus(status, me)))); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error(`GET /v1/timelines/list/${_request.params.id}`, data); | 				this.logger.error(`GET /v1/timelines/list/${_request.params.id}`, data); | ||||||
|  | @ -87,12 +80,11 @@ export class ApiTimelineMastodon { | ||||||
| 
 | 
 | ||||||
| 	public getConversations() { | 	public getConversations() { | ||||||
| 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/conversations', async (_request, reply) => { | 		this.fastify.get<{ Querystring: TimelineArgs }>('/v1/conversations', async (_request, reply) => { | ||||||
| 			const BASE_URL = `${_request.protocol}://${_request.host}`; |  | ||||||
| 			const accessTokens = _request.headers.authorization; |  | ||||||
| 			const client = getClient(BASE_URL, accessTokens); |  | ||||||
| 			try { | 			try { | ||||||
|  | 				const { client, me } = await this.mastodon.getAuthClient(_request); | ||||||
| 				const data = await client.getConversationTimeline(parseTimelineArgs(_request.query)); | 				const data = await client.getConversationTimeline(parseTimelineArgs(_request.query)); | ||||||
| 				reply.send(data.data.map((conversation: Entity.Conversation) => convertConversation(conversation))); | 				const conversations = await Promise.all(data.data.map(async (conversation: Entity.Conversation) => await this.mastoConverters.convertConversation(conversation, me))); | ||||||
|  | 				reply.send(conversations); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				const data = getErrorData(e); | 				const data = getErrorData(e); | ||||||
| 				this.logger.error('GET /v1/conversations', data); | 				this.logger.error('GET /v1/conversations', data); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue