From ada1698ead5fef7b59ff2d3d7872d78a4518c931 Mon Sep 17 00:00:00 2001 From: zyoshoka Date: Sun, 12 Nov 2023 13:38:33 +0900 Subject: [PATCH] fix: wrong json subtype --- .../src/server/web/ClientServerService.ts | 51 ++++-------------- .../backend/src/server/web/FeedService.ts | 53 +++++++++++++++++-- 2 files changed, 59 insertions(+), 45 deletions(-) diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index a15c0a7154..14eac0803f 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -40,7 +40,7 @@ import { RoleService } from '@/core/RoleService.js'; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; -import type { FastifyInstance, FastifyPluginOptions, FastifyReply, FastifyRequest } from 'fastify'; +import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -416,51 +416,20 @@ export class ClientServerService { // URL preview endpoint fastify.get<{ Querystring: { url: string; lang: string; } }>('/url', (request, reply) => this.urlPreviewService.handle(request, reply)); - const feedHandler = ( - feedType: 'atom' | 'rss' | 'json', - options?: { - withReplies?: boolean; - withFiles?: boolean; - }, - ) => async ( - request: FastifyRequest<{ Params: { user: string; } }>, - reply: FastifyReply, - ) => { - const { username, host } = Acct.parse(request.params.user); - const user = await this.usersRepository.findOneBy({ - usernameLower: username.toLowerCase(), - host: host ?? IsNull(), - isSuspended: false, - }); - - const feed = user && await this.feedService.packFeed(user, options); - - if (feed) { - reply.header('Content-Type', `application/${feedType}+xml; charset=utf-8`); - - if (feedType === 'atom') return feed.atom1(); - else if (feedType === 'rss') return feed.rss2(); - else return feed.json1(); - } else { - reply.code(404); - return; - } - }; - // Atom - fastify.get<{ Params: { user: string; } }>('/@:user.atom', feedHandler('atom')); - fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.atom', feedHandler('atom', { withReplies: true })); - fastify.get<{ Params: { user: string; } }>('/@:user.with_files.atom', feedHandler('atom', { withFiles: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.atom', this.feedService.handle('atom')); + fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.atom', this.feedService.handle('atom', { withReplies: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.with_files.atom', this.feedService.handle('atom', { withFiles: true })); // RSS - fastify.get<{ Params: { user: string; } }>('/@:user.rss', feedHandler('rss')); - fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.rss', feedHandler('rss', { withReplies: true })); - fastify.get<{ Params: { user: string; } }>('/@:user.with_files.rss', feedHandler('rss', { withFiles: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.rss', this.feedService.handle('rss')); + fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.rss', this.feedService.handle('rss', { withReplies: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.with_files.rss', this.feedService.handle('rss', { withFiles: true })); // JSON - fastify.get<{ Params: { user: string; } }>('/@:user.json', feedHandler('json')); - fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.json', feedHandler('json', { withReplies: true })); - fastify.get<{ Params: { user: string; } }>('/@:user.with_files.json', feedHandler('json', { withFiles: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.json', this.feedService.handle('json')); + fastify.get<{ Params: { user: string; } }>('/@:user.with_replies.json', this.feedService.handle('json', { withReplies: true })); + fastify.get<{ Params: { user: string; } }>('/@:user.with_files.json', this.feedService.handle('json', { withFiles: true })); //#region SSR (for crawlers) // User diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 5056c08a80..822b918191 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -7,13 +7,15 @@ import { Inject, Injectable } from '@nestjs/common'; import { Equal, In, IsNull, Not } from 'typeorm'; import { Feed } from 'feed'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository, NotesRepository, UserProfilesRepository } from '@/models/_.js'; +import * as Acct from '@/misc/acct.js'; +import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import type { MiUser } from '@/models/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import type { FastifyReply, FastifyRequest } from 'fastify'; @Injectable() export class FeedService { @@ -21,6 +23,9 @@ export class FeedService { @Inject(DI.config) private config: Config, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, @@ -37,7 +42,7 @@ export class FeedService { } @bindThis - public async packFeed( + private async packFeed( user: MiUser, options?: { withReplies?: boolean; @@ -72,6 +77,8 @@ export class FeedService { take: 20, }); + const optionLink = opts.withReplies ? '.with_replies' : opts.withFiles ? '.with_files' : ''; + const feed = new Feed({ id: author.link, title: `${author.name} (@${user.username}@${this.config.host})`, @@ -81,8 +88,8 @@ export class FeedService { link: author.link, image: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), feedLinks: { - json: `${author.link}.json`, - atom: `${author.link}.atom`, + json: `${author.link + optionLink}.json`, + atom: `${author.link + optionLink}.atom`, }, author, copyright: user.name ?? user.username, @@ -106,4 +113,42 @@ export class FeedService { return feed; } + + @bindThis + public handle( + feedType: 'atom' | 'rss' | 'json', + options?: { + withReplies?: boolean; + withFiles?: boolean; + }, + ) { + return async ( + request: FastifyRequest<{ Params: { user: string; } }>, + reply: FastifyReply, + ) => { + const { username, host } = Acct.parse(request.params.user); + const user = await this.usersRepository.findOneBy({ + usernameLower: username.toLowerCase(), + host: host ?? IsNull(), + isSuspended: false, + }); + + const feed = user && await this.packFeed(user, options); + + if (feed) { + const subtype = feedType === 'atom' || feedType === 'rss' + ? `${feedType}+xml` + : feedType; + + reply.header('Content-Type', `application/${subtype}; charset=utf-8`); + + if (feedType === 'atom') return feed.atom1(); + else if (feedType === 'rss') return feed.rss2(); + else return feed.json1(); + } else { + reply.code(404); + return; + } + }; + } }