[mastodon-client] Code cleanup & reformat

This commit is contained in:
Laura Hausmann 2023-10-06 03:12:52 +02:00
parent 8bc7bf373e
commit afd9e236a3
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
21 changed files with 168 additions and 157 deletions

View file

@ -38,7 +38,7 @@ export class NoteConverter {
host,
));
const reactionCount = NoteReactions.countBy({noteId: note.id});
const reactionCount = NoteReactions.countBy({ noteId: note.id });
const reaction = user ? NoteReactions.findOneBy({
userId: user.id,
@ -87,7 +87,7 @@ export class NoteConverter {
});
const isPinned = user && note.userId === user.id
? UserNotePinings.exist({where: {userId: user.id, noteId: note.id}})
? UserNotePinings.exist({ where: { userId: user.id, noteId: note.id } })
: undefined;
const tags = note.tags.map(tag => {

View file

@ -27,14 +27,14 @@ export class UserConverter {
acct = `${u.username}@${u.host}`;
acctUrl = `https://${u.host}/@${u.username}`;
}
const profile = UserProfiles.findOneBy({userId: u.id});
const profile = UserProfiles.findOneBy({ userId: u.id });
const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? "")) ?? escapeMFM(profile?.description ?? ""));
const avatar = u.avatarId
? (DriveFiles.findOneBy({id: u.avatarId}))
? (DriveFiles.findOneBy({ id: u.avatarId }))
.then(p => p?.url ?? Users.getIdenticonUrl(u.id))
: Users.getIdenticonUrl(u.id);
const banner = u.bannerId
? (DriveFiles.findOneBy({id: u.bannerId}))
? (DriveFiles.findOneBy({ id: u.bannerId }))
.then(p => p?.url ?? `${config.url}/static-assets/transparent.png`)
: `${config.url}/static-assets/transparent.png`;
const followersCount = profile.then(profile => {

View file

@ -11,7 +11,7 @@ export function setupEndpointsFilter(router: Router): void {
router.post(["/v1/filters", "/v2/filters"],
auth(true, ['write:filters']),
async (ctx) => {
ctx.status = 400;
ctx.status = 400;
ctx.body = { error: "Please change word mute settings in the web frontend settings." };
}
);

View file

@ -44,7 +44,7 @@ export function setupEndpointsList(router: Router): void {
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: id });
if (!list) throw new MastoApiError(404);
const body = ctx.request.body as any;
@ -58,7 +58,7 @@ export function setupEndpointsList(router: Router): void {
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: id });
if (!list) throw new MastoApiError(404);
await ListHelpers.deleteList(ctx.user, list);
@ -83,7 +83,7 @@ export function setupEndpointsList(router: Router): void {
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: id });
if (!list) throw new MastoApiError(404);
const body = ctx.request.body as any;
@ -100,7 +100,7 @@ export function setupEndpointsList(router: Router): void {
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: id });
if (!list) throw new MastoApiError(404);
const body = ctx.request.body as any;

View file

@ -34,7 +34,7 @@ export function setupEndpointsMisc(router: Router): void {
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: id });
if (!announcement) throw new MastoApiError(404);
await MiscHelpers.dismissAnnouncement(announcement, ctx.user);

View file

@ -1,6 +1,12 @@
import Router from "@koa/router";
import { convertId, IdType } from "../../index.js";
import { convertAccountId, convertPollId, convertStatusIds, convertStatusEditIds, convertStatusSourceId, } from "../converters.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";
@ -29,7 +35,7 @@ export function setupEndpointsStatus(router: Router): void {
.then(p => NoteConverter.encode(p, ctx.user))
.then(p => convertStatusIds(p));
if (key !== null) NoteHelpers.postIdempotencyCache.set(key, {status: ctx.body});
if (key !== null) NoteHelpers.postIdempotencyCache.set(key, { status: ctx.body });
}
);
router.put("/v1/statuses/:id",
@ -264,7 +270,7 @@ export function setupEndpointsStatus(router: Router): void {
const note = await NoteHelpers.getNoteOr404(id, ctx.user);
const data = await PollHelpers.getPoll(note, ctx.user);
ctx.body = convertPollId(data);
});
});
router.post<{ Params: { id: string } }>(
"/v1/polls/:id/votes",
auth(true, ["write:statuses"]),
@ -276,7 +282,7 @@ export function setupEndpointsStatus(router: Router): void {
const choices = toArray(body.choices ?? []).map(p => parseInt(p));
if (choices.length < 1) {
ctx.status = 400;
ctx.body = {error: 'Must vote for at least one option'};
ctx.body = { error: 'Must vote for at least one option' };
return;
}

View file

@ -74,7 +74,7 @@ export function setupEndpointsTimeline(router: Router): void {
.then(n => NoteConverter.encodeMany(n, ctx.user, cache));
ctx.body = tl.map(s => convertStatusIds(s));
});
});
router.get<{ Params: { hashtag: string } }>(
"/v1/timelines/tag/:hashtag",
auth(false, ['read:statuses']),
@ -97,13 +97,13 @@ export function setupEndpointsTimeline(router: Router): void {
.then(n => NoteConverter.encodeMany(n, ctx.user, cache));
ctx.body = tl.map(s => convertStatusIds(s));
});
});
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: listId });
if (!list) throw new MastoApiError(404);
const args = normalizeUrlQuery(convertPaginationArgsIds(limitToInt(ctx.query)));

