make the /@user.rss atom and json endpoint work
This commit is contained in:
commit
4e47a558f0
|
@ -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({
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue