remove queries to scheduled_note table

This commit is contained in:
naskya 2024-06-07 08:39:57 +09:00
parent 16f26bc6d7
commit 2c54bc3364
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
4 changed files with 88 additions and 64 deletions

View file

@ -3,7 +3,10 @@ use crate::{
database::db_conn,
misc::get_note_summary::{get_note_summary, PartialNoteToSummarize},
model::entity::{access_token, app, sw_subscription},
util::{http_client, id::{get_timestamp, InvalidIdError}},
util::{
http_client,
id::{get_timestamp, InvalidIdError},
},
};
use once_cell::sync::OnceCell;
use sea_orm::prelude::*;
@ -255,8 +258,8 @@ pub async fn send_push_notification(
// TODO: refactoring
let mut payload = if use_mastodon_api {
// Content generated per subscription
"".to_string()
// Content generated per subscription
"".to_string()
} else {
// Format the `content` passed from the TypeScript backend
// for Firefish push notifications

View file

@ -40,7 +40,6 @@ import type { NoteReaction } from "@/models/entities/note-reaction.js";
import { Cache } from "@/misc/cache.js";
import { isFiltered } from "@/misc/is-filtered.js";
import { unfurl } from "unfurl.js";
import type { ScheduledNote } from "@/models/entities/scheduled-note.js";
export class NoteConverter {
private static noteContentHtmlCache = new Cache<string | null>(
@ -162,9 +161,7 @@ export class NoteConverter {
return renote.url ?? renote.uri ?? `${config.url}/notes/${renote.id}`;
});
const identifier = `${note.id}:${(
note.updatedAt ?? note.createdAt
).getTime()}`;
const identifier = `${note.id}:${(note.updatedAt ?? note.createdAt).getTime()}`;
const text = quoteUri.then((quoteUri) =>
note.text !== null
@ -579,13 +576,12 @@ export class NoteConverter {
/** Encode a schduled note. */
public static async encodeScheduledNote(
scheduledNote: ScheduledNote,
ctx: MastoContext,
note: Note,
_: MastoContext,
): Promise<MastodonEntity.ScheduledStatus> {
const { note, user } = scheduledNote;
const renote =
note.renote ?? (note.renoteId ? getNote(note.renoteId, user) : null);
note.renote ??
(note.renoteId ? getNote(note.renoteId, { id: note.userId }) : null);
const quoteUri = Promise.resolve(renote).then((renote) => {
if (!renote || !isQuote(note)) return null;
return renote.url ?? renote.uri ?? `${config.url}/notes/${renote.id}`;
@ -604,12 +600,12 @@ export class NoteConverter {
const files = DriveFiles.packMany(note.fileIds);
const a = await awaitAll({
id: scheduledNote.noteId,
scheduled_at: scheduledNote.scheduledAt.toISOString(),
id: note.id,
scheduled_at: note.scheduledAt!.toISOString(),
params: {
text,
poll: note.hasPoll
? populatePoll(note, user?.id ?? null).then((p) =>
? populatePoll(note, note.userId ?? null).then((p) =>
PollConverter.encodeScheduledPoll(p),
)
: null,
@ -622,7 +618,7 @@ export class NoteConverter {
in_reply_to_id: note.replyId,
language: note.lang,
application_id: 0,
idempotency: scheduledNote.id,
idempotency: note.id,
with_rate_limit: false,
},
media_attachments: files.then((files) =>
@ -634,7 +630,7 @@ export class NoteConverter {
/** Encode an array of schduled notes. */
public static async encodeManyScheduledNotes(
scheduledNotes: ScheduledNote[],
scheduledNotes: Note[],
ctx: MastoContext,
): Promise<MastodonEntity.ScheduledStatus[]> {
const encoded = scheduledNotes.map((n) => this.encodeScheduledNote(n, ctx));

View file

@ -6,7 +6,6 @@ import {
NoteFavorites,
NoteReactions,
Notes,
ScheduledNotes,
UserNotePinings,
} from "@/models/index.js";
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
@ -20,7 +19,7 @@ import deleteReaction from "@/services/note/reaction/delete.js";
import createNote, { extractMentionedUsers } from "@/services/note/create.js";
import editNote from "@/services/note/edit.js";
import deleteNote from "@/services/note/delete.js";
import { genId } from "backend-rs";
import { fetchMeta, genIdAt } from "backend-rs";
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
import { UserConverter } from "@/server/api/mastodon/converters/user.js";
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
@ -36,15 +35,13 @@ import { MastoApiError } from "@/server/api/mastodon/middleware/catch-errors.js"
import { Cache } from "@/misc/cache.js";
import AsyncLock from "async-lock";
import { IdentifiableError } from "@/misc/identifiable-error.js";
import { IsNull } from "typeorm";
import { In, IsNull } from "typeorm";
import {
getStubMastoContext,
type MastoContext,
} from "@/server/api/mastodon/index.js";
import { fetchMeta } from "backend-rs";
import { translate } from "@/misc/translate.js";
import { createScheduledNoteJob } from "@/queue/index.js";
import type { ScheduledNote } from "@/models/entities/scheduled-note.js";
export class NoteHelpers {
public static postIdempotencyCache = new Cache<{
@ -134,9 +131,11 @@ export class NoteHelpers {
});
if (!bookmarked) {
const now = new Date();
await NoteFavorites.insert({
id: genId(),
createdAt: new Date(),
id: genIdAt(now),
createdAt: now,
noteId: note.id,
userId: user.id,
});
@ -385,7 +384,7 @@ export class NoteHelpers {
const user = ctx.user as ILocalUser;
const files =
request.media_ids && request.media_ids.length > 0
? DriveFiles.findByIds(request.media_ids)
? DriveFiles.findBy({ id: In(request.media_ids) })
: [];
const reply = request.in_reply_to_id
@ -403,8 +402,10 @@ export class NoteHelpers {
const visibility =
request.visibility ?? UserHelpers.getDefaultNoteVisibility(ctx);
const data = {
createdAt: new Date(),
const now = new Date();
const data = await awaitAll({
createdAt: now,
files: files,
poll: request.poll
? {
@ -432,39 +433,71 @@ export class NoteHelpers {
? this.extractMentions(request.text ?? "", ctx)
: undefined,
),
};
});
return createNote(
user,
await awaitAll(data),
{
createdAt: now,
scheduledAt: delay != null ? new Date(data.scheduledAt!) : null,
files: data.files,
poll:
data.poll != null
? {
choices: data.poll.choices,
multiple: data.poll.multiple,
expiresAt:
data.poll.expiresAt != null
? new Date(data.poll.expiresAt)
: null,
}
: undefined,
text: data.text || undefined,
lang: data.lang,
reply,
renote,
cw: data.cw,
...(delay != null
? {
visibility: "specified",
visibleUsers: [],
}
: {
visibility: data.visibility,
visibleUsers: data.visibleUsers,
}),
},
false,
!delay
? undefined
: async (note) => {
await ScheduledNotes.insert({
id: genId(),
noteId: note.id,
userId: user.id,
scheduledAt: request.scheduled_at,
});
delay
? async (note) => {
createScheduledNoteJob(
{
user: { id: user.id },
noteId: note.id,
option: {
poll: data.poll,
visibility: await visibility,
poll: data.poll
? {
choices: data.poll.choices,
multiple: data.poll.multiple,
expiresAt: data.poll.expiresAt
? new Date(data.poll.expiresAt)
: null,
}
: undefined,
visibility: data.visibility,
visibleUserIds: await Promise.resolve(visibility)
.then((v) =>
v === "specified" ? data.visibleUsers : undefined,
)
.then((users) => users?.map((u) => u.id)),
replyId: data.reply?.id ?? undefined,
renoteId: data.renote?.id ?? undefined,
},
},
delay,
);
},
}
: undefined,
);
}
@ -476,7 +509,7 @@ export class NoteHelpers {
const user = ctx.user as ILocalUser;
const files =
request.media_ids && request.media_ids.length > 0
? await DriveFiles.findByIds(request.media_ids)
? await DriveFiles.findBy({ id: In(request.media_ids) })
: [];
if (request.media_attributes && request.media_attributes.length > 0) {
@ -750,17 +783,17 @@ export class NoteHelpers {
minId: string | undefined,
limit = 20,
ctx: MastoContext,
): Promise<ScheduledNote[]> {
): Promise<Note[]> {
if (limit > 40) limit = 40;
const query = PaginationHelpers.makePaginationQuery(
ScheduledNotes.createQueryBuilder("scheduledNote"),
Notes.createQueryBuilder("note"),
sinceId,
maxId,
minId,
)
.leftJoinAndSelect("scheduledNote.note", "note")
.leftJoinAndSelect("scheduledNote.user", "user");
.andWhere("note.scheduledAt IS NOT NULL")
.leftJoinAndSelect("note.user", "user");
return PaginationHelpers.execQueryLinkPagination(
query,
@ -773,13 +806,13 @@ export class NoteHelpers {
public static async getScheduledNoteOr404(
id: string,
ctx: MastoContext,
): Promise<ScheduledNote> {
): Promise<Note> {
const user = ctx.user as ILocalUser | null;
const query = ScheduledNotes.createQueryBuilder("scheduledNote")
.where("scheduledNote.noteId = :id", { id })
.andWhere("scheduledNote.userId = :userId", { userId: user?.id })
.leftJoinAndSelect("scheduledNote.note", "note")
.leftJoinAndSelect("scheduledNote.user", "user");
const query = Notes.createQueryBuilder("note")
.where("note.id = :id", { id })
.andWhere("note.userId = :userId", { userId: user?.id })
.andWhere("note.scheduledAt IS NOT NULL")
.leftJoinAndSelect("note.user", "user");
return query.getOneOrFail().catch((_) => {
throw new MastoApiError(404);
});

View file

@ -1,7 +1,6 @@
import type { Note } from "@/models/entities/note.js";
import type { ILocalUser, User } from "@/models/entities/user.js";
import {
Followings,
Notes,
Notifications,
RegistryItems,
@ -14,7 +13,7 @@ import { generateVisibilityQuery } from "@/server/api/common/generate-visibility
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
import { generateMutedUserRenotesQueryForNotes } from "@/server/api/common/generated-muted-renote-query.js";
import { fetchMeta } from "backend-rs";
import { fetchMeta, genId } from "backend-rs";
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
import type { UserList } from "@/models/entities/user-list.js";
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
@ -27,7 +26,6 @@ import { generatePaginationData } from "@/server/api/mastodon/middleware/paginat
import type { MastoContext } from "@/server/api/mastodon/index.js";
import { generateListQuery } from "@/server/api/common/generate-list-query.js";
import { generateFollowingQuery } from "@/server/api/common/generate-following-query.js";
import { genId } from "backend-rs";
export class TimelineHelpers {
public static async getHomeTimeline(
@ -379,14 +377,8 @@ export class TimelineHelpers {
return result;
}
/** Exclude scheduled notes from Mastodon timeline (visibility === "specified" && visibleUserIds.length === 0) */
/** Exclude scheduled notes from Mastodon timeline */
public static generateNoScheduleNotesQuery(q: SelectQueryBuilder<Note>) {
q.andWhere(
new Brackets((qb) => {
qb.where("note.visibility != 'specified'").orWhere(
"array_length(note.visibleUserIds, 1) != 0",
);
}),
);
q.andWhere("note.scheduledAt IS NULL");
}
}