feat(backend): リプライ付き/ファイル付きノートのフィードの追加

This commit is contained in:
zyoshoka 2023-11-12 00:50:54 +09:00
parent 50430e310a
commit b40e20a207
No known key found for this signature in database
GPG Key ID: 0C2CB8FBA309A5B8
2 changed files with 52 additions and 39 deletions

View File

@ -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 } from 'fastify';
import type { FastifyInstance, FastifyPluginOptions, FastifyReply, FastifyRequest } from 'fastify';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@ -416,55 +416,51 @@ export class ClientServerService {
// URL preview endpoint
fastify.get<{ Querystring: { url: string; lang: string; } }>('/url', (request, reply) => this.urlPreviewService.handle(request, reply));
const getFeed = async (acct: string) => {
const { username, host } = Acct.parse(acct);
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,
});
return user && await this.feedService.packFeed(user);
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', async (request, reply) => {
const feed = await getFeed(request.params.user);
if (feed) {
reply.header('Content-Type', 'application/atom+xml; charset=utf-8');
return feed.atom1();
} else {
reply.code(404);
return;
}
});
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 }));
// RSS
fastify.get<{ Params: { user: string; } }>('/@:user.rss', async (request, reply) => {
const feed = await getFeed(request.params.user);
if (feed) {
reply.header('Content-Type', 'application/rss+xml; charset=utf-8');
return feed.rss2();
} else {
reply.code(404);
return;
}
});
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 }));
// JSON
fastify.get<{ Params: { user: string; } }>('/@:user.json', async (request, reply) => {
const feed = await getFeed(request.params.user);
if (feed) {
reply.header('Content-Type', 'application/json; charset=utf-8');
return feed.json1();
} else {
reply.code(404);
return;
}
});
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 }));
//#region SSR (for crawlers)
// User

View File

@ -4,7 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import { In, IsNull } from 'typeorm';
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';
@ -37,7 +37,18 @@ export class FeedService {
}
@bindThis
public async packFeed(user: MiUser) {
public async packFeed(
user: MiUser,
options?: {
withReplies?: boolean;
withFiles?: boolean;
},
) {
const opts = Object.assign({
withReplies: false,
withFiles: false,
}, options);
const author = {
link: `${this.config.url}/@${user.username}`,
name: user.name ?? user.username,
@ -50,6 +61,12 @@ export class FeedService {
userId: user.id,
renoteId: IsNull(),
visibility: In(['public', 'home']),
...(opts.withReplies ? {} : {
replyId: IsNull(),
}),
...(opts.withFiles ? {
fileIds: Not(Equal('{}')),
} : {}),
},
order: { id: -1 },
take: 20,