make the /@user.rss atom and json endpoint work

This commit is contained in:
Kaity A 2022-11-26 10:58:24 +00:00
commit 4e47a558f0
4 changed files with 79 additions and 33 deletions

View file

@ -232,8 +232,43 @@ const getFeed = async (acct: string) => {
return user && await packFeed(user); return user && await packFeed(user);
}; };
// As the /@user[.json|.rss|.atom]/sub endpoint is complicated, we will use a regex to switch between them.
const reUser = new RegExp(`^/@(?<user>[^/]+?)(?:\.(?<feed>json|rss|atom))?(?:/(?<sub>[^/]+))?$`);
router.get(reUser, async (ctx, next) => {
const groups = reUser.exec(ctx.originalUrl)?.groups;
if (!groups) {
await next();
return;
}
ctx.params = groups;
console.log(ctx, ctx.params)
if (groups.feed) {
if (groups.sub) {
await next();
return;
}
switch (groups.feed) {
case 'json':
await jsonFeed(ctx, next);
break;
case 'rss':
await rssFeed(ctx, next);
break;
case 'atom':
await atomFeed(ctx, next);
break;
}
return;
}
await userPage(ctx, next);
});
// Atom // Atom
router.get('/@:user.atom', async ctx => { const atomFeed: Router.Middleware = async ctx => {
const feed = await getFeed(ctx.params.user); const feed = await getFeed(ctx.params.user);
if (feed) { if (feed) {
@ -242,10 +277,10 @@ router.get('/@:user.atom', async ctx => {
} else { } else {
ctx.status = 404; ctx.status = 404;
} }
}); };
// RSS // RSS
router.get('/@:user.rss', async ctx => { const rssFeed: Router.Middleware = async ctx => {
const feed = await getFeed(ctx.params.user); const feed = await getFeed(ctx.params.user);
if (feed) { if (feed) {
@ -254,10 +289,10 @@ router.get('/@:user.rss', async ctx => {
} else { } else {
ctx.status = 404; ctx.status = 404;
} }
}); };
// JSON // JSON
router.get('/@:user.json', async ctx => { const jsonFeed: Router.Middleware = async ctx => {
const feed = await getFeed(ctx.params.user); const feed = await getFeed(ctx.params.user);
if (feed) { if (feed) {
@ -266,43 +301,47 @@ router.get('/@:user.json', async ctx => {
} else { } else {
ctx.status = 404; ctx.status = 404;
} }
}); };
//#region SSR (for crawlers) //#region SSR (for crawlers)
// User // User
router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { const userPage: Router.Middleware = async (ctx, next) => {
const { username, host } = Acct.parse(ctx.params.user); const userParam = ctx.params.user;
const subParam = ctx.params.sub;
const { username, host } = Acct.parse(userParam);
const user = await Users.findOneBy({ const user = await Users.findOneBy({
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: host ?? IsNull(), host: host ?? IsNull(),
isSuspended: false, isSuspended: false,
}); });
if (user != null) { if (user === null) {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
const meta = await fetchMeta();
const me = profile.fields
? profile.fields
.filter(filed => filed.value != null && filed.value.match(/^https?:/))
.map(field => field.value)
: [];
await ctx.render('user', {
user, profile, me,
avatarUrl: await Users.getAvatarUrl(user),
sub: ctx.params.sub,
instanceName: meta.name || 'Calckey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
privateMode: meta.privateMode,
});
ctx.set('Cache-Control', 'public, max-age=15');
} else {
// リモートユーザーなので
// モデレータがAPI経由で参照可能にするために404にはしない
await next(); await next();
return;
} }
});
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
const meta = await fetchMeta();
const me = profile.fields
? profile.fields
.filter(filed => filed.value != null && filed.value.match(/^https?:/))
.map(field => field.value)
: [];
const userDetail = {
user, profile, me,
avatarUrl: await Users.getAvatarUrl(user),
sub: subParam,
instanceName: meta.name || 'Calckey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
privateMode: meta.privateMode,
};
await ctx.render('user', userDetail);
ctx.set('Cache-Control', 'public, max-age=15');
};
router.get('/users/:user', async ctx => { router.get('/users/:user', async ctx => {
const user = await Users.findOneBy({ const user = await Users.findOneBy({

View file

@ -426,7 +426,8 @@ function readPromo() {
> .article { > .article {
display: flex; display: flex;
padding: 28px 32px 18px; padding: 28px 32px 18px;
cursor: pointer;
> .avatar { > .avatar {
flex-shrink: 0; flex-shrink: 0;
display: block; display: block;

View file

@ -346,6 +346,8 @@ if (appearNote.replyId) {
> .reply-to-more { > .reply-to-more {
opacity: 0.7; opacity: 0.7;
cursor: pointer;
} }
> .renote { > .renote {
@ -543,6 +545,7 @@ if (appearNote.replyId) {
> .reply { > .reply {
border-top: solid 0.5px var(--divider); border-top: solid 0.5px var(--divider);
cursor: pointer;
} }
> .reply, .reply-to, .reply-to-more { > .reply, .reply-to, .reply-to-more {

View file

@ -65,6 +65,7 @@ const replies: misskey.entities.Note[] = props.conversation?.filter(item => item
&.children { &.children {
padding: 10px 0 0 16px; padding: 10px 0 0 16px;
font-size: 1em; font-size: 1em;
cursor: auto;
&.max-width_450px { &.max-width_450px {
padding: 10px 0 0 8px; padding: 10px 0 0 8px;
@ -86,9 +87,11 @@ const replies: misskey.entities.Note[] = props.conversation?.filter(item => item
> .body { > .body {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
cursor: pointer;
> .header { > .header {
margin-bottom: 2px; margin-bottom: 2px;
cursor: auto;
} }
> .body { > .body {