View file

@ -10,7 +10,7 @@ import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"
export class ListHelpers {
public static async getLists(user: ILocalUser): Promise<MastodonEntity.List[]> {
return UserLists.findBy({userId: user.id}).then(p => p.map(list => {
return UserLists.findBy({ userId: user.id }).then(p => p.map(list => {
return {
id: list.id,
title: list.name
@ -19,7 +19,7 @@ export class ListHelpers {
}
public static async getList(user: ILocalUser, id: string): Promise<MastodonEntity.List> {
return UserLists.findOneByOrFail({userId: user.id, id: id}).then(list => {
return UserLists.findOneByOrFail({ userId: user.id, id: id }).then(list => {
return {
id: list.id,
title: list.name
@ -32,9 +32,10 @@ export class ListHelpers {
throw new MastoApiError(404);
})
}
public static async getListUsers(user: ILocalUser, id: string, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40): Promise<LinkPaginationObject<User[]>> {
if (limit > 80) limit = 80;
const list = await UserLists.findOneBy({userId: user.id, id: id});
const list = await UserLists.findOneBy({ userId: user.id, id: id });
if (!list) throw new MastoApiError(404);
const query = PaginationHelpers.makePaginationQuery(
UserListJoinings.createQueryBuilder('member'),
@ -42,7 +43,7 @@ export class ListHelpers {
maxId,
minId
)
.andWhere("member.userListId = :listId", {listId: list.id})
.andWhere("member.userListId = :listId", { listId: list.id })
.innerJoinAndSelect("member.user", "user");
return query.take(limit).getMany().then(async p => {
@ -125,9 +126,9 @@ export class ListHelpers {
if (title.length < 1) throw new MastoApiError(400, "Title must not be empty");
if (user.id != list.userId) throw new Error("List is not owned by user");
const partial = {name: title};
const partial = { name: title };
const result = await UserLists.update(list.id, partial)
.then(async _ => await UserLists.findOneByOrFail({id: list.id}));
.then(async _ => await UserLists.findOneByOrFail({ id: list.id }));
return {
id: result.id,
@ -140,9 +141,9 @@ export class ListHelpers {
.select("member.userListId")
.where("member.userId = :memberId");
const query = UserLists.createQueryBuilder('list')
.where("list.userId = :userId", {userId: user.id})
.where("list.userId = :userId", { userId: user.id })
.andWhere(`list.id IN (${joinQuery.getQuery()})`)
.setParameters({memberId: member.id});
.setParameters({ memberId: member.id });
return query.getMany()
.then(results => results.map(result => {

View file

@ -34,7 +34,7 @@ export class MediaHelpers {
comment: body?.description ?? undefined
});
return DriveFiles.findOneByOrFail({id: file.id, userId: user.id})
return DriveFiles.findOneByOrFail({ id: file.id, userId: user.id })
.then(p => DriveFiles.pack(p));
}
@ -51,13 +51,13 @@ export class MediaHelpers {
}
public static async getMedia(user: ILocalUser, id: string): Promise<DriveFile | null> {
return DriveFiles.findOneBy({id: id, userId: user.id});
return DriveFiles.findOneBy({ id: id, userId: user.id });
}
public static async getMediaOr404(user: ILocalUser, id: string): Promise<DriveFile> {
return this.getMedia(user, id).then(p => {
if (p) return p;
throw new MastoApiError(404);
if (p) return p;
throw new MastoApiError(404);
});
}
}

View file

@ -13,7 +13,7 @@ export class MfmHelpers {
return null;
}
const {window} = new JSDOM("");
const { window } = new JSDOM("");
const doc = window.document;
@ -122,7 +122,7 @@ export class MfmHelpers {
mention(node) {
const a = doc.createElement("a");
const {username, host, acct} = node.props;
const { username, host, acct } = node.props;
const remoteUserInfo = mentionedRemoteUsers.find(
(remoteUser) =>
remoteUser.username === username && remoteUser.host === host,

View file

@ -7,11 +7,10 @@ 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 } from "@/models/entities/user.js";
import { ILocalUser, User } from "@/models/entities/user.js";
import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js";
import { genId } from "@/misc/gen-id.js";
import * as Acct from "@/misc/acct.js";
import { User } from "@/models/entities/user.js";
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
import { generateMutedUserQueryForUsers } from "@/server/api/common/generate-muted-user-query.js";
import { generateBlockQueryForUsers } from "@/server/api/common/generate-block-query.js";
@ -23,8 +22,8 @@ import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility
export class MiscHelpers {
public static async getInstance(): Promise<MastodonEntity.Instance> {
const userCount = Users.count({where: {host: IsNull()}});
const noteCount = Notes.count({where: {userHost: IsNull()}});
const userCount = Users.count({ where: { host: IsNull() } });
const noteCount = Notes.count({ where: { userHost: IsNull() } });
const instanceCount = Instances.count({ cache: 3600000 });
const contact = await Users.findOne({
where: {
@ -33,7 +32,7 @@ export class MiscHelpers {
isDeleted: false,
isSuspended: false,
},
order: {id: "ASC"},
order: { id: "ASC" },
})
.then(p => p ? UserConverter.encode(p) : null)
.then(p => p ? convertAccountId(p) : null);
@ -100,9 +99,9 @@ export class MiscHelpers {
if (includeRead) {
const [announcements, reads] = await Promise.all([
Announcements.createQueryBuilder("announcement")
.orderBy({"announcement.id": "DESC"})
.orderBy({ "announcement.id": "DESC" })
.getMany(),
AnnouncementReads.findBy({userId: user.id})
AnnouncementReads.findBy({ userId: user.id })
.then(p => p.map(x => x.announcementId))
]);
@ -115,7 +114,7 @@ export class MiscHelpers {
const query = Announcements.createQueryBuilder("announcement")
.where(`announcement.id NOT IN (${sq.getQuery()})`)
.orderBy({"announcement.id": "DESC"})
.orderBy({ "announcement.id": "DESC" })
.setParameter("userId", user.id);
return query.getMany()
@ -123,7 +122,7 @@ export class MiscHelpers {
}
public static async dismissAnnouncement(announcement: Announcement, user: ILocalUser): Promise<void> {
const exists = await AnnouncementReads.exist({where: {userId: user.id, announcementId: announcement.id}});
const exists = await AnnouncementReads.exist({ where: { userId: user.id, announcementId: announcement.id } });
if (!exists) {
await AnnouncementReads.insert({
id: genId(),
@ -139,19 +138,19 @@ export class MiscHelpers {
const results: Promise<MastodonEntity.SuggestedAccount[]>[] = [];
const pinned = fetchMeta().then(meta => Promise.all(
meta.pinnedUsers
.map((acct) => Acct.parse(acct))
.map((acct) =>
Users.findOneBy({
usernameLower: acct.username.toLowerCase(),
host: acct.host ?? IsNull(),
}))
meta.pinnedUsers
.map((acct) => Acct.parse(acct))
.map((acct) =>
Users.findOneBy({
usernameLower: acct.username.toLowerCase(),
host: acct.host ?? IsNull(),
}))
)
.then(p => p.filter(x => !!x) as User[])
.then(p => UserConverter.encodeMany(p, cache))
.then(p => p.map(x => {
return {source: "staff", account: x} as MastodonEntity.SuggestedAccount
}))
.then(p => p.filter(x => !!x) as User[])
.then(p => UserConverter.encodeMany(p, cache))
.then(p => p.map(x => {
return { source: "staff", account: x } as MastodonEntity.SuggestedAccount
}))
);
const query = Users.createQueryBuilder("user")
@ -170,7 +169,7 @@ export class MiscHelpers {
.getMany()
.then(p => UserConverter.encodeMany(p, cache))
.then(p => p.map(x => {
return {source: "global", account: x} as MastodonEntity.SuggestedAccount
return { source: "global", account: x } as MastodonEntity.SuggestedAccount
}));
results.push(pinned);
@ -182,17 +181,18 @@ export class MiscHelpers {
public static async getCustomEmoji() {
return Emojis.find({
where: {
host: IsNull(),
},
order: {
category: "ASC",
name: "ASC",
},
cache: {
id: "meta_emojis",
milliseconds: 3600000, // 1 hour
}}
where: {
host: IsNull(),
},
order: {
category: "ASC",
name: "ASC",
},
cache: {
id: "meta_emojis",
milliseconds: 3600000, // 1 hour
}
}
)
.then(dbRes => populateEmojis(dbRes.map(p => p.name), null)
.then(p => p.map(x => EmojiConverter.encode(x))
@ -230,7 +230,7 @@ export class MiscHelpers {
}
public static getPreferences(user: ILocalUser): Promise<MastodonEntity.Preferences> {
const profile = UserProfiles.findOneByOrFail({userId: user.id});
const profile = UserProfiles.findOneByOrFail({ userId: user.id });
const sensitive = profile.then(p => p.alwaysMarkNsfw);
const language = profile.then(p => p.lang);
const privacy = UserHelpers.getDefaultNoteVisibility(user)

View file

@ -147,7 +147,7 @@ export class NoteHelpers {
maxId,
minId
)
.andWhere("reaction.noteId = :noteId", {noteId: note.id})
.andWhere("reaction.noteId = :noteId", { noteId: note.id })
.innerJoinAndSelect("reaction.user", "user");
return query.take(limit).getMany().then(async p => {
@ -168,7 +168,7 @@ export class NoteHelpers {
const cache = UserHelpers.getFreshAccountCache();
const account = Promise.resolve(note.user ?? await UserHelpers.getUserCached(note.userId, cache))
.then(p => UserConverter.encode(p, cache));
const edits = await NoteEdits.find({where: {noteId: note.id}, order: {id: "ASC"}});
const edits = await NoteEdits.find({ where: { noteId: note.id }, order: { id: "ASC" } });
const history: Promise<MastodonEntity.StatusEdit>[] = [];
const curr = {
@ -219,7 +219,7 @@ export class NoteHelpers {
maxId,
minId
)
.andWhere("note.renoteId = :noteId", {noteId: note.id})
.andWhere("note.renoteId = :noteId", { noteId: note.id })
.andWhere("note.text IS NULL") // We don't want to count quotes as renotes
.innerJoinAndSelect("note.user", "user");
@ -244,7 +244,7 @@ export class NoteHelpers {
const query = makePaginationQuery(Notes.createQueryBuilder("note"))
.andWhere(
"note.id IN (SELECT id FROM note_replies(:noteId, :depth, :limit))",
{noteId, depth, limit},
{ noteId, depth, limit },
);
generateVisibilityQuery(query, user);

View file

@ -2,7 +2,7 @@ import { ILocalUser } from "@/models/entities/user.js";
import { Notes, Notifications } from "@/models/index.js";
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
import { Notification } from "@/models/entities/notification.js";
import {MastoApiError} from "@/server/api/mastodon/middleware/catch-errors.js";
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
export class NotificationHelpers {
public static async getNotifications(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40, types: string[] | undefined, excludeTypes: string[] | undefined, accountId: string | undefined): Promise<Notification[]> {
@ -24,11 +24,11 @@ export class NotificationHelpers {
maxId,
minId
)
.andWhere("notification.notifieeId = :userId", {userId: user.id})
.andWhere("notification.type IN (:...types)", {types: requestedTypes});
.andWhere("notification.notifieeId = :userId", { userId: user.id })
.andWhere("notification.type IN (:...types)", { types: requestedTypes });
if (accountId !== undefined)
query.andWhere("notification.notifierId = :notifierId", {notifierId: accountId});
query.andWhere("notification.notifierId = :notifierId", { notifierId: accountId });
query.leftJoinAndSelect("notification.note", "note");
@ -36,22 +36,22 @@ export class NotificationHelpers {
}
public static async getNotification(id: string, user: ILocalUser): Promise<Notification | null> {
return Notifications.findOneBy({id: id, notifieeId: user.id});
return Notifications.findOneBy({ id: id, notifieeId: user.id });
}
public static async getNotificationOr404(id: string, user: ILocalUser): Promise<Notification> {
return this.getNotification(id, user).then(p => {
if (p) return p;
throw new MastoApiError(404);
if (p) return p;
throw new MastoApiError(404);
});
}
public static async dismissNotification(id: string, user: ILocalUser): Promise<void> {
const result = await Notifications.update({id: id, notifieeId: user.id}, {isRead: true});
const result = await Notifications.update({ id: id, notifieeId: user.id }, { isRead: true });
}
public static async clearAllNotifications(user: ILocalUser): Promise<void> {
await Notifications.update({notifieeId: user.id}, {isRead: true});
await Notifications.update({ notifieeId: user.id }, { isRead: true });
}
public static async markConversationAsRead(id: string, user: ILocalUser): Promise<void> {
@ -68,7 +68,7 @@ export class NotificationHelpers {
.setParameter("conversationId", id)
.setParameter("types", ['reply', 'mention'])
.update()
.set({isRead: true})
.set({ isRead: true })
.execute();
}

View file

@ -13,22 +13,22 @@ export class PaginationHelpers {
if (sinceId && minId) throw new Error("Can't user both sinceId and minId params");
if (sinceId && maxId) {
q.andWhere(`${idField} > :sinceId`, {sinceId: sinceId});
q.andWhere(`${idField} < :maxId`, {maxId: maxId});
q.andWhere(`${idField} > :sinceId`, { sinceId: sinceId });
q.andWhere(`${idField} < :maxId`, { maxId: maxId });
q.orderBy(`${idField}`, "DESC");
}
if (minId && maxId) {
q.andWhere(`${idField} > :minId`, {minId: minId});
q.andWhere(`${idField} < :maxId`, {maxId: maxId});
q.andWhere(`${idField} > :minId`, { minId: minId });
q.andWhere(`${idField} < :maxId`, { maxId: maxId });
q.orderBy(`${idField}`, "ASC");
} else if (sinceId) {
q.andWhere(`${idField} > :sinceId`, {sinceId: sinceId});
q.andWhere(`${idField} > :sinceId`, { sinceId: sinceId });
q.orderBy(`${idField}`, "DESC");
} else if (minId) {
q.andWhere(`${idField} > :minId`, {minId: minId});
q.andWhere(`${idField} > :minId`, { minId: minId });
q.orderBy(`${idField}`, "ASC");
} else if (maxId) {
q.andWhere(`${idField} < :maxId`, {maxId: maxId});
q.andWhere(`${idField} < :maxId`, { maxId: maxId });
q.orderBy(`${idField}`, "DESC");
} else {
q.orderBy(`${idField}`, "DESC");

View file

@ -10,7 +10,7 @@ import { deliver } from "@/queue/index.js";
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
import renderVote from "@/remote/activitypub/renderer/vote.js";
import { Not } from "typeorm";
import {MastoApiError} from "@/server/api/mastodon/middleware/catch-errors.js";
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
export class PollHelpers {
public static async getPoll(note: Note, user: ILocalUser | null): Promise<MastodonEntity.Poll> {
@ -34,7 +34,7 @@ export class PollHelpers {
if (block) throw new Error('You are blocked by the poll author');
}
const poll = await Polls.findOneByOrFail({noteId: note.id});
const poll = await Polls.findOneByOrFail({ noteId: note.id });
if (poll.expiresAt && poll.expiresAt < createdAt) throw new Error('Poll is expired');

View file

@ -63,7 +63,10 @@ export class SearchHelpers {
if (!match) match = q.match(/^@(?<user>[a-zA-Z0-9_]+)$/)
if (match) {
// check if user is already in database
const dbResult = await Users.findOneBy({usernameLower: match.groups!.user.toLowerCase(), host: match.groups?.host ?? IsNull()});
const dbResult = await Users.findOneBy({
usernameLower: match.groups!.user.toLowerCase(),
host: match.groups?.host ?? IsNull()
});
if (dbResult) return [dbResult];
const result = await resolveUser(match.groups!.user.toLowerCase(), match.groups?.host ?? null);
@ -89,23 +92,23 @@ export class SearchHelpers {
if (following) {
const followingQuery = Followings.createQueryBuilder("following")
.select("following.followeeId")
.where("following.followerId = :followerId", {followerId: user.id});
.where("following.followerId = :followerId", { followerId: user.id });
query.andWhere(
new Brackets((qb) => {
qb.where(`user.id IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, {meId: user.id});
qb.where(`user.id IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, { meId: user.id });
}),
);
}
query.andWhere(
new Brackets((qb) => {
qb.where("user.name ILIKE :q", {q: `%${sqlLikeEscape(q)}%`});
qb.orWhere("user.usernameLower ILIKE :q", {q: `%${sqlLikeEscape(q)}%`});
qb.where("user.name ILIKE :q", { q: `%${sqlLikeEscape(q)}%` });
qb.orWhere("user.usernameLower ILIKE :q", { q: `%${sqlLikeEscape(q)}%` });
})
);
query.orderBy({'user.notesCount': 'DESC'});
query.orderBy({ 'user.notesCount': 'DESC' });
return query.skip(offset ?? 0).take(limit).getMany().then(p => minId ? p.reverse() : p);
}
@ -184,8 +187,8 @@ export class SearchHelpers {
const chunk = ids.slice(start, start + chunkSize);
const query = Notes.createQueryBuilder("note")
.where({id: In(chunk)})
.orderBy({id: "DESC"})
.where({ id: In(chunk) })
.orderBy({ id: "DESC" })
generateVisibilityQuery(query, user);
@ -197,11 +200,11 @@ export class SearchHelpers {
if (following) {
const followingQuery = Followings.createQueryBuilder("following")
.select("following.followeeId")
.where("following.followerId = :followerId", {followerId: user.id});
.where("following.followerId = :followerId", { followerId: user.id });
query.andWhere(
new Brackets((qb) => {
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, {meId: user.id});
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, { meId: user.id });
}),
)
}
@ -267,8 +270,8 @@ export class SearchHelpers {
const chunk = ids.slice(start, start + chunkSize);
const query = Notes.createQueryBuilder("note")
.where({id: In(chunk)})
.orderBy({id: "DESC"})
.where({ id: In(chunk) })
.orderBy({ id: "DESC" })
generateVisibilityQuery(query, user);
@ -356,23 +359,23 @@ export class SearchHelpers {
);
if (accountId) {
query.andWhere("note.userId = :userId", {userId: accountId});
query.andWhere("note.userId = :userId", { userId: accountId });
}
if (following) {
const followingQuery = Followings.createQueryBuilder("following")
.select("following.followeeId")
.where("following.followerId = :followerId", {followerId: user.id});
.where("following.followerId = :followerId", { followerId: user.id });
query.andWhere(
new Brackets((qb) => {
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, {meId: user.id});
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, { meId: user.id });
}),
)
}
query
.andWhere("note.text ILIKE :q", {q: `%${sqlLikeEscape(q)}%`})
.andWhere("note.text ILIKE :q", { q: `%${sqlLikeEscape(q)}%` })
.leftJoinAndSelect("note.renote", "renote");
@ -390,8 +393,8 @@ export class SearchHelpers {
const tags = Hashtags.createQueryBuilder('tag')
.select('tag.name')
.distinctOn(['tag.name'])
.where("tag.name ILIKE :q", {q: `%${sqlLikeEscape(q)}%`})
.orderBy({'tag.name': 'ASC'})
.where("tag.name ILIKE :q", { q: `%${sqlLikeEscape(q)}%` })
.orderBy({ 'tag.name': 'ASC' })
.skip(offset ?? 0).take(limit).getMany();
return tags.then(p => p.map(tag => {

View file

@ -17,7 +17,7 @@ import { UserConverter } from "@/server/api/mastodon/converters/user.js";
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
import { awaitAll } from "@/prelude/await-all.js";
import { unique } from "@/prelude/array.js";
import {MastoApiError} from "@/server/api/mastodon/middleware/catch-errors.js";
import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js";
export class TimelineHelpers {
public static async getHomeTimeline(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20): Promise<Note[]> {
@ -25,7 +25,7 @@ export class TimelineHelpers {
const followingQuery = Followings.createQueryBuilder("following")
.select("following.followeeId")
.where("following.followerId = :followerId", {followerId: user.id});
.where("following.followerId = :followerId", { followerId: user.id });
const query = PaginationHelpers.makePaginationQuery(
Notes.createQueryBuilder("note"),
@ -35,7 +35,7 @@ export class TimelineHelpers {
)
.andWhere(
new Brackets((qb) => {
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, {meId: user.id});
qb.where(`note.userId IN (${followingQuery.getQuery()} UNION ALL VALUES (:meId))`, { meId: user.id });
}),
)
.leftJoinAndSelect("note.renote", "renote");
@ -114,7 +114,7 @@ export class TimelineHelpers {
.andWhere(`note.userId IN (${listQuery.getQuery()})`)
.andWhere("note.visibility != 'specified'")
.leftJoinAndSelect("note.renote", "renote")
.setParameters({listId: list.id});
.setParameters({ listId: list.id });
generateVisibilityQuery(query, user);
@ -137,11 +137,11 @@ export class TimelineHelpers {
minId
)
.andWhere("note.visibility = 'public'")
.andWhere("note.tags @> array[:tag]::varchar[]", {tag: tag});
.andWhere("note.tags @> array[:tag]::varchar[]", { tag: tag });
if (any.length > 0) query.andWhere("note.tags && array[:...any]::varchar[]", {any: any});
if (all.length > 0) query.andWhere("note.tags @> array[:...all]::varchar[]", {all: all});
if (none.length > 0) query.andWhere("NOT(note.tags @> array[:...none]::varchar[])", {none: none});
if (any.length > 0) query.andWhere("note.tags && array[:...any]::varchar[]", { any: any });
if (all.length > 0) query.andWhere("note.tags @> array[:...all]::varchar[]", { all: all });
if (none.length > 0) query.andWhere("NOT(note.tags @> array[:...none]::varchar[])", { none: none });
if (remote) query.andWhere("note.userHost IS NOT NULL");
if (local) query.andWhere("note.userHost IS NULL");
@ -168,7 +168,7 @@ export class TimelineHelpers {
.select("COALESCE(note.threadId, note.id)", "conversationId")
.addSelect("note.id", "latest")
.distinctOn(["COALESCE(note.threadId, note.id)"])
.orderBy({"COALESCE(note.threadId, note.id)": minId ? "ASC" : "DESC", "note.id": "DESC"})
.orderBy({ "COALESCE(note.threadId, note.id)": minId ? "ASC" : "DESC", "note.id": "DESC" })
.andWhere("note.visibility = 'specified'")
.andWhere(
new Brackets(qb => {
@ -183,7 +183,7 @@ export class TimelineHelpers {
minId
)
.innerJoin(`(${sq.getQuery()})`, "sq", "note.id = sq.latest")
.setParameters({userId: user.id})
.setParameters({ userId: user.id })
return query.take(limit).getMany().then(p => {
if (minId !== undefined) p = p.reverse();

View file

@ -67,8 +67,8 @@ type RelationshipType = 'followers' | 'following';
export class UserHelpers {
public static async followUser(target: User, localUser: ILocalUser, reblogs: boolean, notify: boolean): Promise<MastodonEntity.Relationship> {
//FIXME: implement reblogs & notify params
const following = await Followings.exist({where: {followerId: localUser.id, followeeId: target.id}});
const requested = await FollowRequests.exist({where: {followerId: localUser.id, followeeId: target.id}});
const following = await Followings.exist({ where: { followerId: localUser.id, followeeId: target.id } });
const requested = await FollowRequests.exist({ where: { followerId: localUser.id, followeeId: target.id } });
if (!following && !requested)
await createFollowing(localUser, target);
@ -76,8 +76,8 @@ export class UserHelpers {
}
public static async unfollowUser(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const following = await Followings.exist({where: {followerId: localUser.id, followeeId: target.id}});
const requested = await FollowRequests.exist({where: {followerId: localUser.id, followeeId: target.id}});
const following = await Followings.exist({ where: { followerId: localUser.id, followeeId: target.id } });
const requested = await FollowRequests.exist({ where: { followerId: localUser.id, followeeId: target.id } });
if (following)
await deleteFollowing(localUser, target);
if (requested)
@ -87,7 +87,7 @@ export class UserHelpers {
}
public static async blockUser(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const blocked = await Blockings.exist({where: {blockerId: localUser.id, blockeeId: target.id}});
const blocked = await Blockings.exist({ where: { blockerId: localUser.id, blockeeId: target.id } });
if (!blocked)
await createBlocking(localUser, target);
@ -95,7 +95,7 @@ export class UserHelpers {
}
public static async unblockUser(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const blocked = await Blockings.exist({where: {blockerId: localUser.id, blockeeId: target.id}});
const blocked = await Blockings.exist({ where: { blockerId: localUser.id, blockeeId: target.id } });
if (blocked)
await deleteBlocking(localUser, target);
@ -104,7 +104,7 @@ export class UserHelpers {
public static async muteUser(target: User, localUser: ILocalUser, notifications: boolean = true, duration: number = 0): Promise<MastodonEntity.Relationship> {
//FIXME: respect notifications parameter
const muted = await Mutings.exist({where: {muterId: localUser.id, muteeId: target.id}});
const muted = await Mutings.exist({ where: { muterId: localUser.id, muteeId: target.id } });
if (!muted) {
await Mutings.insert({
id: genId(),
@ -126,7 +126,7 @@ export class UserHelpers {
}
public static async unmuteUser(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const muting = await Mutings.findOneBy({muterId: localUser.id, muteeId: target.id});
const muting = await Mutings.findOneBy({ muterId: localUser.id, muteeId: target.id });
if (muting) {
await Mutings.delete({
id: muting.id,
@ -139,14 +139,14 @@ export class UserHelpers {
}
public static async acceptFollowRequest(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const pending = await FollowRequests.exist({where: {followerId: target.id, followeeId: localUser.id}});
const pending = await FollowRequests.exist({ where: { followerId: target.id, followeeId: localUser.id } });
if (pending)
await acceptFollowRequest(localUser, target);
return this.getUserRelationshipTo(target.id, localUser.id);
}
public static async rejectFollowRequest(target: User, localUser: ILocalUser): Promise<MastodonEntity.Relationship> {
const pending = await FollowRequests.exist({where: {followerId: target.id, followeeId: localUser.id}});
const pending = await FollowRequests.exist({ where: { followerId: target.id, followeeId: localUser.id } });
if (pending)
await rejectFollowRequest(localUser, target);
return this.getUserRelationshipTo(target.id, localUser.id);
@ -193,7 +193,7 @@ export class UserHelpers {
public static async verifyCredentials(user: ILocalUser): Promise<MastodonEntity.Account> {
const acct = UserConverter.encode(user);
const profile = UserProfiles.findOneByOrFail({userId: user.id});
const profile = UserProfiles.findOneByOrFail({ userId: user.id });
const privacy = this.getDefaultNoteVisibility(user);
const fields = profile.then(profile => profile.fields.map(field => {
return {
@ -222,7 +222,7 @@ export class UserHelpers {
public static async getUserFromAcct(acct: string): Promise<User> {
const split = acct.toLowerCase().split('@');
if (split.length > 2) throw new Error('Invalid acct');
return Users.findOneBy({usernameLower: split[0], host: split[1] ?? IsNull()})
return Users.findOneBy({ usernameLower: split[0], host: split[1] ?? IsNull() })
.then(p => {
if (p) return p;
throw new MastoApiError(404);
@ -239,7 +239,7 @@ export class UserHelpers {
minId
);
query.andWhere("muting.muterId = :userId", {userId: user.id})
query.andWhere("muting.muterId = :userId", { userId: user.id })
.innerJoinAndSelect("muting.mutee", "mutee");
return query.take(limit).getMany().then(async p => {
@ -275,7 +275,7 @@ export class UserHelpers {
minId
);
query.andWhere("blocking.blockerId = :userId", {userId: user.id})
query.andWhere("blocking.blockerId = :userId", { userId: user.id })
.innerJoinAndSelect("blocking.blockee", "blockee");
return query.take(limit).getMany().then(p => {
@ -302,7 +302,7 @@ export class UserHelpers {
minId
);
query.andWhere("request.followeeId = :userId", {userId: user.id})
query.andWhere("request.followeeId = :userId", { userId: user.id })
.innerJoinAndSelect("request.follower", "follower");
return query.take(limit).getMany().then(p => {
@ -356,7 +356,7 @@ export class UserHelpers {
new Brackets(qb => {
qb.where("note.replyId IS NULL")
.orWhere(new Brackets(qb => {
qb.where('note.mentions = :mentions', {mentions: []})
qb.where('note.mentions = :mentions', { mentions: [] })
.andWhere('thread.userId = :userId')
}));
}));
@ -375,7 +375,7 @@ export class UserHelpers {
query.andWhere("note.visibility != 'hidden'");
query.andWhere("note.visibility != 'specified'");
query.setParameters({userId: user.id});
query.setParameters({ userId: user.id });
return PaginationHelpers.execQuery(query, limit, minId !== undefined);
}
@ -389,7 +389,7 @@ export class UserHelpers {
maxId,
minId
)
.andWhere("favorite.userId = :meId", {meId: localUser.id})
.andWhere("favorite.userId = :meId", { meId: localUser.id })
.leftJoinAndSelect("favorite.note", "note");
generateVisibilityQuery(query, localUser);
@ -413,7 +413,7 @@ export class UserHelpers {
maxId,
minId
)
.andWhere("reaction.userId = :meId", {meId: localUser.id})
.andWhere("reaction.userId = :meId", { meId: localUser.id })
.leftJoinAndSelect("reaction.note", "note");
generateVisibilityQuery(query, localUser);
@ -431,11 +431,11 @@ export class UserHelpers {
private static async getUserRelationships(type: RelationshipType, user: User, localUser: ILocalUser | null, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40): Promise<LinkPaginationObject<User[]>> {
if (limit > 80) limit = 80;
const profile = await UserProfiles.findOneByOrFail({userId: user.id});
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
if (profile.ffVisibility === "private") {
if (!localUser || user.id !== localUser.id) return {data: []};
if (!localUser || user.id !== localUser.id) return { data: [] };
} else if (profile.ffVisibility === "followers") {
if (!localUser) return {data: []};
if (!localUser) return { data: [] };
if (user.id !== localUser.id) {
const isFollowed = await Followings.exist({
where: {
@ -443,7 +443,7 @@ export class UserHelpers {
followerId: localUser.id,
},
});
if (!isFollowed) return {data: []};
if (!isFollowed) return { data: [] };
}
}
@ -455,10 +455,10 @@ export class UserHelpers {
);
if (type === "followers") {
query.andWhere("following.followeeId = :userId", {userId: user.id})
query.andWhere("following.followeeId = :userId", { userId: user.id })
.innerJoinAndSelect("following.follower", "follower");
} else {
query.andWhere("following.followerId = :userId", {userId: user.id})
query.andWhere("following.followerId = :userId", { userId: user.id })
.innerJoinAndSelect("following.followee", "followee");
}
@ -538,6 +538,11 @@ export class UserHelpers {
}
public static async getDefaultNoteVisibility(user: ILocalUser): Promise<IceshrimpVisibility> {
return RegistryItems.findOneBy({domain: IsNull(), userId: user.id, key: 'defaultNoteVisibility', scope: '{client,base}'}).then(p => p?.value ?? 'public')
return RegistryItems.findOneBy({
domain: IsNull(),
userId: user.id,
key: 'defaultNoteVisibility',
scope: '{client,base}'
}).then(p => p?.value ?? 'public')
}
}

View file

@ -19,12 +19,11 @@ export function auth(required: boolean, scopes: string[] = []) {
return;
}
if (!AuthConverter.decode(scopes).every(p => ctx.scopes.includes(p))) {
if (!AuthConverter.decode(scopes).every(p => ctx.scopes.includes(p))) {
if (required) {
ctx.status = 403;
ctx.body = {error: "This action is outside the authorized scopes"};
}
else {
ctx.body = { error: "This action is outside the authorized scopes" };
} else {
ctx.user = null;
ctx.scopes = [];
}

View file

@ -1,9 +1,10 @@
import { MastoContext, logger } from "@/server/api/mastodon/index.js";
import { logger, MastoContext } from "@/server/api/mastodon/index.js";
import { IdentifiableError } from "@/misc/identifiable-error.js";
import { ApiError } from "@/server/api/error.js";
export class MastoApiError extends Error {
statusCode: number;
constructor(statusCode: number, message?: string) {
if (message == null) {
switch (statusCode) {
@ -26,20 +27,16 @@ export async function CatchErrorsMiddleware(ctx: MastoContext, next: () => Promi
} catch (e: any) {
if (e instanceof MastoApiError) {
ctx.status = e.statusCode;
}
else if (e instanceof IdentifiableError) {
} else if (e instanceof IdentifiableError) {
ctx.status = 400;
}
else if (e instanceof ApiError) {
} else if (e instanceof ApiError) {
ctx.status = e.httpStatusCode ?? 500;
}
else {
} else {
logger.error(`Error occured in ${ctx.method} ${ctx.path}:`);
if (e instanceof Error) {
if (e.stack) logger.error(e.stack);
else logger.error(`${e.name}: ${e.message}`);
}
else {
} else {
logger.error(e);
}
ctx.status = 500;

View file

@ -5,7 +5,7 @@ export async function NormalizeQueryMiddleware(ctx: MastoContext, next: () => Pr
if (!ctx.request.body || Object.keys(ctx.request.body).length === 0) {
ctx.request.body = ctx.request.query;
} else {
ctx.request.body = {...ctx.request.body, ...ctx.request.query};
ctx.request.body = { ...ctx.request.body, ...ctx.request.query };
}
}
await next();