mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-21 17:37:29 -07:00
[mastodon-client] Remove ID conversion
BREAKING: Please log out and log back in of any clients in use, as their cache is now invalid
This commit is contained in:
parent
011d7f36c3
commit
9d24f8aea5
15 changed files with 139 additions and 436 deletions
|
@ -1,27 +0,0 @@
|
|||
export enum IdType {
|
||||
IceshrimpId,
|
||||
MastodonId
|
||||
}
|
||||
|
||||
const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
//FIXME: This implementation breaks for IceshrimpIDs with leading zeroes
|
||||
//FIXME: Make this idempotent
|
||||
export function convertId(id: string, target: IdType): string {
|
||||
if (target == IdType.IceshrimpId) {
|
||||
return BigInt(id).toString(36);
|
||||
}
|
||||
else if (target == IdType.MastodonId) {
|
||||
let result = 0n;
|
||||
const iter = id.toLowerCase();
|
||||
|
||||
for (let i = 0; i < iter.length; i++){
|
||||
const char = iter[i];
|
||||
if (!chars.includes(char)) throw new Error('Invalid ID');
|
||||
result = result * 36n + BigInt(chars.indexOf(char));
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
throw new Error('Unknown ID type');
|
||||
}
|
|
@ -20,10 +20,6 @@ import verifyEmail from "./private/verify-email.js";
|
|||
import discord from "./service/discord.js";
|
||||
import github from "./service/github.js";
|
||||
import twitter from "./service/twitter.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
|
||||
// re-export native rust id conversion (function and enum)
|
||||
export { IdType, convertId };
|
||||
|
||||
// Init app
|
||||
const app = new Koa();
|
||||
|
|
|
@ -1,117 +0,0 @@
|
|||
import { convertId, IdType } from "../index.js";
|
||||
|
||||
// It's *very* important to put `param = structuredClone(param)` at the top of each function that doesn't simply call simpleConvertId on the param object directly.
|
||||
|
||||
function simpleConvertId(data: any) {
|
||||
// copy the object to bypass weird pass by reference bugs
|
||||
data = structuredClone(data);
|
||||
data.id = convertId(data.id, IdType.MastodonId);
|
||||
return data;
|
||||
}
|
||||
|
||||
export function convertAccountId(account: MastodonEntity.Account | MastodonEntity.MutedAccount) {
|
||||
return simpleConvertId(account);
|
||||
}
|
||||
|
||||
export function convertAnnouncementId(announcement: MastodonEntity.Announcement) {
|
||||
return simpleConvertId(announcement);
|
||||
}
|
||||
|
||||
export function convertAttachmentId(attachment: MastodonEntity.Attachment) {
|
||||
return simpleConvertId(attachment);
|
||||
}
|
||||
|
||||
export function convertListId(list: MastodonEntity.List) {
|
||||
return simpleConvertId(list);
|
||||
}
|
||||
|
||||
export function convertPollId(poll: MastodonEntity.Poll) {
|
||||
return simpleConvertId(poll);
|
||||
}
|
||||
|
||||
export function convertRelationshipId(relationship: MastodonEntity.Relationship) {
|
||||
return simpleConvertId(relationship);
|
||||
}
|
||||
|
||||
export function convertStatusSourceId(statusSource: MastodonEntity.StatusSource) {
|
||||
return simpleConvertId(statusSource);
|
||||
}
|
||||
|
||||
export function convertSuggestionIds(suggestion: MastodonEntity.SuggestedAccount) {
|
||||
suggestion = structuredClone(suggestion);
|
||||
suggestion.account = convertAccountId(suggestion.account)
|
||||
return suggestion
|
||||
}
|
||||
|
||||
export function convertNotificationIds(notification: MastodonEntity.Notification) {
|
||||
notification = structuredClone(notification);
|
||||
notification.account = convertAccountId(notification.account);
|
||||
notification.id = convertId(notification.id, IdType.MastodonId);
|
||||
if (notification.status)
|
||||
notification.status = convertStatusIds(notification.status);
|
||||
if (notification.reaction)
|
||||
notification.reaction = convertReactionIds(notification.reaction);
|
||||
return notification;
|
||||
}
|
||||
|
||||
export function convertReactionIds(reaction: MastodonEntity.Reaction) {
|
||||
reaction = structuredClone(reaction);
|
||||
if (reaction.accounts) {
|
||||
reaction.accounts = reaction.accounts.map(convertAccountId);
|
||||
}
|
||||
return reaction;
|
||||
}
|
||||
|
||||
export function convertSearchIds(search: MastodonEntity.Search) {
|
||||
search = structuredClone(search);
|
||||
search.accounts = search.accounts.map(p => convertAccountId(p));
|
||||
search.statuses = search.statuses.map(p => convertStatusIds(p));
|
||||
return search;
|
||||
}
|
||||
|
||||
export function convertStatusIds(status: MastodonEntity.Status) {
|
||||
status = structuredClone(status);
|
||||
status.account = convertAccountId(status.account);
|
||||
status.id = convertId(status.id, IdType.MastodonId);
|
||||
if (status.in_reply_to_account_id)
|
||||
status.in_reply_to_account_id = convertId(
|
||||
status.in_reply_to_account_id,
|
||||
IdType.MastodonId,
|
||||
);
|
||||
if (status.in_reply_to_id)
|
||||
status.in_reply_to_id = convertId(status.in_reply_to_id, IdType.MastodonId);
|
||||
status.media_attachments = status.media_attachments.map((attachment) =>
|
||||
convertAttachmentId(attachment),
|
||||
);
|
||||
status.mentions = status.mentions.map((mention) => ({
|
||||
...mention,
|
||||
id: convertId(mention.id, IdType.MastodonId),
|
||||
}));
|
||||
if (status.poll) status.poll = convertPollId(status.poll);
|
||||
if (status.reblog) status.reblog = convertStatusIds(status.reblog);
|
||||
if (status.quote) status.quote = convertStatusIds(status.quote);
|
||||
status.reactions = status.reactions.map(convertReactionIds);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
export function convertStatusEditIds(edit: MastodonEntity.StatusEdit) {
|
||||
edit = structuredClone(edit);
|
||||
edit.account = convertAccountId(edit.account);
|
||||
edit.media_attachments = edit.media_attachments.map((attachment) =>
|
||||
convertAttachmentId(attachment),
|
||||
);
|
||||
if (edit.poll) edit.poll = convertPollId(edit.poll);
|
||||
return edit;
|
||||
}
|
||||
|
||||
export function convertConversationIds(conversation: MastodonEntity.Conversation) {
|
||||
conversation = structuredClone(conversation);
|
||||
conversation.id = convertId(conversation.id, IdType.MastodonId);
|
||||
conversation.accounts = conversation.accounts.map(convertAccountId);
|
||||
if (conversation.last_status) {
|
||||
conversation.last_status = convertStatusIds(conversation.last_status);
|
||||
}
|
||||
|
||||
return conversation;
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import { convertAccountId, convertListId, convertRelationshipId, convertStatusIds, } from "../converters.js";
|
||||
import { argsToBools, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||
|
@ -12,53 +10,43 @@ export function setupEndpointsAccount(router: Router): void {
|
|||
router.get("/v1/accounts/verify_credentials",
|
||||
auth(true, ['read:accounts']),
|
||||
async (ctx) => {
|
||||
const acct = await UserHelpers.verifyCredentials(ctx);
|
||||
ctx.body = convertAccountId(acct);
|
||||
ctx.body = await UserHelpers.verifyCredentials(ctx);
|
||||
}
|
||||
);
|
||||
router.patch("/v1/accounts/update_credentials",
|
||||
auth(true, ['write:accounts']),
|
||||
async (ctx) => {
|
||||
const acct = await UserHelpers.updateCredentials(ctx);
|
||||
ctx.body = convertAccountId(acct)
|
||||
ctx.body = await UserHelpers.updateCredentials(ctx)
|
||||
}
|
||||
);
|
||||
router.get("/v1/accounts/lookup",
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(ctx.query);
|
||||
const user = await UserHelpers.getUserFromAcct(args.acct);
|
||||
const account = await UserConverter.encode(user, ctx);
|
||||
ctx.body = convertAccountId(account);
|
||||
ctx.body = await UserConverter.encode(user, ctx);
|
||||
}
|
||||
);
|
||||
router.get("/v1/accounts/relationships",
|
||||
auth(true, ['read:follows']),
|
||||
async (ctx) => {
|
||||
const ids = (normalizeUrlQuery(ctx.query, ['id[]'])['id[]'] ?? [])
|
||||
.map((id: string) => convertId(id, IdType.IceshrimpId));
|
||||
const result = await UserHelpers.getUserRelationhipToMany(ids, ctx.user.id);
|
||||
ctx.body = result.map(rel => convertRelationshipId(rel));
|
||||
const ids = (normalizeUrlQuery(ctx.query, ['id[]'])['id[]'] ?? []);
|
||||
ctx.body = await UserHelpers.getUserRelationhipToMany(ids, ctx.user.id);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>("/v1/accounts/:id",
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const account = await UserConverter.encode(await UserHelpers.getUserOr404(userId), ctx);
|
||||
ctx.body = convertAccountId(account);
|
||||
ctx.body = await UserConverter.encode(await UserHelpers.getUserOr404(ctx.params.id), ctx);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/statuses",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
|
||||
const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)));
|
||||
const res = await UserHelpers.getUserStatuses(query, args.max_id, args.since_id, args.min_id, args.limit, args['only_media'], args['exclude_replies'], args['exclude_reblogs'], args.pinned, args.tagged, ctx);
|
||||
const tl = await NoteConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
|
@ -71,72 +59,61 @@ export function setupEndpointsAccount(router: Router): void {
|
|||
"/v1/accounts/:id/followers",
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserFollowers(query, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const followers = await UserConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = followers.map((account) => convertAccountId(account));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/following",
|
||||
auth(false),
|
||||
async (ctx) => {
|
||||
const userId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const query = await UserHelpers.getUserCachedOr404(userId, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const query = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserFollowing(query, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const following = await UserConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = following.map((account) => convertAccountId(account));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/lists",
|
||||
auth(true, ["read:lists"]),
|
||||
async (ctx) => {
|
||||
const member = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const results = await ListHelpers.getListsByMember(member, ctx);
|
||||
ctx.body = results.map(p => convertListId(p));
|
||||
const member = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await ListHelpers.getListsByMember(member, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/follow",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
//FIXME: Parse form data
|
||||
const result = await UserHelpers.followUser(target, true, false, ctx);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
ctx.body = await UserHelpers.followUser(target, true, false, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/unfollow",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unfollowUser(target, ctx);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.unfollowUser(target, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/block",
|
||||
auth(true, ["write:blocks"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.blockUser(target, ctx);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.blockUser(target, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/unblock",
|
||||
auth(true, ["write:blocks"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unblockUser(target, ctx);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.unblockUser(target, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
|
@ -145,18 +122,16 @@ export function setupEndpointsAccount(router: Router): void {
|
|||
async (ctx) => {
|
||||
//FIXME: parse form data
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query, ['duration']), ['notifications']));
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.muteUser(target, args.notifications, args.duration, ctx);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.muteUser(target, args.notifications, args.duration, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/accounts/:id/unmute",
|
||||
auth(true, ["write:mutes"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.unmuteUser(target, ctx);
|
||||
ctx.body = convertRelationshipId(result)
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.unmuteUser(target, ctx);
|
||||
},
|
||||
);
|
||||
router.get("/v1/featured_tags",
|
||||
|
@ -172,63 +147,56 @@ export function setupEndpointsAccount(router: Router): void {
|
|||
router.get("/v1/bookmarks",
|
||||
auth(true, ["read:bookmarks"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserBookmarks(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const bookmarks = await NoteConverter.encodeMany(res, ctx);
|
||||
ctx.body = bookmarks.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.get("/v1/favourites",
|
||||
auth(true, ["read:favourites"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserFavorites(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const favorites = await NoteConverter.encodeMany(res, ctx);
|
||||
ctx.body = favorites.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.get("/v1/mutes",
|
||||
auth(true, ["read:mutes"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const res = await UserHelpers.getUserMutes(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
ctx.body = res.map(m => convertAccountId(m));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
ctx.body = await UserHelpers.getUserMutes(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
}
|
||||
);
|
||||
router.get("/v1/blocks",
|
||||
auth(true, ["read:blocks"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserBlocks(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const blocks = await UserConverter.encodeMany(res, ctx);
|
||||
ctx.body = blocks.map(b => convertAccountId(b));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.get("/v1/follow_requests",
|
||||
auth(true, ["read:follows"]),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await UserHelpers.getUserFollowRequests(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const requests = await UserConverter.encodeMany(res, ctx);
|
||||
ctx.body = requests.map(b => convertAccountId(b));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/follow_requests/:id/authorize",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.acceptFollowRequest(target, ctx);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.acceptFollowRequest(target, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/follow_requests/:id/reject",
|
||||
auth(true, ["write:follows"]),
|
||||
async (ctx) => {
|
||||
const target = await UserHelpers.getUserCachedOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const result = await UserHelpers.rejectFollowRequest(target, ctx);
|
||||
ctx.body = convertRelationshipId(result);
|
||||
const target = await UserHelpers.getUserCachedOr404(ctx.params.id, ctx);
|
||||
ctx.body = await UserHelpers.rejectFollowRequest(target, ctx);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { AuthHelpers } from "@/server/api/mastodon/helpers/auth.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
import { AuthConverter } from "@/server/api/mastodon/converters/auth.js";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
|
@ -15,7 +14,7 @@ export function setupEndpointsAuth(router: Router): void {
|
|||
const red = body.redirect_uris;
|
||||
const appData = await AuthHelpers.registerApp(body['client_name'], scopeArr, red, body['website']);
|
||||
ctx.body = {
|
||||
id: convertId(appData.id, IdType.MastodonId),
|
||||
id: appData.id,
|
||||
name: appData.name,
|
||||
website: body.website,
|
||||
redirect_uri: red,
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { convertAccountId, convertListId, } from "../converters.js";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { UserLists } from "@/models/index.js";
|
||||
|
@ -14,18 +12,14 @@ export function setupEndpointsList(router: Router): void {
|
|||
router.get("/v1/lists",
|
||||
auth(true, ['read:lists']),
|
||||
async (ctx, reply) => {
|
||||
ctx.body = await ListHelpers.getLists(ctx)
|
||||
.then(p => p.map(list => convertListId(list)));
|
||||
ctx.body = await ListHelpers.getLists(ctx);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/lists/:id",
|
||||
auth(true, ['read:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
|
||||
ctx.body = await ListHelpers.getListOr404(id, ctx)
|
||||
.then(p => convertListId(p));
|
||||
ctx.body = await ListHelpers.getListOr404(ctx.params.id, ctx);
|
||||
},
|
||||
);
|
||||
router.post("/v1/lists",
|
||||
|
@ -33,31 +27,26 @@ export function setupEndpointsList(router: Router): void {
|
|||
async (ctx, reply) => {
|
||||
const body = ctx.request.body as any;
|
||||
const title = (body.title ?? '').trim();
|
||||
|
||||
ctx.body = await ListHelpers.createList(title, ctx)
|
||||
.then(p => convertListId(p));
|
||||
ctx.body = await ListHelpers.createList(title, ctx);
|
||||
}
|
||||
);
|
||||
router.put<{ Params: { id: string } }>(
|
||||
"/v1/lists/:id",
|
||||
auth(true, ['write:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
|
||||
if (!list) throw new MastoApiError(404);
|
||||
|
||||
const body = ctx.request.body as any;
|
||||
const title = (body.title ?? '').trim();
|
||||
ctx.body = await ListHelpers.updateList(list, title, ctx)
|
||||
.then(p => convertListId(p));
|
||||
ctx.body = await ListHelpers.updateList(list, title, ctx);
|
||||
},
|
||||
);
|
||||
router.delete<{ Params: { id: string } }>(
|
||||
"/v1/lists/:id",
|
||||
auth(true, ['write:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
|
||||
if (!list) throw new MastoApiError(404);
|
||||
|
||||
await ListHelpers.deleteList(list, ctx);
|
||||
|
@ -68,26 +57,22 @@ export function setupEndpointsList(router: Router): void {
|
|||
"/v1/lists/:id/accounts",
|
||||
auth(true, ['read:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await ListHelpers.getListUsers(id, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const accounts = await UserConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = accounts.map(account => convertAccountId(account));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query));
|
||||
const res = await ListHelpers.getListUsers(ctx.params.id, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/lists/:id/accounts",
|
||||
auth(true, ['write:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
|
||||
if (!list) throw new MastoApiError(404);
|
||||
|
||||
const body = ctx.request.body as any;
|
||||
if (!body['account_ids']) throw new MastoApiError(400, "Missing account_ids[] field");
|
||||
|
||||
const ids = toArray(body['account_ids']).map(p => convertId(p, IdType.IceshrimpId));
|
||||
const ids = toArray(body['account_ids']);
|
||||
const targets = await Promise.all(ids.map(p => getUser(p)));
|
||||
await ListHelpers.addToList(list, targets, ctx);
|
||||
ctx.body = {}
|
||||
|
@ -97,14 +82,13 @@ export function setupEndpointsList(router: Router): void {
|
|||
"/v1/lists/:id/accounts",
|
||||
auth(true, ['write:lists']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: id });
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.id });
|
||||
if (!list) throw new MastoApiError(404);
|
||||
|
||||
const body = ctx.request.body as any;
|
||||
if (!body['account_ids']) throw new MastoApiError(400, "Missing account_ids[] field");
|
||||
|
||||
const ids = toArray(body['account_ids']).map(p => convertId(p, IdType.IceshrimpId));
|
||||
const ids = toArray(body['account_ids']);
|
||||
const targets = await Promise.all(ids.map(p => getUser(p)));
|
||||
await ListHelpers.removeFromList(list, targets, ctx);
|
||||
ctx.body = {}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import Router from "@koa/router";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
import { convertAttachmentId } from "@/server/api/mastodon/converters.js";
|
||||
import { MediaHelpers } from "@/server/api/mastodon/helpers/media.js";
|
||||
import { FileConverter } from "@/server/api/mastodon/converters/file.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
|
@ -9,28 +7,23 @@ export function setupEndpointsMedia(router: Router): void {
|
|||
router.get<{ Params: { id: string } }>("/v1/media/:id",
|
||||
auth(true, ['write:media']),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const file = await MediaHelpers.getMediaPackedOr404(id, ctx);
|
||||
const attachment = FileConverter.encode(file);
|
||||
ctx.body = convertAttachmentId(attachment);
|
||||
const file = await MediaHelpers.getMediaPackedOr404(ctx.params.id, ctx);
|
||||
ctx.body = FileConverter.encode(file);
|
||||
}
|
||||
);
|
||||
router.put<{ Params: { id: string } }>("/v1/media/:id",
|
||||
auth(true, ['write:media']),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const file = await MediaHelpers.getMediaOr404(id, ctx);
|
||||
const result = await MediaHelpers.updateMedia(file, ctx)
|
||||
const file = await MediaHelpers.getMediaOr404(ctx.params.id, ctx);
|
||||
ctx.body = await MediaHelpers.updateMedia(file, ctx)
|
||||
.then(p => FileConverter.encode(p));
|
||||
ctx.body = convertAttachmentId(result);
|
||||
}
|
||||
);
|
||||
router.post(["/v2/media", "/v1/media"],
|
||||
auth(true, ['write:media']),
|
||||
async (ctx) => {
|
||||
const result = await MediaHelpers.uploadMedia(ctx)
|
||||
ctx.body = await MediaHelpers.uploadMedia(ctx)
|
||||
.then(p => FileConverter.encode(p));
|
||||
ctx.body = convertAttachmentId(result);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ import Router from "@koa/router";
|
|||
import { MiscHelpers } from "@/server/api/mastodon/helpers/misc.js";
|
||||
import { argsToBools, limitToInt } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { Announcements } from "@/models/index.js";
|
||||
import { convertAnnouncementId, convertStatusIds, convertSuggestionIds } from "@/server/api/mastodon/converters.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
|
||||
|
||||
|
@ -24,8 +22,7 @@ export function setupEndpointsMisc(router: Router): void {
|
|||
auth(true),
|
||||
async (ctx) => {
|
||||
const args = argsToBools(ctx.query, ['with_dismissed']);
|
||||
ctx.body = await MiscHelpers.getAnnouncements(args['with_dismissed'], ctx)
|
||||
.then(p => p.map(x => convertAnnouncementId(x)));
|
||||
ctx.body = await MiscHelpers.getAnnouncements(args['with_dismissed'], ctx);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -33,8 +30,7 @@ export function setupEndpointsMisc(router: Router): void {
|
|||
"/v1/announcements/:id/dismiss",
|
||||
auth(true, ['write:accounts']),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const announcement = await Announcements.findOneBy({ id: id });
|
||||
const announcement = await Announcements.findOneBy({ id: ctx.params.id });
|
||||
if (!announcement) throw new MastoApiError(404);
|
||||
|
||||
await MiscHelpers.dismissAnnouncement(announcement, ctx);
|
||||
|
@ -54,8 +50,7 @@ export function setupEndpointsMisc(router: Router): void {
|
|||
router.get("/v1/trends/statuses",
|
||||
async (ctx) => {
|
||||
const args = limitToInt(ctx.query);
|
||||
ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx)
|
||||
.then(p => p.map(x => convertStatusIds(x)));
|
||||
ctx.body = await MiscHelpers.getTrendingStatuses(args.limit, args.offset, ctx);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -76,8 +71,7 @@ export function setupEndpointsMisc(router: Router): void {
|
|||
auth(true, ['read']),
|
||||
async (ctx) => {
|
||||
const args = limitToInt(ctx.query);
|
||||
ctx.body = await MiscHelpers.getFollowSuggestions(args.limit, ctx)
|
||||
.then(p => p.map(x => convertSuggestionIds(x)));
|
||||
ctx.body = await MiscHelpers.getFollowSuggestions(args.limit, ctx);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { convertNotificationIds } from "../converters.js";
|
||||
import { limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { NotificationHelpers } from "@/server/api/mastodon/helpers/notification.js";
|
||||
import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
|
@ -10,19 +8,17 @@ export function setupEndpointsNotifications(router: Router): void {
|
|||
router.get("/v1/notifications",
|
||||
auth(true, ['read:notifications']),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)), ['types[]', 'exclude_types[]']);
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query), ['types[]', 'exclude_types[]']);
|
||||
const res = await NotificationHelpers.getNotifications(args.max_id, args.since_id, args.min_id, args.limit, args['types[]'], args['exclude_types[]'], args.account_id, ctx);
|
||||
const data = await NotificationConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = data.map(n => convertNotificationIds(n));
|
||||
ctx.body = await NotificationConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
|
||||
router.get("/v1/notifications/:id",
|
||||
auth(true, ['read:notifications']),
|
||||
async (ctx) => {
|
||||
const notification = await NotificationHelpers.getNotificationOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
ctx.body = convertNotificationIds(await NotificationConverter.encode(notification, ctx));
|
||||
const notification = await NotificationHelpers.getNotificationOr404(ctx.params.id, ctx);
|
||||
ctx.body = await NotificationConverter.encode(notification, ctx);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -37,7 +33,7 @@ export function setupEndpointsNotifications(router: Router): void {
|
|||
router.post("/v1/notifications/:id/dismiss",
|
||||
auth(true, ['write:notifications']),
|
||||
async (ctx) => {
|
||||
const notification = await NotificationHelpers.getNotificationOr404(convertId(ctx.params.id, IdType.IceshrimpId), ctx);
|
||||
const notification = await NotificationHelpers.getNotificationOr404(ctx.params.id, ctx);
|
||||
await NotificationHelpers.dismissNotification(notification.id, ctx);
|
||||
ctx.body = {};
|
||||
}
|
||||
|
@ -46,8 +42,7 @@ export function setupEndpointsNotifications(router: Router): void {
|
|||
router.post("/v1/conversations/:id/read",
|
||||
auth(true, ['write:conversations']),
|
||||
async (ctx, reply) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
await NotificationHelpers.markConversationAsRead(id, ctx);
|
||||
await NotificationHelpers.markConversationAsRead(ctx.params.id, ctx);
|
||||
ctx.body = {};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { convertSearchIds } from "../converters.js";
|
||||
import { argsToBools, limitToInt, normalizeUrlQuery } from "./timeline.js";
|
||||
import { SearchHelpers } from "@/server/api/mastodon/helpers/search.js";
|
||||
import { auth } from "@/server/api/mastodon/middleware/auth.js";
|
||||
|
||||
|
@ -8,15 +7,13 @@ export function setupEndpointsSearch(router: Router): void {
|
|||
router.get(["/v1/search", "/v2/search"],
|
||||
auth(true, ['read:search']),
|
||||
async (ctx) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query), ['resolve', 'following', 'exclude_unreviewed'])));
|
||||
const result = await SearchHelpers.search(args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx);
|
||||
|
||||
ctx.body = convertSearchIds(result);
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query), ['resolve', 'following', 'exclude_unreviewed']));
|
||||
ctx.body = await SearchHelpers.search(args.q, args.type, args.resolve, args.following, args.account_id, args['exclude_unreviewed'], args.max_id, args.min_id, args.limit, args.offset, ctx);
|
||||
|
||||
if (ctx.path === "/v1/search") {
|
||||
ctx.body = {
|
||||
...ctx.body,
|
||||
hashtags: result.hashtags.map(p => p.name),
|
||||
hashtags: ctx.body.hashtags.map((p: MastodonEntity.Tag) => p.name),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
import Router from "@koa/router";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import {
|
||||
convertAccountId,
|
||||
convertPollId,
|
||||
convertStatusEditIds,
|
||||
convertStatusIds,
|
||||
convertStatusSourceId,
|
||||
} from "../converters.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { NoteHelpers } from "@/server/api/mastodon/helpers/note.js";
|
||||
import { convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { limitToInt, normalizeUrlQuery } from "@/server/api/mastodon/endpoints/timeline.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { PollHelpers } from "@/server/api/mastodon/helpers/poll.js";
|
||||
import { toArray } from "@/prelude/array.js";
|
||||
|
@ -32,8 +24,7 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
|
||||
let request = NoteHelpers.normalizeComposeOptions(ctx.request.body);
|
||||
ctx.body = await NoteHelpers.createNote(request, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
|
||||
if (key !== null) NoteHelpers.postIdempotencyCache.set(key, { status: ctx.body });
|
||||
}
|
||||
|
@ -41,31 +32,25 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
router.put("/v1/statuses/:id",
|
||||
auth(true, ['write:statuses']),
|
||||
async (ctx) => {
|
||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
let request = NoteHelpers.normalizeEditOptions(ctx.request.body);
|
||||
ctx.body = await NoteHelpers.editNote(request, note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>("/v1/statuses/:id",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
const status = await NoteConverter.encode(note, ctx);
|
||||
ctx.body = convertStatusIds(status);
|
||||
ctx.body = await NoteConverter.encode(note, ctx);
|
||||
}
|
||||
);
|
||||
router.delete<{ Params: { id: string } }>("/v1/statuses/:id",
|
||||
auth(true, ['write:statuses']),
|
||||
async (ctx) => {
|
||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(noteId, ctx);
|
||||
ctx.body = await NoteHelpers.deleteNote(note, ctx)
|
||||
.then(p => convertStatusIds(p));
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
ctx.body = await NoteHelpers.deleteNote(note, ctx);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -74,14 +59,11 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
//FIXME: determine final limits within helper functions instead of here
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
const ancestors = await NoteHelpers.getNoteAncestors(note, ctx.user ? 4096 : 60, ctx)
|
||||
.then(n => NoteConverter.encodeMany(n, ctx))
|
||||
.then(n => n.map(s => convertStatusIds(s)));
|
||||
.then(n => NoteConverter.encodeMany(n, ctx));
|
||||
const descendants = await NoteHelpers.getNoteDescendants(note, ctx.user ? 4096 : 40, ctx.user ? 4096 : 20, ctx)
|
||||
.then(n => NoteConverter.encodeMany(n, ctx))
|
||||
.then(n => n.map(s => convertStatusIds(s)));
|
||||
.then(n => NoteConverter.encodeMany(n, ctx));
|
||||
|
||||
ctx.body = {
|
||||
ancestors,
|
||||
|
@ -93,69 +75,57 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/history",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const res = await NoteHelpers.getNoteEditHistory(note, ctx);
|
||||
ctx.body = res.map(p => convertStatusEditIds(p));
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
ctx.body = await NoteHelpers.getNoteEditHistory(note, ctx);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/statuses/:id/source",
|
||||
auth(true, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const src = NoteHelpers.getNoteSource(note);
|
||||
ctx.body = convertStatusSourceId(src);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
ctx.body = NoteHelpers.getNoteSource(note);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/statuses/:id/reblogged_by",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await NoteHelpers.getNoteRebloggedBy(note, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const users = await UserConverter.encodeMany(res, ctx);
|
||||
ctx.body = users.map(m => convertAccountId(m));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.get<{ Params: { id: string } }>(
|
||||
"/v1/statuses/:id/favourited_by",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query as any)));
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query as any));
|
||||
const res = await NoteHelpers.getNoteFavoritedBy(note, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const users = await UserConverter.encodeMany(res, ctx);
|
||||
ctx.body = users.map(m => convertAccountId(m));
|
||||
ctx.body = await UserConverter.encodeMany(res, ctx);
|
||||
}
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/statuses/:id/favourite",
|
||||
auth(true, ["write:favourites"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
const reaction = await NoteHelpers.getDefaultReaction();
|
||||
|
||||
ctx.body = await NoteHelpers.reactToNote(note, reaction, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
}
|
||||
);
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/statuses/:id/unfavourite",
|
||||
auth(true, ["write:favourites"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.removeReactFromNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -163,12 +133,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/reblog",
|
||||
auth(true, ["write:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.reblogNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -176,12 +144,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/unreblog",
|
||||
auth(true, ["write:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.unreblogNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -189,12 +155,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/bookmark",
|
||||
auth(true, ["write:bookmarks"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.bookmarkNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -202,12 +166,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/unbookmark",
|
||||
auth(true, ["write:bookmarks"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.unbookmarkNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -215,12 +177,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/pin",
|
||||
auth(true, ["write:accounts"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.pinNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -228,12 +188,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/unpin",
|
||||
auth(true, ["write:accounts"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.unpinNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -241,12 +199,10 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/react/:name",
|
||||
auth(true, ["write:favourites"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.reactToNote(note, ctx.params.name, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -254,35 +210,29 @@ export function setupEndpointsStatus(router: Router): void {
|
|||
"/v1/statuses/:id/unreact/:name",
|
||||
auth(true, ["write:favourites"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
|
||||
|
||||
ctx.body = await NoteHelpers.removeReactFromNote(note, ctx)
|
||||
.then(p => NoteConverter.encode(p, ctx))
|
||||
.then(p => convertStatusIds(p));
|
||||
.then(p => NoteConverter.encode(p, ctx));
|
||||
},
|
||||
);
|
||||
router.get<{ Params: { id: string } }>("/v1/polls/:id",
|
||||
auth(false, ["read:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const data = await PollHelpers.getPoll(note, ctx);
|
||||
ctx.body = convertPollId(data);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
|
||||
ctx.body = await PollHelpers.getPoll(note, ctx);
|
||||
});
|
||||
router.post<{ Params: { id: string } }>(
|
||||
"/v1/polls/:id/votes",
|
||||
auth(true, ["write:statuses"]),
|
||||
async (ctx) => {
|
||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||
const note = await NoteHelpers.getNoteOr404(id, ctx);
|
||||
const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx);
|
||||
|
||||
const body: any = ctx.request.body;
|
||||
const choices = toArray(body.choices ?? []).map(p => parseInt(p));
|
||||
if (choices.length < 1) throw new MastoApiError(400, "Must vote for at least one option");
|
||||
|
||||
const data = await PollHelpers.voteInPoll(choices, note, ctx);
|
||||
ctx.body = convertPollId(data);
|
||||
ctx.body = await PollHelpers.voteInPoll(choices, note, ctx);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { ParsedUrlQuery } from "querystring";
|
||||
import { convertConversationIds, convertStatusIds, } from "../converters.js";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
import { TimelineHelpers } from "@/server/api/mastodon/helpers/timeline.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { UserLists } from "@/models/index.js";
|
||||
|
@ -39,16 +37,6 @@ export function argsToBools(q: ParsedUrlQuery, additional: string[] = []) {
|
|||
return object;
|
||||
}
|
||||
|
||||
export function convertPaginationArgsIds(q: ParsedUrlQuery) {
|
||||
if (typeof q.min_id === "string")
|
||||
q.min_id = convertId(q.min_id, IdType.IceshrimpId);
|
||||
if (typeof q.max_id === "string")
|
||||
q.max_id = convertId(q.max_id, IdType.IceshrimpId);
|
||||
if (typeof q.since_id === "string")
|
||||
q.since_id = convertId(q.since_id, IdType.IceshrimpId);
|
||||
return q;
|
||||
}
|
||||
|
||||
export function normalizeUrlQuery(q: ParsedUrlQuery, arrayKeys: string[] = []): any {
|
||||
const dict: any = {};
|
||||
|
||||
|
@ -66,55 +54,44 @@ export function setupEndpointsTimeline(router: Router): void {
|
|||
router.get("/v1/timelines/public",
|
||||
auth(true, ['read:statuses']),
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))));
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)));
|
||||
const res = await TimelineHelpers.getPublicTimeline(args.max_id, args.since_id, args.min_id, args.limit, args.only_media, args.local, args.remote, ctx);
|
||||
const tl = await NoteConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
});
|
||||
router.get<{ Params: { hashtag: string } }>(
|
||||
"/v1/timelines/tag/:hashtag",
|
||||
auth(false, ['read:statuses']),
|
||||
async (ctx, reply) => {
|
||||
const tag = (ctx.params.hashtag ?? '').trim();
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(argsToBools(limitToInt(ctx.query))), ['any[]', 'all[]', 'none[]']);
|
||||
const args = normalizeUrlQuery(argsToBools(limitToInt(ctx.query)), ['any[]', 'all[]', 'none[]']);
|
||||
const res = await TimelineHelpers.getTagTimeline(tag, args.max_id, args.since_id, args.min_id, args.limit, args['any[]'] ?? [], args['all[]'] ?? [], args['none[]'] ?? [], args.only_media, args.local, args.remote, ctx);
|
||||
const tl = await NoteConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.get("/v1/timelines/home",
|
||||
auth(true, ['read:statuses']),
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query));
|
||||
const res = await TimelineHelpers.getHomeTimeline(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const tl = await NoteConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
});
|
||||
router.get<{ Params: { listId: string } }>(
|
||||
"/v1/timelines/list/:listId",
|
||||
auth(true, ['read:lists']),
|
||||
async (ctx, reply) => {
|
||||
const listId = convertId(ctx.params.listId, IdType.IceshrimpId);
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: listId });
|
||||
const list = await UserLists.findOneBy({ userId: ctx.user.id, id: ctx.params.listId });
|
||||
if (!list) throw new MastoApiError(404);
|
||||
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query));
|
||||
const res = await TimelineHelpers.getListTimeline(list, args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
const tl = await NoteConverter.encodeMany(res, ctx);
|
||||
|
||||
ctx.body = tl.map(s => convertStatusIds(s));
|
||||
ctx.body = await NoteConverter.encodeMany(res, ctx);
|
||||
},
|
||||
);
|
||||
router.get("/v1/conversations",
|
||||
auth(true, ['read:statuses']),
|
||||
async (ctx, reply) => {
|
||||
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));
|
||||
const res = await TimelineHelpers.getConversations(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
|
||||
ctx.body = res.map(c => convertConversationIds(c));
|
||||
const args = normalizeUrlQuery(limitToInt(ctx.query));
|
||||
ctx.body = await TimelineHelpers.getConversations(args.max_id, args.since_id, args.min_id, args.limit, ctx);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import { AnnouncementReads, Announcements, Emojis, Instances, Notes, UserProfile
|
|||
import { IsNull } from "typeorm";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
|
||||
import { convertAccountId } from "@/server/api/mastodon/converters.js";
|
||||
import { Announcement } from "@/models/entities/announcement.js";
|
||||
import { ILocalUser, User } from "@/models/entities/user.js";
|
||||
import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
|
||||
|
@ -35,8 +34,7 @@ export class MiscHelpers {
|
|||
},
|
||||
order: { id: "ASC" },
|
||||
})
|
||||
.then(p => p ? UserConverter.encode(p, ctx) : null)
|
||||
.then(p => p ? convertAccountId(p) : null);
|
||||
.then(p => p ? UserConverter.encode(p, ctx) : null);
|
||||
const meta = await fetchMeta(true);
|
||||
|
||||
const res = {
|
||||
|
|
|
@ -18,7 +18,6 @@ import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
|||
import { generatePaginationData } from "@/server/api/mastodon/middleware/pagination.js"
|
||||
import { addPinned, removePinned } from "@/services/i/pin.js";
|
||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js";
|
||||
import mfm from "mfm-js";
|
||||
|
@ -360,11 +359,10 @@ export class NoteHelpers {
|
|||
if (body.scheduled_at != null)
|
||||
result.scheduled_at = new Date(Date.parse(body.scheduled_at));
|
||||
if (body.in_reply_to_id)
|
||||
result.in_reply_to_id = convertId(body.in_reply_to_id, IdType.IceshrimpId);
|
||||
result.in_reply_to_id = body.in_reply_to_id;
|
||||
if (body.media_ids)
|
||||
result.media_ids = body.media_ids && body.media_ids.length > 0
|
||||
? toArray(body.media_ids)
|
||||
.map(p => convertId(p, IdType.IceshrimpId))
|
||||
: undefined;
|
||||
|
||||
if (body.poll) {
|
||||
|
@ -392,7 +390,6 @@ export class NoteHelpers {
|
|||
if (body.media_ids)
|
||||
result.media_ids = body.media_ids && body.media_ids.length > 0
|
||||
? toArray(body.media_ids)
|
||||
.map(p => convertId(p, IdType.IceshrimpId))
|
||||
: undefined;
|
||||
|
||||
if (body.poll) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { MastoContext } from "@/server/api/mastodon/index.js";
|
||||
import config from "@/config/index.js";
|
||||
import { convertId, IdType } from "@/misc/convert-id.js";
|
||||
|
||||
type PaginationData = {
|
||||
limit: number;
|
||||
|
@ -15,11 +14,11 @@ export async function PaginationMiddleware(ctx: MastoContext, next: () => Promis
|
|||
const link: string[] = [];
|
||||
const limit = ctx.pagination.limit;
|
||||
if (ctx.pagination.maxId) {
|
||||
const l = `<${config.url}/api${ctx.path}?limit=${limit}&max_id=${convertId(ctx.pagination.maxId, IdType.MastodonId)}>; rel="next"`;
|
||||
const l = `<${config.url}/api${ctx.path}?limit=${limit}&max_id=${ctx.pagination.maxId}>; rel="next"`;
|
||||
link.push(l);
|
||||
}
|
||||
if (ctx.pagination.minId) {
|
||||
const l = `<${config.url}/api${ctx.path}?limit=${limit}&min_id=${convertId(ctx.pagination.maxId, IdType.MastodonId)}>; rel="prev"`;
|
||||
const l = `<${config.url}/api${ctx.path}?limit=${limit}&min_id=${ctx.pagination.maxId}>; rel="prev"`;
|
||||
link.push(l);
|
||||
}
|
||||
if (link.length > 0) {
|
||||
|
|
Loading…
Reference in a new issue